/* flext - C++ layer for Max/MSP and pd (pure data) externals Copyright (c) 2001-2005 Thomas Grill (gr@grrrr.org) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. */ /*! \file flbuf.cpp \brief Implementation of the buffer abstraction class. */ #include "flext.h" #include "flfeatures.h" #if FLEXT_SYS != FLEXT_SYS_JMAX #if FLEXT_SYS == FLEXT_SYS_PD #define DIRTY_INTERVAL 0 // buffer dirty check in msec #endif #if FLEXT_SYS == FLEXT_SYS_MAX // defined in flsupport.cpp extern const t_symbol *sym_buffer,*sym_size; #endif flext::buffer::buffer(const t_symbol *bn,bool delayed): sym(NULL),data(NULL), chns(0),frames(0) { #if FLEXT_SYS == FLEXT_SYS_PD arr = NULL; interval = DIRTY_INTERVAL; isdirty = false; ticking = false; tick = clock_new(this,(t_method)cb_tick); #endif if(bn) Set(bn,delayed); ClearDirty(); } flext::buffer::~buffer() { #if FLEXT_SYS == FLEXT_SYS_PD clock_free(tick); #endif } int flext::buffer::Set(const t_symbol *s,bool nameonly) { int ret = 0; bool valid = data != NULL; // valid now? (before change) if(s && sym != s) { ret = 1; data = NULL; frames = 0; chns = 0; } if(s && *GetString(s)) sym = s; if(!sym) { if(valid) ret = -1; } else if(!nameonly) { #if FLEXT_SYS == FLEXT_SYS_PD int frames1; t_sample *data1; arr = (t_garray *)pd_findbyclass(const_cast(sym), garray_class); if(!arr) { if (*GetString(sym)) FLEXT_LOG1("buffer: no such array '%s'",GetString(sym)); // sym = NULL; if(valid) ret = -1; } else if(!garray_getfloatarray(arr, &frames1, &data1)) { error("buffer: bad template '%s'",GetString(sym)); data = NULL; frames = 0; if(valid) ret = -1; } else { ret = 0; garray_usedindsp(arr); if(frames != frames1) { frames = frames1; if(!ret) ret = 1; } if(data != data1) { data = data1; if(!ret) ret = 1; } chns = 1; } #elif FLEXT_SYS == FLEXT_SYS_MAX if(sym->s_thing) { const t_buffer *p = (const t_buffer *)sym->s_thing; FLEXT_ASSERT(!NOGOOD(p)); if(ob_sym(p) != sym_buffer) { post("buffer: object '%s' not valid (type %s)",GetString(sym),GetString(ob_sym(p))); if(valid) ret = -2; } else { #ifdef FLEXT_DEBUG // post("flext: buffer object '%s' - valid:%i samples:%i channels:%i frames:%i",GetString(sym),p->b_valid,p->b_frames,p->b_nchans,p->b_frames); #endif if(data != p->b_samples) { data = p->b_samples; if(!ret) ret = 1; } if(chns != p->b_nchans) { chns = p->b_nchans; if(!ret) ret = 1; } if(frames != p->b_frames) { frames = p->b_frames; if(!ret) ret = 1; } } } else { FLEXT_LOG1("buffer: symbol '%s' not defined", GetString(sym)); /*if(valid)*/ ret = -1; } #else #error not implemented #endif } return ret; } bool flext::buffer::Update() { FLEXT_ASSERT(sym); bool upd = false; #if FLEXT_SYS == FLEXT_SYS_PD if(!arr) return data == NULL; int frames1; t_sample *data1; if(!garray_getfloatarray(arr, &frames1, &data1)) { data = NULL; chns = 0; frames = 0; upd = true; } else if(data != data1 || frames != frames1) { data = data1; frames = frames1; upd = true; } #elif FLEXT_SYS == FLEXT_SYS_MAX const t_buffer *p = (const t_buffer *)sym->s_thing; if(p) { FLEXT_ASSERT(!NOGOOD(p) && ob_sym(p) == sym_buffer); if(data != p->b_samples || chns != p->b_nchans || frames != p->b_frames) { data = p->b_samples; chns = p->b_nchans; frames = p->b_frames; upd = true; } } else { // buffer~ has e.g. been renamed data = NULL; chns = 0; frames = 0; upd = true; } #else #error not implemented #endif return upd; } flext::buffer::lock_t flext::buffer::Lock() { FLEXT_ASSERT(sym); #if FLEXT_SYS == FLEXT_SYS_PD FLEXT_ASSERT(arr); #ifdef _FLEXT_HAVE_PD_GARRAYLOCKS garray_lock(arr); #endif return false; #elif FLEXT_SYS == FLEXT_SYS_MAX t_buffer *p = (t_buffer *)sym->s_thing; FLEXT_ASSERT(p); #ifdef _FLEXT_HAVE_MAX_INUSEFLAG long old = p->b_inuse; p->b_inuse = 1; return old; #else return 0; #endif #else #error not implemented #endif } void flext::buffer::Unlock(flext::buffer::lock_t prv) { FLEXT_ASSERT(sym); #if FLEXT_SYS == FLEXT_SYS_PD FLEXT_ASSERT(arr); #ifdef _FLEXT_HAVE_PD_GARRAYLOCKS garray_unlock(arr); #endif #elif FLEXT_SYS == FLEXT_SYS_MAX t_buffer *p = (t_buffer *)sym->s_thing; FLEXT_ASSERT(p); #ifdef _FLEXT_HAVE_MAX_INUSEFLAG p->b_inuse = prv; #endif #else #error not implemented #endif } void flext::buffer::Frames(int fr,bool keep,bool zero) { FLEXT_ASSERT(sym); #if FLEXT_SYS == FLEXT_SYS_PD // is this function guaranteed to keep memory and set rest to zero? ::garray_resize(arr,(float)fr); Update(); #elif FLEXT_SYS == FLEXT_SYS_MAX t_sample *tmp = NULL; int sz = frames; if(fr < sz) sz = fr; if(keep) { // copy buffer data to tmp storage tmp = (t_sample *)NewAligned(sz*sizeof(t_sample)); FLEXT_ASSERT(tmp); CopySamples(tmp,data,sz); } t_atom msg; t_buffer *buf = (t_buffer *)sym->s_thing; // b_msr reflects buffer sample rate... is this what we want? // Max bug: adding half a sample to prevent roundoff errors.... float ms = (fr+0.5)/buf->b_msr; SetFloat(msg,ms); ::typedmess((object *)buf,(t_symbol *)sym_size,1,&msg); Update(); if(tmp) { // copy data back CopySamples(data,tmp,sz); FreeAligned(tmp); if(zero && sz < fr) ZeroSamples(data+sz,fr-sz); } else if(zero) ZeroSamples(data,fr); #else #error #endif } #if FLEXT_SYS == FLEXT_SYS_PD void flext::buffer::SetRefrIntv(float intv) { interval = intv; if(interval == 0 && ticking) { clock_unset(tick); ticking = false; } } #elif FLEXT_SYS == FLEXT_SYS_MAX void flext::buffer::SetRefrIntv(float) {} #else #error #endif void flext::buffer::Dirty(bool force) { FLEXT_ASSERT(sym); #if FLEXT_SYS == FLEXT_SYS_PD if((!ticking) && (interval || force)) { ticking = true; cb_tick(this); // immediately redraw } else { if(force) clock_delay(tick,0); isdirty = true; } #elif FLEXT_SYS == FLEXT_SYS_MAX t_buffer *p = (t_buffer *)sym->s_thing; FLEXT_ASSERT(p && !NOGOOD(p)); p->b_modtime = gettime(); #else #error Not implemented #endif } #if FLEXT_SYS == FLEXT_SYS_PD void flext::buffer::cb_tick(buffer *b) { if(b->arr) garray_redraw(b->arr); #ifdef FLEXT_DEBUG else error("buffer: array is NULL"); #endif if(b->isdirty && b->interval) { b->isdirty = false; b->ticking = true; clock_delay(b->tick,b->interval); } else b->ticking = false; } #endif void flext::buffer::ClearDirty() { #if FLEXT_SYS == FLEXT_SYS_PD cleantime = clock_getlogicaltime(); #elif FLEXT_SYS == FLEXT_SYS_MAX cleantime = gettime(); #else #error Not implemented #endif } bool flext::buffer::IsDirty() const { if(!sym) return false; #if FLEXT_SYS == FLEXT_SYS_PD #ifdef _FLEXT_HAVE_PD_GARRAYUPDATETIME return arr && (isdirty || garray_updatetime(arr) > cleantime); #else // Don't know.... (no method in PD judging whether buffer has been changed from outside flext...) return true; #endif #elif FLEXT_SYS == FLEXT_SYS_MAX t_buffer *p = (t_buffer *)sym->s_thing; FLEXT_ASSERT(p && !NOGOOD(p)); return p->b_modtime > cleantime; #else #error Not implemented #endif } #endif // Jmax