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
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
33 //-----------------------------------------------------------------------------
34 #include "chuck_ugen.h"
36 #include "chuck_lang.h"
37 #include "chuck_errmsg.h"
43 //-----------------------------------------------------------------------------
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 //-----------------------------------------------------------------------------
59 //-----------------------------------------------------------------------------
60 void fa_init( Chuck_UGen
** & base
, t_CKUINT
& capacity
)
69 //-----------------------------------------------------------------------------
72 //-----------------------------------------------------------------------------
73 void fa_done( Chuck_UGen
** & base
, t_CKUINT
& capacity
)
75 if( base
) delete [] base
;
83 //-----------------------------------------------------------------------------
86 //-----------------------------------------------------------------------------
87 void fa_resize( Chuck_UGen
** & base
, t_CKUINT
& capacity
)
96 Chuck_UGen
** new_base
= new Chuck_UGen
*[capacity
];
101 memcpy( new_base
, base
, capacity
/ 2 * sizeof(Chuck_UGen
*) );
113 //-----------------------------------------------------------------------------
114 // name: fa_push_back()
116 //-----------------------------------------------------------------------------
117 void fa_push_back( Chuck_UGen
** & base
, t_CKUINT
& capacity
,
118 t_CKUINT size
, Chuck_UGen
* value
)
121 if( size
== capacity
) fa_resize( base
, capacity
);
129 //-----------------------------------------------------------------------------
132 //-----------------------------------------------------------------------------
133 t_CKBOOL
fa_lookup( Chuck_UGen
** base
, t_CKUINT size
, const Chuck_UGen
* value
)
136 for( t_CKUINT i
= 0; i
< size
; i
++ )
137 if( base
[i
] == value
)
147 //-----------------------------------------------------------------------------
148 // name: Chuck_UGen()
150 //-----------------------------------------------------------------------------
151 Chuck_UGen::Chuck_UGen()
159 //-----------------------------------------------------------------------------
160 // name: ~Chuck_UGen()
162 //-----------------------------------------------------------------------------
163 Chuck_UGen::~Chuck_UGen()
171 //-----------------------------------------------------------------------------
174 //-----------------------------------------------------------------------------
175 void Chuck_UGen::init()
180 m_multi_chan_size
= 0;
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
);
192 m_max_src
= 0xffffffff;
214 //-----------------------------------------------------------------------------
217 //-----------------------------------------------------------------------------
218 void Chuck_UGen::done()
221 shred
->remove( this );
223 assert( this->m_ref_count
== 0 );
226 this->disconnect( TRUE
);
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()
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
);
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
*) );
255 if( m_multi_chan_size
== 1 )
258 m_multi_chan_size
= 0;
260 m_multi_chan
[0] = this;
265 m_num_outs
= num_outs
;
271 //-----------------------------------------------------------------------------
272 // name: set_max_src()
274 //-----------------------------------------------------------------------------
275 t_CKBOOL
Chuck_UGen::set_max_src( t_CKUINT num
)
284 //-----------------------------------------------------------------------------
285 // name: get_num_src()
287 //-----------------------------------------------------------------------------
288 t_CKUINT
Chuck_UGen::get_num_src()
296 //-----------------------------------------------------------------------------
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
;
307 if( outs
>= 1 && ins
== 1 )
309 // check if already connected
310 // if( fa_lookup( m_src_list, m_num_src, src ) )
313 if( m_num_src
>= m_max_src
)
317 fa_push_back( m_src_list
, m_src_cap
, m_num_src
, src
);
320 src
->add_by( this, isUpChuck
);
326 fa_push_back( m_src_uana_list
, m_src_uana_cap
, m_num_uana_src
, 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 ) )
337 if( m_num_src >= m_max_src )
341 fa_push_back( m_src_list, m_src_cap, m_num_src, src );
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
;
360 EM_error3( "internal error: unhandled UGen add: outs: %d ins: %d", outs
, ins
);
370 //-----------------------------------------------------------------------------
373 //-----------------------------------------------------------------------------
374 void Chuck_UGen::add_by( Chuck_UGen
* dest
, t_CKBOOL isUpChuck
)
377 fa_push_back( m_dest_list
, m_dest_cap
, m_num_dest
, dest
);
385 fa_push_back( m_dest_uana_list
, m_dest_uana_cap
, m_num_uana_dest
, dest
);
387 // TODO: verify we don't need to ref count
394 //-----------------------------------------------------------------------------
397 //-----------------------------------------------------------------------------
398 t_CKBOOL
Chuck_UGen::remove( Chuck_UGen
* src
)
401 t_CKUINT outs
= src
->m_num_outs
;
402 t_CKUINT ins
= this->m_num_ins
;
404 t_CKBOOL ret
= FALSE
;
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
;
424 for( t_CKUINT i
= 0; i
< m_num_src
; i
++ )
425 if( m_src_list
[i
] == src
)
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 );
437 /* else if( outs >= 2 && ins == 1 )
439 if( m_num_src == 0 ) return FALSE;
442 for( t_CKUINT i = 0; i < m_num_src; i++ )
443 if( m_src_list[i] == src )
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 );
454 else if( outs
== 1 && ins
>= 2 )
456 for( i
= 0; i
< ins
; i
++ )
457 if( !m_multi_chan
[i
]->remove( src
) ) return FALSE
;
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
;
473 //-----------------------------------------------------------------------------
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
)
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
];
488 m_dest_uana_list
[--m_num_uana_dest
] = NULL
;
492 for( t_CKUINT i
= 0; i
< m_num_dest
; i
++ )
493 if( m_dest_list
[i
] == dest
)
496 for( t_CKUINT j
= i
+1; j
< m_num_dest
; j
++ )
497 m_dest_list
[j
-1] = m_dest_list
[j
];
501 // null the last element
502 m_dest_list
[--m_num_dest
] = NULL
;
509 //-----------------------------------------------------------------------------
512 //-----------------------------------------------------------------------------
513 void Chuck_UGen::remove_all( )
515 assert( this->m_num_dest
== 0 );
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()
541 //-----------------------------------------------------------------------------
542 t_CKBOOL
Chuck_UGen::disconnect( t_CKBOOL recursive
)
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 );
563 // disconnect src too?
573 //-----------------------------------------------------------------------------
574 // name: is_connected_from()
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
) )
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
) )
599 //-----------------------------------------------------------------------------
602 //-----------------------------------------------------------------------------
603 t_CKBOOL
Chuck_UGen::system_tick( t_CKTIME now
)
608 t_CKUINT i
; Chuck_UGen
* ugen
; SAMPLE multi
;
616 ugen
= m_src_list
[0];
617 if( ugen
->m_time
< now
) ugen
->system_tick( now
);
618 m_sum
= ugen
->m_current
;
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
);
628 m_sum
+= ugen
->m_current
;
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
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
;
656 multi
/= m_multi_chan_size
;
661 if( owner
!= NULL
&& owner
->m_time
< now
)
662 owner
->system_tick( now
);
664 if( m_op
> 0 ) // UGEN_OP_TICK
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
;
677 else if( m_op
< 0 ) // UGEN_OP_PASS
694 //-----------------------------------------------------------------------------
695 // name: Chuck_UAna()
697 //-----------------------------------------------------------------------------
698 Chuck_UAna::Chuck_UAna() : Chuck_UGen()
702 // reset uana time (HACK: negative so upchuck() works at now=0)
705 // m_blob_proxy = NULL;
711 //-----------------------------------------------------------------------------
712 // name: ~Chuck_UAna()
714 //-----------------------------------------------------------------------------
715 Chuck_UAna::~Chuck_UAna()
717 // do nothing for now
723 //-----------------------------------------------------------------------------
726 //-----------------------------------------------------------------------------
727 Chuck_UAnaBlobProxy
* Chuck_UAna::blobProxy() const { return getBlobProxy( this ); }
732 //-----------------------------------------------------------------------------
733 // name: numIncomingUAnae()
735 //-----------------------------------------------------------------------------
736 t_CKINT
Chuck_UAna::numIncomingUAnae() const { return m_num_uana_src
; }
741 //-----------------------------------------------------------------------------
742 // name: getIncomingBlob()
744 //-----------------------------------------------------------------------------
745 Chuck_UAnaBlobProxy
* Chuck_UAna::getIncomingBlob( t_CKUINT index
) const
748 if( index
>= m_num_uana_src
)
751 // TODO: DANGER: this cast is very dangerous!
752 return ((Chuck_UAna
*)m_src_uana_list
[index
])->blobProxy();
758 //-----------------------------------------------------------------------------
759 // name: getIncomingUAna()
761 //-----------------------------------------------------------------------------
762 Chuck_UAna
* Chuck_UAna::getIncomingUAna( t_CKUINT index
) const
765 if( index
>= m_num_uana_src
)
768 // TODO: DANGER: this cast is very dangerous!
769 return ((Chuck_UAna
*)m_src_uana_list
[index
]);
775 //-----------------------------------------------------------------------------
776 // name: is_up_connected_from()
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
) )
784 // TODO: multichannel?
792 //-----------------------------------------------------------------------------
795 //-----------------------------------------------------------------------------
796 t_CKBOOL
Chuck_UAna::system_tock( t_CKTIME now
)
798 if( m_uana_time
>= now
)
801 t_CKUINT i
; Chuck_UGen
* ugen
; Chuck_UAna
* uana
; SAMPLE multi
;
807 ugen
= m_src_list
[0];
808 // TODO: address this horrendous hack
809 if( ugen
->m_is_uana
)
812 uana
= (Chuck_UAna
*)ugen
;
814 if( uana
->m_uana_time
< now
) uana
->system_tock( now
);
817 m_sum
= ugen
->m_current
;
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
)
827 uana
= (Chuck_UAna
*)ugen
;
829 if( uana
->m_uana_time
< now
) uana
->system_tock( now
);
834 // TODO: operate on blob data
839 // tock multiple channels
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
)
850 uana
= (Chuck_UAna
*)ugen
;
852 if( uana
->m_uana_time
< now
) uana
->system_tock( now
);
858 if( owner
!= NULL
&& owner
->m_is_uana
)
861 uana
= (Chuck_UAna
*)owner
;
863 if( uana
->m_uana_time
< now
) uana
->system_tock( now
);
866 if( m_op
> 0 ) // UGEN_OP_TOCK
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
877 else if( m_op
< 0 ) // UGEN_OP_PASS
888 // TODO: set m_last_blob to current?