*** empty log message ***
[chuck-blob.git] / v2 / chuck_ugen.cpp
blob34de11f784c004041f9fcc316607d40925bed4b1
1 /*----------------------------------------------------------------------------
2 ChucK Concurrent, On-the-fly Audio Programming Language
3 Compiler and Virtual Machine
5 Copyright (c) 2004 Ge Wang and Perry R. Cook. All rights reserved.
6 http://chuck.cs.princeton.edu/
7 http://soundlab.cs.princeton.edu/
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 U.S.A.
23 -----------------------------------------------------------------------------*/
25 //-----------------------------------------------------------------------------
26 // name: chuck_ugen.cpp
27 // desc: chuck unit generator interface
29 // authors: Ge Wang (gewang@cs.princeton.edu)
30 // Perry R. Cook (prc@cs.princeton.edu)
31 // date: spring 2004 - 1.1
32 // spring 2005 - 1.2
33 //-----------------------------------------------------------------------------
34 #include "chuck_ugen.h"
35 #include "chuck_vm.h"
36 #include "chuck_lang.h"
37 #include "chuck_errmsg.h"
38 using namespace std;
43 //-----------------------------------------------------------------------------
44 // fast array
45 //-----------------------------------------------------------------------------
46 void fa_init( Chuck_UGen ** & base, t_CKUINT & capacity );
47 void fa_done( Chuck_UGen ** & base, t_CKUINT & capacity );
48 void fa_resize( Chuck_UGen ** & base, t_CKUINT & capacity );
49 void fa_push_back( Chuck_UGen ** & base, t_CKUINT & capacity,
50 t_CKUINT size, Chuck_UGen * value );
51 t_CKBOOL fa_lookup( Chuck_UGen ** base, t_CKUINT size,
52 const Chuck_UGen * value );
56 //-----------------------------------------------------------------------------
57 // name: fa_init()
58 // desc: ...
59 //-----------------------------------------------------------------------------
60 void fa_init( Chuck_UGen ** & base, t_CKUINT & capacity )
62 base = NULL;
63 capacity = 0;
69 //-----------------------------------------------------------------------------
70 // name: fa_done()
71 // desc: ...
72 //-----------------------------------------------------------------------------
73 void fa_done( Chuck_UGen ** & base, t_CKUINT & capacity )
75 if( base ) delete [] base;
76 base = NULL;
77 capacity = 0;
83 //-----------------------------------------------------------------------------
84 // name: fa_resize()
85 // desc: ...
86 //-----------------------------------------------------------------------------
87 void fa_resize( Chuck_UGen ** & base, t_CKUINT & capacity )
89 // initial
90 if( capacity == 0 )
91 capacity = 8;
92 else
93 capacity *= 2;
95 // allocate
96 Chuck_UGen ** new_base = new Chuck_UGen *[capacity];
97 // delete
98 if( base )
100 // copy
101 memcpy( new_base, base, capacity / 2 * sizeof(Chuck_UGen *) );
102 // delete
103 delete [] base;
106 // done
107 base = new_base;
113 //-----------------------------------------------------------------------------
114 // name: fa_push_back()
115 // desc: ...
116 //-----------------------------------------------------------------------------
117 void fa_push_back( Chuck_UGen ** & base, t_CKUINT & capacity,
118 t_CKUINT size, Chuck_UGen * value )
120 // resize
121 if( size == capacity ) fa_resize( base, capacity );
122 // add
123 base[size] = value;
129 //-----------------------------------------------------------------------------
130 // name: fa_lookup()
131 // desc: ...
132 //-----------------------------------------------------------------------------
133 t_CKBOOL fa_lookup( Chuck_UGen ** base, t_CKUINT size, const Chuck_UGen * value )
135 // loop
136 for( t_CKUINT i = 0; i < size; i++ )
137 if( base[i] == value )
138 return TRUE;
140 // not found
141 return FALSE;
147 //-----------------------------------------------------------------------------
148 // name: Chuck_UGen()
149 // desc: constructor
150 //-----------------------------------------------------------------------------
151 Chuck_UGen::Chuck_UGen()
153 this->init();
159 //-----------------------------------------------------------------------------
160 // name: ~Chuck_UGen()
161 // desc: ...
162 //-----------------------------------------------------------------------------
163 Chuck_UGen::~Chuck_UGen()
165 this->done();
171 //-----------------------------------------------------------------------------
172 // name: init()
173 // desc: ...
174 //-----------------------------------------------------------------------------
175 void Chuck_UGen::init()
177 tick = NULL;
178 pmsg = NULL;
179 m_multi_chan = NULL;
180 m_multi_chan_size = 0;
181 m_num_ins = 1;
182 m_num_outs = 1;
184 fa_init( m_src_list, m_src_cap );
185 fa_init( m_dest_list, m_dest_cap );
186 fa_init( m_src_uana_list, m_src_uana_cap );
187 fa_init( m_dest_uana_list, m_dest_uana_cap );
188 m_num_src = 0;
189 m_num_dest = 0;
190 m_num_uana_src = 0;
191 m_num_uana_dest = 0;
192 m_max_src = 0xffffffff;
193 m_time = 0;
194 m_valid = TRUE;
195 m_sum = 0.0f;
196 m_current = 0.0f;
197 m_last = 0.0f;
198 m_op = UGEN_OP_TICK;
199 m_gain = 1.0f;
200 m_pan = 1.0f;
201 m_next = 0.0f;
202 m_use_next = FALSE;
204 shred = NULL;
205 owner = NULL;
207 // what a hack
208 m_is_uana = FALSE;
214 //-----------------------------------------------------------------------------
215 // name: done()
216 // desc: ...
217 //-----------------------------------------------------------------------------
218 void Chuck_UGen::done()
220 if( this->shred )
221 shred->remove( this );
223 assert( this->m_ref_count == 0 );
225 // disconnect
226 this->disconnect( TRUE );
227 m_valid = FALSE;
229 fa_done( m_src_list, m_src_cap );
230 fa_done( m_dest_list, m_dest_cap );
231 fa_done( m_src_uana_list, m_src_uana_cap );
232 fa_done( m_dest_uana_list, m_dest_uana_cap );
234 // TODO: m_multi_chan, break ref count loop
240 //-----------------------------------------------------------------------------
241 // name: alloc_multi_chan()
242 // desc: ...
243 //-----------------------------------------------------------------------------
244 void Chuck_UGen::alloc_multi_chan( t_CKUINT num_ins, t_CKUINT num_outs )
246 // get max of num_ins and num_outs
247 m_multi_chan_size = ( num_ins > num_outs ? num_ins : num_outs );
249 // allocate
250 m_multi_chan = new Chuck_UGen *[m_multi_chan_size];
251 // zero it out, whoever call this will fill in
252 memset( m_multi_chan, 0, m_multi_chan_size * sizeof(Chuck_UGen *) );
254 // mono
255 if( m_multi_chan_size == 1 )
257 // zero out
258 m_multi_chan_size = 0;
259 // self
260 m_multi_chan[0] = this;
263 // remember
264 m_num_ins = num_ins;
265 m_num_outs = num_outs;
271 //-----------------------------------------------------------------------------
272 // name: set_max_src()
273 // dsec: ...
274 //-----------------------------------------------------------------------------
275 t_CKBOOL Chuck_UGen::set_max_src( t_CKUINT num )
277 m_max_src = num;
278 return TRUE;
284 //-----------------------------------------------------------------------------
285 // name: get_num_src()
286 // desc: ...
287 //-----------------------------------------------------------------------------
288 t_CKUINT Chuck_UGen::get_num_src()
290 return m_num_src;
296 //-----------------------------------------------------------------------------
297 // name: add()
298 // dsec: ...
299 //-----------------------------------------------------------------------------
300 t_CKBOOL Chuck_UGen::add( Chuck_UGen * src, t_CKBOOL isUpChuck )
302 // examine ins and outs
303 t_CKUINT outs = src->m_num_outs;
304 t_CKUINT ins = this->m_num_ins;
305 t_CKUINT i;
307 if( outs >= 1 && ins == 1 )
309 // check if already connected
310 // if( fa_lookup( m_src_list, m_num_src, src ) )
311 // return FALSE;
312 // check for limit
313 if( m_num_src >= m_max_src )
314 return FALSE;
316 // append
317 fa_push_back( m_src_list, m_src_cap, m_num_src, src );
318 m_num_src++;
319 src->add_ref();
320 src->add_by( this, isUpChuck );
322 // upchuck
323 if( isUpChuck )
325 // add to uana list
326 fa_push_back( m_src_uana_list, m_src_uana_cap, m_num_uana_src, src );
327 m_num_uana_src++;
328 // TODO: verify that we don't need to reference count
331 /* else if( outs >= 2 && ins == 1 )
333 // check if already connect
334 if( fa_lookup( m_src_list, m_num_src, src ) )
335 return FALSE;
336 // check for limit
337 if( m_num_src >= m_max_src )
338 return FALSE;
340 // append
341 fa_push_back( m_src_list, m_src_cap, m_num_src, src );
342 m_num_src++;
343 src->add_ref();
344 src->add_by( this );
345 } */
346 else if( outs == 1 && ins >= 2 )
348 // add to each channel
349 for( i = 0; i < ins; i++ )
350 if( !this->m_multi_chan[i]->add( src, isUpChuck ) ) return FALSE;
352 else if( outs >= 2 && ins >= 2 )
354 // add to each channel
355 for( i = 0; i < ins; i++ )
356 if( !this->m_multi_chan[i]->add( src->m_multi_chan[i%outs], isUpChuck ) ) return FALSE;
358 else
360 EM_error3( "internal error: unhandled UGen add: outs: %d ins: %d", outs, ins );
361 assert( FALSE );
364 return TRUE;
370 //-----------------------------------------------------------------------------
371 // name: add_by()
372 // dsec: ...
373 //-----------------------------------------------------------------------------
374 void Chuck_UGen::add_by( Chuck_UGen * dest, t_CKBOOL isUpChuck )
376 // append
377 fa_push_back( m_dest_list, m_dest_cap, m_num_dest, dest );
378 dest->add_ref();
379 m_num_dest++;
381 // uana
382 if( isUpChuck )
384 // add to uana list
385 fa_push_back( m_dest_uana_list, m_dest_uana_cap, m_num_uana_dest, dest );
386 m_num_uana_dest++;
387 // TODO: verify we don't need to ref count
394 //-----------------------------------------------------------------------------
395 // name: remove()
396 // dsec: ...
397 //-----------------------------------------------------------------------------
398 t_CKBOOL Chuck_UGen::remove( Chuck_UGen * src )
400 // ins and outs
401 t_CKUINT outs = src->m_num_outs;
402 t_CKUINT ins = this->m_num_ins;
403 t_CKUINT i;
404 t_CKBOOL ret = FALSE;
406 // take action
407 if( outs >= 1 && ins == 1 )
409 if( m_num_src == 0 ) return FALSE;
411 // remove from uana list (first, due to ref count)
412 for( t_CKUINT j = 0; j < m_num_uana_src; j++ )
413 if( m_src_uana_list[j] == src )
415 // since src list is a super set of this list,
416 // removing here -> removing at least one from src list
417 for( t_CKUINT k = j+1; k < m_num_uana_src; k++ )
418 m_src_uana_list[k-1] = m_src_uana_list[k];
420 m_src_uana_list[--m_num_uana_src] = NULL;
423 // remove
424 for( t_CKUINT i = 0; i < m_num_src; i++ )
425 if( m_src_list[i] == src )
427 ret = TRUE;
428 for( t_CKUINT j = i+1; j < m_num_src; j++ )
429 m_src_list[j-1] = m_src_list[j];
431 m_src_list[--m_num_src] = NULL;
432 src->remove_by( this );
433 src->release();
437 /* else if( outs >= 2 && ins == 1 )
439 if( m_num_src == 0 ) return FALSE;
441 // remove
442 for( t_CKUINT i = 0; i < m_num_src; i++ )
443 if( m_src_list[i] == src )
445 ret = TRUE;
446 for( t_CKUINT j = i+1; j < m_num_src; j++ )
447 m_src_list[j-1] = m_src_list[j];
449 m_src_list[--m_num_src] = NULL;
450 src->remove_by( this );
451 src->release();
453 } */
454 else if( outs == 1 && ins >= 2 )
456 for( i = 0; i < ins; i++ )
457 if( !m_multi_chan[i]->remove( src ) ) return FALSE;
458 ret = TRUE;
460 else if( outs >= 2 && ins >= 2 )
462 for( i = 0; i < ins; i++ )
463 if( !m_multi_chan[i]->remove( src->m_multi_chan[i%outs] ) ) return FALSE;
464 ret = TRUE;
467 return ret;
473 //-----------------------------------------------------------------------------
474 // name: remove_by()
475 // dsec: ...
476 //-----------------------------------------------------------------------------
477 void Chuck_UGen::remove_by( Chuck_UGen * dest )
479 // remove from uana list (first due to reference count)
480 for( t_CKUINT j = 0; j < m_num_uana_dest; j++ )
481 if( m_dest_uana_list[j] == dest )
483 // get rid of it
484 for( t_CKUINT k = j+1; k < m_num_uana_dest; k++ )
485 m_dest_uana_list[k-1] = m_dest_uana_list[k];
487 // null last element
488 m_dest_uana_list[--m_num_uana_dest] = NULL;
491 // remove
492 for( t_CKUINT i = 0; i < m_num_dest; i++ )
493 if( m_dest_list[i] == dest )
495 // get rid of it
496 for( t_CKUINT j = i+1; j < m_num_dest; j++ )
497 m_dest_list[j-1] = m_dest_list[j];
499 // release
500 dest->release();
501 // null the last element
502 m_dest_list[--m_num_dest] = NULL;
509 //-----------------------------------------------------------------------------
510 // name: add()
511 // dsec: ...
512 //-----------------------------------------------------------------------------
513 void Chuck_UGen::remove_all( )
515 assert( this->m_num_dest == 0 );
517 // remove
518 while( m_num_src > 0 )
520 // make sure at least one got disconnected
521 if( !this->remove( m_src_list[0] ) )
523 // TODO: figure out why this is necessary!
525 // get rid of it, but don't release
526 for( t_CKUINT j = 1; j < m_num_src; j++ )
527 m_src_list[j-1] = m_src_list[j];
529 // null the last element
530 m_src_list[--m_num_src] = NULL;
538 //-----------------------------------------------------------------------------
539 // name: disconnect()
540 // desc: ...
541 //-----------------------------------------------------------------------------
542 t_CKBOOL Chuck_UGen::disconnect( t_CKBOOL recursive )
544 // remove
545 while( m_num_dest > 0 )
547 // make sure at least one got disconnected
548 if( !m_dest_list[0]->remove( this ) )
550 // get rid of it, but don't release
551 for( t_CKUINT j = 1; j < m_num_dest; j++ )
552 m_dest_list[j-1] = m_dest_list[j];
554 // null the last element
555 m_dest_list[--m_num_dest] = NULL;
559 // for( unsigned int i = 0; i < m_num_dest; i++ )
560 // m_dest_list[i]->remove( this );
561 // m_num_dest = 0;
563 // disconnect src too?
564 if( recursive )
565 this->remove_all();
567 return TRUE;
573 //-----------------------------------------------------------------------------
574 // name: is_connected_from()
575 // desc: ...
576 //-----------------------------------------------------------------------------
577 t_CKBOOL Chuck_UGen::is_connected_from( Chuck_UGen * src )
579 if( m_src_list != NULL && fa_lookup( m_src_list, m_num_src, src ) )
580 return TRUE;
582 // multichannel
583 if( m_multi_chan != NULL )
585 for( t_CKUINT i = 0; i < m_multi_chan_size; i++ )
587 if( fa_lookup( m_multi_chan[i]->m_src_list,
588 m_multi_chan[i]->m_num_src, src ) )
589 return TRUE;
593 return FALSE;
599 //-----------------------------------------------------------------------------
600 // name: tick()
601 // dsec: ...
602 //-----------------------------------------------------------------------------
603 t_CKBOOL Chuck_UGen::system_tick( t_CKTIME now )
605 if( m_time >= now )
606 return m_valid;
608 t_CKUINT i; Chuck_UGen * ugen; SAMPLE multi;
610 // inc time
611 m_time = now;
612 // initial sum
613 m_sum = 0.0f;
614 if( m_num_src )
616 ugen = m_src_list[0];
617 if( ugen->m_time < now ) ugen->system_tick( now );
618 m_sum = ugen->m_current;
620 // tick the src list
621 for( i = 1; i < m_num_src; i++ )
623 ugen = m_src_list[i];
624 if( ugen->m_time < now ) ugen->system_tick( now );
625 if( ugen->m_valid )
627 if( m_op <= 1 )
628 m_sum += ugen->m_current;
629 else // special ops
631 switch( m_op )
633 case 2: m_sum -= ugen->m_current; break;
634 case 3: m_sum *= ugen->m_current; break;
635 case 4: m_sum /= ugen->m_current; break;
636 default: m_sum += ugen->m_current; break;
643 // tick multiple channels
644 multi = 0.0f;
645 if( m_multi_chan_size )
647 for( i = 0; i < m_multi_chan_size; i++ )
649 ugen = m_multi_chan[i];
650 if( ugen->m_time < now ) ugen->system_tick( now );
651 // multiple channels are added
652 multi += ugen->m_current;
655 // scale multi
656 multi /= m_multi_chan_size;
657 m_sum += multi;
660 // if owner
661 if( owner != NULL && owner->m_time < now )
662 owner->system_tick( now );
664 if( m_op > 0 ) // UGEN_OP_TICK
666 // tick the ugen
667 if( tick ) m_valid = tick( this, m_sum, &m_current, NULL );
668 if( !m_valid ) m_current = 0.0f;
669 // apply gain and pan
670 m_current *= m_gain * m_pan;
671 // dedenormal
672 CK_DDN( m_current );
673 // save as last
674 m_last = m_current;
675 return m_valid;
677 else if( m_op < 0 ) // UGEN_OP_PASS
679 // pass through
680 m_current = m_sum;
681 m_last = m_current;
682 return TRUE;
684 else // UGEN_OP_STOP
685 m_current = 0.0f;
687 m_last = m_current;
688 return TRUE;
694 //-----------------------------------------------------------------------------
695 // name: Chuck_UAna()
696 // desc: constructor
697 //-----------------------------------------------------------------------------
698 Chuck_UAna::Chuck_UAna() : Chuck_UGen()
700 // mark as true
701 m_is_uana = TRUE;
702 // reset uana time (HACK: negative so upchuck() works at now=0)
703 m_uana_time = -1;
704 // zero out proxy
705 // m_blob_proxy = NULL;
711 //-----------------------------------------------------------------------------
712 // name: ~Chuck_UAna()
713 // desc: destructor
714 //-----------------------------------------------------------------------------
715 Chuck_UAna::~Chuck_UAna()
717 // do nothing for now
723 //-----------------------------------------------------------------------------
724 // name: blobProxy()
725 // desc: ...
726 //-----------------------------------------------------------------------------
727 Chuck_UAnaBlobProxy * Chuck_UAna::blobProxy() const { return getBlobProxy( this ); }
732 //-----------------------------------------------------------------------------
733 // name: numIncomingUAnae()
734 // desc: ...
735 //-----------------------------------------------------------------------------
736 t_CKINT Chuck_UAna::numIncomingUAnae() const { return m_num_uana_src; }
741 //-----------------------------------------------------------------------------
742 // name: getIncomingBlob()
743 // desc: ...
744 //-----------------------------------------------------------------------------
745 Chuck_UAnaBlobProxy * Chuck_UAna::getIncomingBlob( t_CKUINT index ) const
747 // sanity check
748 if( index >= m_num_uana_src )
749 return NULL;
751 // TODO: DANGER: this cast is very dangerous!
752 return ((Chuck_UAna *)m_src_uana_list[index])->blobProxy();
758 //-----------------------------------------------------------------------------
759 // name: getIncomingUAna()
760 // desc: ...
761 //-----------------------------------------------------------------------------
762 Chuck_UAna * Chuck_UAna::getIncomingUAna( t_CKUINT index ) const
764 // sanity check
765 if( index >= m_num_uana_src )
766 return NULL;
768 // TODO: DANGER: this cast is very dangerous!
769 return ((Chuck_UAna *)m_src_uana_list[index]);
775 //-----------------------------------------------------------------------------
776 // name: is_up_connected_from()
777 // desc: ...
778 //-----------------------------------------------------------------------------
779 t_CKBOOL Chuck_UAna::is_up_connected_from( Chuck_UAna * src )
781 if( m_src_uana_list != NULL && fa_lookup( m_src_uana_list, m_num_uana_src, src ) )
782 return TRUE;
784 // TODO: multichannel?
786 return FALSE;
792 //-----------------------------------------------------------------------------
793 // name: tock()
794 // dsec: ...
795 //-----------------------------------------------------------------------------
796 t_CKBOOL Chuck_UAna::system_tock( t_CKTIME now )
798 if( m_uana_time >= now )
799 return m_valid;
801 t_CKUINT i; Chuck_UGen * ugen; Chuck_UAna * uana; SAMPLE multi;
803 // inc time
804 m_uana_time = now;
805 if( m_num_src )
807 ugen = m_src_list[0];
808 // TODO: address this horrendous hack
809 if( ugen->m_is_uana )
811 // cast to uana
812 uana = (Chuck_UAna *)ugen;
813 // tock it
814 if( uana->m_uana_time < now ) uana->system_tock( now );
816 // sum
817 m_sum = ugen->m_current;
819 // tick the src list
820 for( i = 1; i < m_num_src; i++ )
822 ugen = m_src_list[i];
823 // TODO: address this horrendous hack
824 if( ugen->m_is_uana )
826 // cast to uana
827 uana = (Chuck_UAna *)ugen;
828 // tock it
829 if( uana->m_uana_time < now ) uana->system_tock( now );
832 if( ugen->m_valid )
834 // TODO: operate on blob data
839 // tock multiple channels
840 multi = 0.0f;
841 if( m_multi_chan_size )
843 for( i = 0; i < m_multi_chan_size; i++ )
845 ugen = m_multi_chan[i];
846 // TODO: address this horrendous hack
847 if( ugen->m_is_uana )
849 // cast to uana
850 uana = (Chuck_UAna *)ugen;
851 // tock it
852 if( uana->m_uana_time < now ) uana->system_tock( now );
857 // if owner
858 if( owner != NULL && owner->m_is_uana )
860 // cast to uana
861 uana = (Chuck_UAna *)owner;
862 // tock it
863 if( uana->m_uana_time < now ) uana->system_tock( now );
866 if( m_op > 0 ) // UGEN_OP_TOCK
868 // tock the uana
869 if( tock ) m_valid = tock( this, this, blobProxy(), NULL );
870 if( !m_valid ) { /* clear out blob? */ }
871 // timestamp the blob
872 blobProxy()->when() = now;
873 // TODO: set current_blob to out_blob
874 // TODO: set last_blob to current
875 return m_valid;
877 else if( m_op < 0 ) // UGEN_OP_PASS
879 // pass through
880 // TODO: anything?
881 return TRUE;
883 else // UGEN_OP_STOP
885 // TODO: anything?
888 // TODO: set m_last_blob to current?
890 return TRUE;