/* xsample - extended sample objects for Max/MSP and pd (pure data) Copyright (c) 2001-2003 Thomas Grill (xovo@gmx.net) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. */ #include "main.h" #include #ifdef _MSC_VER #pragma warning (disable:4244) #endif class xrecord: public xsample { FLEXT_HEADER_S(xrecord,xsample,setup) public: xrecord(I argc,const t_atom *argv); virtual BL Init(); virtual V m_help(); virtual V m_print(); virtual I m_set(I argc,const t_atom *argv); virtual V m_pos(F pos); virtual V m_all(); virtual V m_start(); virtual V m_stop(); virtual BL m_reset(); virtual V m_units(xs_unit md = xsu__); virtual V m_min(F mn); virtual V m_max(F mx); inline V m_append(BL app) { if(!(appmode = app)) m_pos(0); } virtual V m_draw(I argc,const t_atom *argv); protected: I inchns; BL sigmode,appmode; F drintv; BL dorec,doloop,mixmode; L curpos; // in samples inline V outputmin() { ToOutFloat(1,curmin*s2u); } inline V outputmax() { ToOutFloat(2,curmax*s2u); } inline V mg_pos(F &v) const { v = curpos*s2u; } private: static V setup(t_classid c); virtual V s_dsp(); TMPLSIGFUN(s_rec); DEFSIGCALL(recfun); virtual V m_signal(I n,S *const *in,S *const *out) { if(bufchk()) recfun(n,in,out); } FLEXT_CALLVAR_F(mg_pos,m_pos) FLEXT_CALLBACK(m_all) FLEXT_CALLSET_F(m_min) FLEXT_CALLSET_F(m_max) FLEXT_CALLBACK_F(m_min) FLEXT_CALLBACK_F(m_max) FLEXT_ATTRVAR_B(doloop) FLEXT_ATTRVAR_B(mixmode) FLEXT_ATTRVAR_B(sigmode) FLEXT_CALLSET_B(m_append) FLEXT_ATTRGET_B(appmode) FLEXT_CALLBACK_V(m_draw) }; FLEXT_LIB_DSP_V("xrecord~",xrecord) V xrecord::setup(t_classid c) { DefineHelp(c,"xrecord~"); FLEXT_CADDATTR_VAR(c,"pos",mg_pos,m_pos); FLEXT_CADDATTR_VAR(c,"min",mg_min,m_min); FLEXT_CADDATTR_VAR(c,"max",mg_max,m_max); FLEXT_CADDMETHOD_(c,0,"all",m_all); FLEXT_CADDMETHOD_(c,0,"draw",m_draw); FLEXT_CADDATTR_VAR1(c,"loop",doloop); FLEXT_CADDATTR_VAR1(c,"mixmode",mixmode); FLEXT_CADDATTR_VAR1(c,"sigmode",sigmode); FLEXT_CADDATTR_VAR(c,"append",appmode,m_append); } xrecord::xrecord(I argc,const t_atom *argv): dorec(false), sigmode(false),mixmode(false), appmode(true),doloop(false), drintv(0), inchns(1) { I argi = 0; #if FLEXT_SYS == FLEXT_SYS_MAX if(argc > argi && CanbeInt(argv[argi])) { inchns = GetAInt(argv[argi]); argi++; } #endif if(argc > argi && IsSymbol(argv[argi])) { buf = new buffer(GetSymbol(argv[argi]),true); argi++; #if FLEXT_SYS == FLEXT_SYS_MAX // oldstyle command line? if(argi == 1 && argc == 2 && CanbeInt(argv[argi])) { inchns = GetAInt(argv[argi]); argi++; post("%s: old style command line detected - please change to '%s [channels] [buffer]'",thisName(),thisName()); } #endif } else buf = new buffer(NULL,true); for(I ci = 0; ci < inchns; ++ci) { C tmp[40]; STD::sprintf(tmp,ci == 0?"Messages/audio channel %i":"Audio channel %i",ci+1); AddInSignal(tmp); // audio signals } AddInSignal("On/Off/Fade/Mix signal (0..1)"); // on/off signal AddInFloat("Starting point of recording"); // min AddInFloat("Ending point of recording"); // max AddOutSignal("Current position of recording"); // pos signal AddOutFloat("Starting point (rounded to frame)"); // min AddOutFloat("Ending point (rounded to frame)"); // max AddOutBang("Bang on loop end/rollover"); // loop bang FLEXT_ADDMETHOD(inchns+1,m_min); FLEXT_ADDMETHOD(inchns+2,m_max); } BL xrecord::Init() { if(xsample::Init()) { m_reset(); return true; } else return false; } V xrecord::m_units(xs_unit mode) { xsample::m_units(mode); m_sclmode(); outputmin(); outputmax(); } V xrecord::m_min(F mn) { xsample::m_min(mn); m_pos(curpos*s2u); outputmin(); } V xrecord::m_max(F mx) { xsample::m_max(mx); m_pos(curpos*s2u); outputmax(); } V xrecord::m_all() { xsample::m_all(); outputmin(); outputmax(); } V xrecord::m_pos(F pos) { curpos = pos?(L)(pos/s2u+.5):0; if(curpos < curmin) curpos = curmin; else if(curpos > curmax) curpos = curmax; } I xrecord::m_set(I argc,const t_atom *argv) { I r = xsample::m_set(argc,argv); if(r < 0) m_reset(); // resets pos/min/max if(r != 0) m_units(); return r; } V xrecord::m_start() { if(!sigmode && !appmode) m_pos(0); m_refresh(); dorec = true; buf->SetRefrIntv(drintv); s_dsp(); } V xrecord::m_stop() { dorec = false; buf->Dirty(true); buf->SetRefrIntv(0); s_dsp(); } BL xrecord::m_reset() { curpos = 0; return xsample::m_reset(); } V xrecord::m_draw(I argc,const t_atom *argv) { if(argc >= 1) { drintv = GetInt(argv[0]); if(dorec) buf->SetRefrIntv(drintv); } else buf->Dirty(true); } TMPLDEF V xrecord::s_rec(I n,S *const *invecs,S *const *outvecs) { SIGCHNS(BCHNS,buf->Channels(),ICHNS,inchns); const S *const *sig = invecs; register I si = 0; const S *on = invecs[inchns]; S *pos = outvecs[0]; BL lpbang = false; register const F pf = sclmul; register L o = curpos; if(o < curmin) o = curmin; // if(buf && dorec && curlen > 0) { if(buf && dorec && curmax > curmin) { while(n) { L ncur = curmax-o; // at max to buffer or recording end if(ncur <= 0) { // end of buffer if(doloop) { o = curmin; // ncur = curlen; ncur = curmax-o; } else m_stop(); // loop expired; lpbang = true; } if(!dorec) break; if(ncur > n) ncur = n; register I i; register S *bf = buf->Data()+o*BCHNS; register F p = scale(o); if(sigmode) { if(appmode) { // append to current position if(!mixmode) { for(i = 0; i < ncur; ++i,++si) { if(*(on++) >= 0) { for(int ci = 0; ci < ICHNS; ++ci) bf[ci] = sig[ci][si]; bf += BCHNS; *(pos++) = p,p += pf,++o; } else *(pos++) = p; } } else { for(i = 0; i < ncur; ++i,++si) { register const S g = *(on++); if(g >= 0) { for(int ci = 0; ci < ICHNS; ++ci) bf[ci] = bf[ci]*(1.-g)+sig[ci][si]*g; bf += BCHNS; *(pos++) = p,p += pf,++o; } else *(pos++) = p; } } } else { // don't append if(!mixmode) { for(i = 0; i < ncur; ++i,++si) { if(*(on++) >= 0) { for(int ci = 0; ci < ICHNS; ++ci) bf[ci] = sig[ci][si]; bf += BCHNS; *(pos++) = p,p += pf,++o; } else { *(pos++) = p = scale(o = 0); bf = buf->Data(); } } } else { for(i = 0; i < ncur; ++i,++si) { register const S g = *(on++); if(g >= 0) { for(int ci = 0; ci < ICHNS; ++ci) bf[ci] = bf[ci]*(1.-g)+sig[ci][si]*g; bf += BCHNS; *(pos++) = p,p += pf,++o; } else { *(pos++) = p = scale(o = 0); bf = buf->Data(); } } } } } else { // message mode // Altivec optimization for that! if(!mixmode) { for(int ci = 0; ci < ICHNS; ++ci) { register S *b = bf+ci; register const F *s = sig[ci]; for(i = 0; i < ncur; ++i,b += BCHNS,++s) *b = *s; } si += ncur; } else { for(i = 0; i < ncur; ++i,++si) { register const S w = *(on++); for(int ci = 0; ci < ICHNS; ++ci) bf[ci] = bf[ci]*(1.-w)+sig[ci][si]*w; bf += BCHNS; } } for(i = 0; i < ncur; ++i) { *(pos++) = p,p += pf,++o; } } n -= ncur; } curpos = o; buf->Dirty(); } if(n) { register F p = scale(o); while(n--) *(pos++) = p; } if(lpbang) ToOutBang(3); } V xrecord::s_dsp() { switch(buf->Channels()*1000+inchns) { case 1001: SETSIGFUN(recfun,TMPLFUN(s_rec,1,1)); break; case 1002: SETSIGFUN(recfun,TMPLFUN(s_rec,1,2)); break; case 2001: SETSIGFUN(recfun,TMPLFUN(s_rec,2,1)); break; case 2002: SETSIGFUN(recfun,TMPLFUN(s_rec,2,2)); break; case 4001: case 4002: case 4003: SETSIGFUN(recfun,TMPLFUN(s_rec,4,-1)); break; case 4004: SETSIGFUN(recfun,TMPLFUN(s_rec,4,4)); break; default: SETSIGFUN(recfun,TMPLFUN(s_rec,-1,-1)); break; } } V xrecord::m_help() { post("%s - part of xsample objects, version " XSAMPLE_VERSION,thisName()); #ifdef FLEXT_DEBUG post("compiled on " __DATE__ " " __TIME__); #endif post("(C) Thomas Grill, 2001-2003"); #if FLEXT_SYS == FLEXT_SYS_MAX post("Arguments: %s [channels=1] [buffer]",thisName()); #else post("Arguments: %s [buffer]",thisName()); #endif post("Inlets: 1:Messages/Audio signal, 2:Trigger signal, 3:Min point, 4: Max point"); post("Outlets: 1:Position signal, 2:Min point, 3:Max point"); post("Methods:"); post("\thelp: shows this help"); post("\tset [name]: set buffer or reinit"); post("\tenable 0/1: turn dsp calculation off/on"); post("\treset: reset min/max recording points and recording offset"); post("\tprint: print current settings"); post("\t@sigmode 0/1: specify message or signal triggered recording"); post("\t@append 0/1: reset recording position or append to current position"); post("\t@loop 0/1: switches looping off/on"); post("\t@mixmode 0/1: specify if audio signal should be mixed in"); post("\tmin {unit}: set minimum recording point"); post("\tmax {unit}: set maximum recording point"); post("\tall: select entire buffer length"); post("\tpos {unit}: set recording position (obeying the current scale mode)"); post("\tbang/start: start recording"); post("\tstop: stop recording"); post("\trefresh: checks buffer and refreshes outlets"); post("\t@units 0/1/2/3: set units to frames/buffer size/ms/s"); post("\t@sclmode 0/1/2/3: set range of position to units/units in loop/buffer/loop"); post("\tdraw [{float}]: redraw buffer immediately (arg omitted) or periodic (in ms)"); post(""); } V xrecord::m_print() { static const C sclmode_txt[][20] = {"units","units in loop","buffer","loop"}; // print all current settings post("%s - current settings:",thisName()); post("bufname = '%s', length = %.3f, channels = %i",buf->Name(),(F)(buf->Frames()*s2u),buf->Channels()); post("in channels = %i, frames/unit = %.3f, scale mode = %s",inchns,(F)(1./s2u),sclmode_txt[sclmode]); post("sigmode = %s, append = %s, loop = %s, mixmode = %s",sigmode?"yes":"no",appmode?"yes":"no",doloop?"yes":"no",mixmode?"yes":"no"); post(""); }