/*---------------------------------------------------------------------------- ChucK Concurrent, On-the-fly Audio Programming Language Compiler and Virtual Machine Copyright (c) 2004 Ge Wang and Perry R. Cook. All rights reserved. http://chuck.stanford.edu/ http://chuck.cs.princeton.edu/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 U.S.A.-----------------------------------------------------------------------------*/ //-----------------------------------------------------------------------------// file: chuck_ugen.cpp// desc: chuck unit generator interface//// authors: Ge Wang (ge@ccrma.stanford.edu | gewang@cs.princeton.edu)// Rebecca Fiebrink (fiebrink@cs.princeton.edu)// Spencer Salazar (spencer@ccrma.stanford.edu)// date: spring 2004 - 1.1// spring 2005 - 1.2// spring 2007 - UAna//-----------------------------------------------------------------------------#include "chuck_ugen.h"#include "chuck_instr.h"#include "chuck_type.h"#include "chuck_vm.h"#include "chuck_lang.h"#include "chuck_errmsg.h"using namespace std; //-----------------------------------------------------------------------------// fast array//-----------------------------------------------------------------------------void fa_init( Chuck_UGen ** & base, t_CKUINT & capacity );void fa_done( Chuck_UGen ** & base, t_CKUINT & capacity );void fa_resize( Chuck_UGen ** & base, t_CKUINT & capacity );void fa_push_back( Chuck_UGen ** & base, t_CKUINT & capacity, t_CKUINT size, Chuck_UGen * value );t_CKBOOL fa_lookup( Chuck_UGen ** base, t_CKUINT size, const Chuck_UGen * value ); //-----------------------------------------------------------------------------// name: fa_init()// desc: ...//-----------------------------------------------------------------------------void fa_init( Chuck_UGen ** & base, t_CKUINT & capacity ){ base = NULL; capacity = 0;} //-----------------------------------------------------------------------------// name: fa_done()// desc: ...//-----------------------------------------------------------------------------void fa_done( Chuck_UGen ** & base, t_CKUINT & capacity ){ if( base ) delete [] base; base = NULL; capacity = 0;} //-----------------------------------------------------------------------------// name: fa_resize()// desc: ...//-----------------------------------------------------------------------------void fa_resize( Chuck_UGen ** & base, t_CKUINT & capacity ){ // initial if( capacity == 0 ) capacity = 8; else capacity *= 2; // allocate Chuck_UGen ** new_base = new Chuck_UGen *[capacity]; // delete if( base ) { // copy memcpy( new_base, base, capacity / 2 * sizeof(Chuck_UGen *) ); // delete delete [] base; } // done base = new_base;} //-----------------------------------------------------------------------------// name: fa_push_back()// desc: ...//-----------------------------------------------------------------------------void fa_push_back( Chuck_UGen ** & base, t_CKUINT & capacity, t_CKUINT size, Chuck_UGen * value ){ // resize if( size == capacity ) fa_resize( base, capacity ); // add base[size] = value;} //-----------------------------------------------------------------------------// name: fa_lookup()// desc: ...//-----------------------------------------------------------------------------t_CKBOOL fa_lookup( Chuck_UGen ** base, t_CKUINT size, const Chuck_UGen * value ){ // loop for( t_CKUINT i = 0; i < size; i++ ) if( base[i] == value ) return TRUE; // not found return FALSE;} //-----------------------------------------------------------------------------// name: Chuck_UGen()// desc: constructor//-----------------------------------------------------------------------------Chuck_UGen::Chuck_UGen(){ this->init();} //-----------------------------------------------------------------------------// name: ~Chuck_UGen()// desc: ...//-----------------------------------------------------------------------------Chuck_UGen::~Chuck_UGen(){ this->done();} //-----------------------------------------------------------------------------// name: init()// desc: ...//-----------------------------------------------------------------------------void Chuck_UGen::init(){ tick = NULL; tickf = NULL; // added 1.3.0.0 pmsg = NULL; m_multi_chan = NULL; m_multi_chan_size = 0; m_num_ins = 1; m_num_outs = 1; fa_init( m_src_list, m_src_cap ); fa_init( m_dest_list, m_dest_cap ); fa_init( m_src_uana_list, m_src_uana_cap ); fa_init( m_dest_uana_list, m_dest_uana_cap ); m_num_src = 0; m_num_dest = 0; m_num_uana_src = 0; m_num_uana_dest = 0; m_max_src = CK_NO_VALUE; m_time = 0; m_valid = TRUE; m_sum = 0.0f; m_current = 0.0f; m_last = 0.0f; m_op = UGEN_OP_TICK; m_gain = 1.0f; m_next = 0.0f; m_use_next = FALSE; m_max_block_size = -1; // if this is part of a stereo UGen, this parameter will be initialized // according to the underly panning law (1.4.1.0) m_pan = 1.0f; m_sum_v = NULL; m_current_v = NULL; shred = NULL; owner = NULL; // what a hack m_is_uana = FALSE; // what another hack (added 1.3.0.0) m_is_subgraph = FALSE; m_inlet = m_outlet = NULL; m_multi_in_v = m_multi_out_v = NULL; // 1.4.1.0 (jack): yes more hacks. buffered flag allows // global ugens' samples to be gotten m_is_buffered = FALSE; // buffer empty for any ugen that is not buffered m_buffer.resize( 0 );} //-----------------------------------------------------------------------------// name: done()// desc: ...//-----------------------------------------------------------------------------void Chuck_UGen::done(){ if( this->shred ) shred->remove( this ); assert( this->m_ref_count == 0 ); // disconnect this->disconnect( TRUE ); m_valid = FALSE; fa_done( m_src_list, m_src_cap ); fa_done( m_dest_list, m_dest_cap ); fa_done( m_src_uana_list, m_src_uana_cap ); fa_done( m_dest_uana_list, m_dest_uana_cap ); // reclaim SAFE_DELETE_ARRAY( m_sum_v ); SAFE_DELETE_ARRAY( m_current_v ); // more reclaim (added 1.3.0.0) SAFE_DELETE_ARRAY( m_multi_chan ); // TODO: m_multi_chan, break ref count loop // SPENCERTODO: is this okay??? (added 1.3.0.0) SAFE_DELETE( m_inlet ); SAFE_DELETE( m_outlet ); // clean up array (added 1.3.0.0) SAFE_DELETE_ARRAY( m_multi_in_v ); SAFE_DELETE_ARRAY( m_multi_out_v );} //-----------------------------------------------------------------------------// name: alloc_v()// desc: ...//-----------------------------------------------------------------------------t_CKBOOL Chuck_UGen::alloc_v( t_CKUINT size ){ // reclaim SAFE_DELETE_ARRAY( m_sum_v ); SAFE_DELETE_ARRAY( m_current_v ); // save block size as max block size (added 1.3.0.0) m_max_block_size = size; // go if( size > 0 ) { m_sum_v = new SAMPLE[size]; m_current_v = new SAMPLE[size]; return ( m_sum_v != NULL && m_current_v != NULL ); } return TRUE;} //-----------------------------------------------------------------------------// name: alloc_multi_chan()// desc: ...//-----------------------------------------------------------------------------void Chuck_UGen::alloc_multi_chan( t_CKUINT num_ins, t_CKUINT num_outs ){ // get max of num_ins and num_outs m_multi_chan_size = ( num_ins > num_outs ? num_ins : num_outs ); // allocate m_multi_chan = new Chuck_UGen *[m_multi_chan_size]; // zero it out, whoever call this will fill in memset( m_multi_chan, 0, m_multi_chan_size * sizeof(Chuck_UGen *) ); // mono if( m_multi_chan_size == 1 ) { // zero out m_multi_chan_size = 0; // self m_multi_chan[0] = this; } // if there tick-frame (i.e., has multi-channel tick function; added 1.3.0.0) if( m_multi_chan_size && tickf ) { // m_max_block_size needs to be set via alloc_v() first (added 1.3.0.0) assert(m_max_block_size >= 0); t_CKINT block_size = m_max_block_size == 0 ? 1 : m_max_block_size; SAFE_DELETE_ARRAY(m_multi_in_v); SAFE_DELETE_ARRAY(m_multi_out_v); // allocate a frame for input and output from the tick function (add 1.3.0.0) m_multi_in_v = new SAMPLE[m_multi_chan_size*block_size]; m_multi_out_v = new SAMPLE[m_multi_chan_size*block_size]; } // remember m_num_ins = num_ins; m_num_outs = num_outs;} //-----------------------------------------------------------------------------// name: set_max_src()// dsec: ...//-----------------------------------------------------------------------------t_CKBOOL Chuck_UGen::set_max_src( t_CKUINT num ){ m_max_src = num; return TRUE;} //-----------------------------------------------------------------------------// name: get_num_src()// desc: ...//-----------------------------------------------------------------------------t_CKUINT Chuck_UGen::get_num_src(){ return m_num_src;} //-----------------------------------------------------------------------------// name: set_is_buffered()// dsec: set whether we should store our old samples//-----------------------------------------------------------------------------t_CKBOOL Chuck_UGen::set_is_buffered( t_CKBOOL buffered ){ // check if value is really changing. if not, return if( ( buffered && m_is_buffered ) || ( !buffered && !m_is_buffered ) ) { return TRUE; } if( buffered ) { // alloc // (TODO: smarter way of maximum size of my buffer) m_buffer.resize( 8192 ); } else { // dealloc m_buffer.resize( 0 ); } // store flag m_is_buffered = buffered; return TRUE; } //-----------------------------------------------------------------------------// name: get_buffer()// dsec: get the most recent buffer of samples//-----------------------------------------------------------------------------void Chuck_UGen::get_buffer( SAMPLE * buffer, t_CKINT num_elem ){ if( m_is_buffered ) { // if we actually store our samples, give them num_elem of them m_buffer.get_most_recent( buffer, num_elem ); } else { // otherwise, give them 0 memset( buffer, 0, sizeof( SAMPLE ) * num_elem ); }} //-----------------------------------------------------------------------------// name: src_chan()// desc: added 1.3.3.1// destination ugen for a given source channel//-----------------------------------------------------------------------------Chuck_UGen * Chuck_UGen::src_chan( t_CKUINT chan ){ if( this->m_num_outs == 1) return this; return m_multi_chan[chan%m_num_outs];} //-----------------------------------------------------------------------------// name: dst_for_src_chan()// desc: added 1.3.3.1// destination ugen for a given source channel//-----------------------------------------------------------------------------Chuck_UGen * Chuck_UGen::dst_for_src_chan( t_CKUINT chan ){ if( this->m_num_ins == 1) return this; if( chan < this->m_num_ins ) return m_multi_chan[chan]; return NULL;} //-----------------------------------------------------------------------------// name: add()// dsec: from point of view of destination (RHS) Ugen, add source (LHS) ugen// i.e., src => this//-----------------------------------------------------------------------------t_CKBOOL Chuck_UGen::add( Chuck_UGen * src, t_CKBOOL isUpChuck ){ // spencer 2012: chubgraph handling (added 1.3.0.0) if( m_is_subgraph ) { // sanity check assert( inlet() != NULL ); // add src to this inlet return inlet()->add( src, isUpChuck ); } else if( src->m_is_subgraph ) // (added 1.3.0.0) { // sanity check assert( src->outlet() != NULL ); // call add on the src's outlet instead return add( src->outlet(), isUpChuck ); } // examine ins and outs t_CKUINT outs = src->m_num_outs; t_CKUINT ins = this->m_num_ins; t_CKUINT i; // map output channels to inputs if( outs >= 1 && ins == 1 ) { // check if already connected // if( fa_lookup( m_src_list, m_num_src, src ) ) // return FALSE; // check for limit if( m_num_src >= m_max_src ) return FALSE; Lines 1–500 of 1626. Load more
/*---------------------------------------------------------------------------- ChucK Concurrent, On-the-fly Audio Programming Language Compiler and Virtual Machine Copyright (c) 2004 Ge Wang and Perry R. Cook. All rights reserved. http://chuck.stanford.edu/ http://chuck.cs.princeton.edu/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 U.S.A. -----------------------------------------------------------------------------*/ //----------------------------------------------------------------------------- // file: chuck_ugen.cpp // desc: chuck unit generator interface // // authors: Ge Wang (ge@ccrma.stanford.edu | gewang@cs.princeton.edu) // Rebecca Fiebrink (fiebrink@cs.princeton.edu) // Spencer Salazar (spencer@ccrma.stanford.edu) // date: spring 2004 - 1.1 // spring 2005 - 1.2 // spring 2007 - UAna //----------------------------------------------------------------------------- #include "chuck_ugen.h" #include "chuck_instr.h" #include "chuck_type.h" #include "chuck_vm.h" #include "chuck_lang.h" #include "chuck_errmsg.h" using namespace std; //----------------------------------------------------------------------------- // fast array //----------------------------------------------------------------------------- void fa_init( Chuck_UGen ** & base, t_CKUINT & capacity ); void fa_done( Chuck_UGen ** & base, t_CKUINT & capacity ); void fa_resize( Chuck_UGen ** & base, t_CKUINT & capacity ); void fa_push_back( Chuck_UGen ** & base, t_CKUINT & capacity, t_CKUINT size, Chuck_UGen * value ); t_CKBOOL fa_lookup( Chuck_UGen ** base, t_CKUINT size, const Chuck_UGen * value ); //----------------------------------------------------------------------------- // name: fa_init() // desc: ... //----------------------------------------------------------------------------- void fa_init( Chuck_UGen ** & base, t_CKUINT & capacity ) { base = NULL; capacity = 0; } //----------------------------------------------------------------------------- // name: fa_done() // desc: ... //----------------------------------------------------------------------------- void fa_done( Chuck_UGen ** & base, t_CKUINT & capacity ) { if( base ) delete [] base; base = NULL; capacity = 0; } //----------------------------------------------------------------------------- // name: fa_resize() // desc: ... //----------------------------------------------------------------------------- void fa_resize( Chuck_UGen ** & base, t_CKUINT & capacity ) { // initial if( capacity == 0 ) capacity = 8; else capacity *= 2; // allocate Chuck_UGen ** new_base = new Chuck_UGen *[capacity]; // delete if( base ) { // copy memcpy( new_base, base, capacity / 2 * sizeof(Chuck_UGen *) ); // delete delete [] base; } // done base = new_base; } //----------------------------------------------------------------------------- // name: fa_push_back() // desc: ... //----------------------------------------------------------------------------- void fa_push_back( Chuck_UGen ** & base, t_CKUINT & capacity, t_CKUINT size, Chuck_UGen * value ) { // resize if( size == capacity ) fa_resize( base, capacity ); // add base[size] = value; } //----------------------------------------------------------------------------- // name: fa_lookup() // desc: ... //----------------------------------------------------------------------------- t_CKBOOL fa_lookup( Chuck_UGen ** base, t_CKUINT size, const Chuck_UGen * value ) { // loop for( t_CKUINT i = 0; i < size; i++ ) if( base[i] == value ) return TRUE; // not found return FALSE; } //----------------------------------------------------------------------------- // name: Chuck_UGen() // desc: constructor //----------------------------------------------------------------------------- Chuck_UGen::Chuck_UGen() { this->init(); } //----------------------------------------------------------------------------- // name: ~Chuck_UGen() // desc: ... //----------------------------------------------------------------------------- Chuck_UGen::~Chuck_UGen() { this->done(); } //----------------------------------------------------------------------------- // name: init() // desc: ... //----------------------------------------------------------------------------- void Chuck_UGen::init() { tick = NULL; tickf = NULL; // added 1.3.0.0 pmsg = NULL; m_multi_chan = NULL; m_multi_chan_size = 0; m_num_ins = 1; m_num_outs = 1; fa_init( m_src_list, m_src_cap ); fa_init( m_dest_list, m_dest_cap ); fa_init( m_src_uana_list, m_src_uana_cap ); fa_init( m_dest_uana_list, m_dest_uana_cap ); m_num_src = 0; m_num_dest = 0; m_num_uana_src = 0; m_num_uana_dest = 0; m_max_src = CK_NO_VALUE; m_time = 0; m_valid = TRUE; m_sum = 0.0f; m_current = 0.0f; m_last = 0.0f; m_op = UGEN_OP_TICK; m_gain = 1.0f; m_next = 0.0f; m_use_next = FALSE; m_max_block_size = -1; // if this is part of a stereo UGen, this parameter will be initialized // according to the underly panning law (1.4.1.0) m_pan = 1.0f; m_sum_v = NULL; m_current_v = NULL; shred = NULL; owner = NULL; // what a hack m_is_uana = FALSE; // what another hack (added 1.3.0.0) m_is_subgraph = FALSE; m_inlet = m_outlet = NULL; m_multi_in_v = m_multi_out_v = NULL; // 1.4.1.0 (jack): yes more hacks. buffered flag allows // global ugens' samples to be gotten m_is_buffered = FALSE; // buffer empty for any ugen that is not buffered m_buffer.resize( 0 ); } //----------------------------------------------------------------------------- // name: done() // desc: ... //----------------------------------------------------------------------------- void Chuck_UGen::done() { if( this->shred ) shred->remove( this ); assert( this->m_ref_count == 0 ); // disconnect this->disconnect( TRUE ); m_valid = FALSE; fa_done( m_src_list, m_src_cap ); fa_done( m_dest_list, m_dest_cap ); fa_done( m_src_uana_list, m_src_uana_cap ); fa_done( m_dest_uana_list, m_dest_uana_cap ); // reclaim SAFE_DELETE_ARRAY( m_sum_v ); SAFE_DELETE_ARRAY( m_current_v ); // more reclaim (added 1.3.0.0) SAFE_DELETE_ARRAY( m_multi_chan ); // TODO: m_multi_chan, break ref count loop // SPENCERTODO: is this okay??? (added 1.3.0.0) SAFE_DELETE( m_inlet ); SAFE_DELETE( m_outlet ); // clean up array (added 1.3.0.0) SAFE_DELETE_ARRAY( m_multi_in_v ); SAFE_DELETE_ARRAY( m_multi_out_v ); } //----------------------------------------------------------------------------- // name: alloc_v() // desc: ... //----------------------------------------------------------------------------- t_CKBOOL Chuck_UGen::alloc_v( t_CKUINT size ) { // reclaim SAFE_DELETE_ARRAY( m_sum_v ); SAFE_DELETE_ARRAY( m_current_v ); // save block size as max block size (added 1.3.0.0) m_max_block_size = size; // go if( size > 0 ) { m_sum_v = new SAMPLE[size]; m_current_v = new SAMPLE[size]; return ( m_sum_v != NULL && m_current_v != NULL ); } return TRUE; } //----------------------------------------------------------------------------- // name: alloc_multi_chan() // desc: ... //----------------------------------------------------------------------------- void Chuck_UGen::alloc_multi_chan( t_CKUINT num_ins, t_CKUINT num_outs ) { // get max of num_ins and num_outs m_multi_chan_size = ( num_ins > num_outs ? num_ins : num_outs ); // allocate m_multi_chan = new Chuck_UGen *[m_multi_chan_size]; // zero it out, whoever call this will fill in memset( m_multi_chan, 0, m_multi_chan_size * sizeof(Chuck_UGen *) ); // mono if( m_multi_chan_size == 1 ) { // zero out m_multi_chan_size = 0; // self m_multi_chan[0] = this; } // if there tick-frame (i.e., has multi-channel tick function; added 1.3.0.0) if( m_multi_chan_size && tickf ) { // m_max_block_size needs to be set via alloc_v() first (added 1.3.0.0) assert(m_max_block_size >= 0); t_CKINT block_size = m_max_block_size == 0 ? 1 : m_max_block_size; SAFE_DELETE_ARRAY(m_multi_in_v); SAFE_DELETE_ARRAY(m_multi_out_v); // allocate a frame for input and output from the tick function (add 1.3.0.0) m_multi_in_v = new SAMPLE[m_multi_chan_size*block_size]; m_multi_out_v = new SAMPLE[m_multi_chan_size*block_size]; } // remember m_num_ins = num_ins; m_num_outs = num_outs; } //----------------------------------------------------------------------------- // name: set_max_src() // dsec: ... //----------------------------------------------------------------------------- t_CKBOOL Chuck_UGen::set_max_src( t_CKUINT num ) { m_max_src = num; return TRUE; } //----------------------------------------------------------------------------- // name: get_num_src() // desc: ... //----------------------------------------------------------------------------- t_CKUINT Chuck_UGen::get_num_src() { return m_num_src; } //----------------------------------------------------------------------------- // name: set_is_buffered() // dsec: set whether we should store our old samples //----------------------------------------------------------------------------- t_CKBOOL Chuck_UGen::set_is_buffered( t_CKBOOL buffered ) { // check if value is really changing. if not, return if( ( buffered && m_is_buffered ) || ( !buffered && !m_is_buffered ) ) { return TRUE; } if( buffered ) { // alloc // (TODO: smarter way of maximum size of my buffer) m_buffer.resize( 8192 ); } else { // dealloc m_buffer.resize( 0 ); } // store flag m_is_buffered = buffered; return TRUE; } //----------------------------------------------------------------------------- // name: get_buffer() // dsec: get the most recent buffer of samples //----------------------------------------------------------------------------- void Chuck_UGen::get_buffer( SAMPLE * buffer, t_CKINT num_elem ) { if( m_is_buffered ) { // if we actually store our samples, give them num_elem of them m_buffer.get_most_recent( buffer, num_elem ); } else { // otherwise, give them 0 memset( buffer, 0, sizeof( SAMPLE ) * num_elem ); } } //----------------------------------------------------------------------------- // name: src_chan() // desc: added 1.3.3.1 // destination ugen for a given source channel //----------------------------------------------------------------------------- Chuck_UGen * Chuck_UGen::src_chan( t_CKUINT chan ) { if( this->m_num_outs == 1) return this; return m_multi_chan[chan%m_num_outs]; } //----------------------------------------------------------------------------- // name: dst_for_src_chan() // desc: added 1.3.3.1 // destination ugen for a given source channel //----------------------------------------------------------------------------- Chuck_UGen * Chuck_UGen::dst_for_src_chan( t_CKUINT chan ) { if( this->m_num_ins == 1) return this; if( chan < this->m_num_ins ) return m_multi_chan[chan]; return NULL; } //----------------------------------------------------------------------------- // name: add() // dsec: from point of view of destination (RHS) Ugen, add source (LHS) ugen // i.e., src => this //----------------------------------------------------------------------------- t_CKBOOL Chuck_UGen::add( Chuck_UGen * src, t_CKBOOL isUpChuck ) { // spencer 2012: chubgraph handling (added 1.3.0.0) if( m_is_subgraph ) { // sanity check assert( inlet() != NULL ); // add src to this inlet return inlet()->add( src, isUpChuck ); } else if( src->m_is_subgraph ) // (added 1.3.0.0) { // sanity check assert( src->outlet() != NULL ); // call add on the src's outlet instead return add( src->outlet(), isUpChuck ); } // examine ins and outs t_CKUINT outs = src->m_num_outs; t_CKUINT ins = this->m_num_ins; t_CKUINT i; // map output channels to inputs if( outs >= 1 && ins == 1 ) { // check if already connected // if( fa_lookup( m_src_list, m_num_src, src ) ) // return FALSE; // check for limit if( m_num_src >= m_max_src ) return FALSE;