*** empty log message ***
[chuck-blob.git] / v2 / chuck_vm.cpp
blob3958a2954277904ba8d7a0a081595c66bec356ef
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 // file: chuck_vm.cpp
27 // desc: ...
29 // authors: Ge Wang (gewang@cs.princeton.edu)
30 // Perry R. Cook (prc@cs.princeton.edu)
31 // date: Autumn 2002
32 //-----------------------------------------------------------------------------
33 #include "chuck_vm.h"
34 #include "chuck_instr.h"
35 #include "chuck_bbq.h"
36 #include "chuck_errmsg.h"
37 #include "chuck_dl.h"
38 #include "chuck_type.h"
39 #include "chuck_globals.h"
40 #include "chuck_lang.h"
41 #include "ugen_xxx.h"
43 using namespace std;
45 #if defined(__PLATFORM_WIN32__)
46 #include <windows.h>
47 #else
48 #include <unistd.h>
49 #include <pthread.h>
50 #endif
55 //-----------------------------------------------------------------------------
56 // name: struct Chuck_VM_Frame
57 // desc: func frame
58 //-----------------------------------------------------------------------------
59 struct Chuck_VM_Frame
61 public:
62 t_CKUINT size;
64 public:
65 Chuck_VM_Frame() { size = 0; }
66 ~Chuck_VM_Frame() { }
72 //-----------------------------------------------------------------------------
73 // name: struct Chuck_VM_Func
74 // desc: vm function
75 //-----------------------------------------------------------------------------
76 struct Chuck_VM_Func
78 public:
79 Chuck_VM_Code * code;
80 Chuck_VM_Frame * frame;
81 t_CKUINT index;
83 public:
84 Chuck_VM_Func() { code = NULL; frame = NULL; index = 0; }
85 ~Chuck_VM_Func() { }
91 //-----------------------------------------------------------------------------
92 // name: struct Chuck_VM_FTable
93 // desc: function table
94 //-----------------------------------------------------------------------------
95 struct Chuck_VM_FTable
97 public:
98 Chuck_VM_FTable();
99 ~Chuck_VM_FTable();
101 public:
102 Chuck_VM_Func * get_func( t_CKUINT index );
103 t_CKUINT append( Chuck_VM_Func * f );
104 t_CKBOOL remove( t_CKUINT index );
106 public:
107 vector<Chuck_VM_Func *> func_table;
113 //-----------------------------------------------------------------------------
114 // name: Chuck_VM()
115 // desc: ...
116 //-----------------------------------------------------------------------------
117 Chuck_VM::Chuck_VM()
119 m_shreds = NULL;
120 m_num_shreds = 0;
121 m_shreduler = NULL;
122 m_num_dumped_shreds = 0;
123 m_msg_buffer = NULL;
124 m_reply_buffer = NULL;
125 m_event_buffer = NULL;
126 m_shred_id = 0;
127 m_halt = TRUE;
128 m_audio = FALSE;
129 m_block = TRUE;
130 m_running = FALSE;
132 m_audio_started = FALSE;
133 m_dac = NULL;
134 m_adc = NULL;
135 m_bunghole = NULL;
136 m_num_dac_channels = 0;
137 m_num_adc_channels = 0;
138 m_init = FALSE;
144 //-----------------------------------------------------------------------------
145 // name: ~Chuck_VM()
146 // desc: ...
147 //-----------------------------------------------------------------------------
148 Chuck_VM::~Chuck_VM()
150 if( m_init ) shutdown();
154 // dac tick
155 //UGEN_TICK __dac_tick( Chuck_Object * SELF, SAMPLE in, SAMPLE * out )
156 //{ *out = in; return TRUE; }
157 //UGEN_TICK __bunghole_tick( Chuck_Object * SELF, SAMPLE in, SAMPLE * out )
158 //{ *out = 0.0f; return TRUE; }
161 // static
162 #ifdef __MACOSX_CORE__
163 t_CKINT Chuck_VM::our_priority = 85;
164 #else
165 t_CKINT Chuck_VM::our_priority = 0x7fffffff;
166 #endif
169 #if !defined(__PLATFORM_WIN32__) || defined(__WINDOWS_PTHREAD__)
170 //-----------------------------------------------------------------------------
171 // name: set_priority()
172 // desc: ...
173 //-----------------------------------------------------------------------------
174 t_CKBOOL Chuck_VM::set_priority( t_CKINT priority, Chuck_VM * vm )
176 struct sched_param param;
177 pthread_t tid = pthread_self();
178 int policy;
180 // log
181 EM_log( CK_LOG_INFO, "setting thread priority to: %ld...", priority );
183 // get for thread
184 if( pthread_getschedparam( tid, &policy, &param) )
186 if( vm )
187 vm->m_last_error = "could not get current scheduling parameters";
188 return FALSE;
191 // priority
192 param.sched_priority = priority;
193 // policy
194 policy = SCHED_RR;
195 // set for thread
196 if( pthread_setschedparam( tid, policy, &param ) )
198 if( vm )
199 vm->m_last_error = "could not set new scheduling parameters";
200 return FALSE;
203 return TRUE;
205 #else
206 //-----------------------------------------------------------------------------
207 // name: set_priority()
208 // desc: ...
209 //-----------------------------------------------------------------------------
210 t_CKBOOL Chuck_VM::set_priority( t_CKINT priority, Chuck_VM * vm )
212 // if priority is 0 then done
213 if( !priority ) return TRUE;
215 // set the priority class of the process
216 // if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) )
217 // return FALSE;
219 // log
220 EM_log( CK_LOG_INFO, "setting thread priority to: %ld...", priority );
222 // set the priority the thread
223 // if( !SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL ) )
224 if( !SetThreadPriority( GetCurrentThread(), priority ) )
226 if( vm )
227 vm->m_last_error = "could not set new scheduling parameters";
228 return FALSE;
231 return TRUE;
233 #endif
238 //-----------------------------------------------------------------------------
239 // name: initialize()
240 // desc: ...
241 //-----------------------------------------------------------------------------
242 t_CKBOOL Chuck_VM::initialize( t_CKBOOL enable_audio, t_CKBOOL halt, t_CKUINT srate,
243 t_CKUINT buffer_size, t_CKUINT num_buffers,
244 t_CKUINT dac, t_CKUINT adc, t_CKUINT dac_chan,
245 t_CKUINT adc_chan, t_CKBOOL block )
247 if( m_init )
249 m_last_error = "VM already initialized!";
250 return FALSE;
253 // boost thread priority
254 // if( priority != 0x7fffffff && !set_priority( priority, this ) )
255 // return FALSE;
257 // log
258 EM_log( CK_LOG_SYSTEM, "initializing virtual machine..." );
259 EM_pushlog(); // push stack
260 EM_log( CK_LOG_SYSTEM, "behavior: %s", halt ? "HALT" : "LOOP" );
262 // lockdown
263 Chuck_VM_Object::lock_all();
265 // allocate bbq
266 m_bbq = new BBQ;
267 m_halt = halt;
268 m_audio = enable_audio;
269 m_block = block;
271 // log
272 EM_log( CK_LOG_SYSTEM, "allocating shreduler..." );
273 // allocate shreduler
274 m_shreduler = new Chuck_VM_Shreduler;
275 m_shreduler->bbq = m_bbq;
276 m_shreduler->rt_audio = enable_audio;
278 // log
279 EM_log( CK_LOG_SYSTEM, "allocating messaging buffers..." );
280 // allocate msg buffer
281 m_msg_buffer = new CBufferSimple;
282 m_msg_buffer->initialize( 1024, sizeof(Chuck_Msg *) );
283 //m_msg_buffer->join(); // this should return 0
284 m_reply_buffer = new CBufferSimple;
285 m_reply_buffer->initialize( 1024, sizeof(Chuck_Msg *) );
286 //m_reply_buffer->join(); // this should return 0 too
287 m_event_buffer = new CBufferSimple;
288 m_event_buffer->initialize( 1024, sizeof(Chuck_Event *) );
289 //m_event_buffer->join(); // this should also return 0
291 // log
292 EM_log( CK_LOG_SYSTEM, "real-time audio: %s", enable_audio ? "YES" : "NO" );
293 EM_log( CK_LOG_SYSTEM, "mode: %s", block ? "BLOCKING" : "CALLBACK" );
294 EM_log( CK_LOG_SYSTEM, "sample rate: %ld", srate );
295 EM_log( CK_LOG_SYSTEM, "buffer size: %ld", buffer_size );
296 if( enable_audio )
298 EM_log( CK_LOG_SYSTEM, "num buffers: %ld", num_buffers );
299 EM_log( CK_LOG_SYSTEM, "devices adc: %ld dac: %d (default 0)", adc, dac );
301 EM_log( CK_LOG_SYSTEM, "channels in: %ld out: %ld", adc_chan, dac_chan );
302 m_num_adc_channels = adc_chan;
303 m_num_dac_channels = dac_chan;
305 // at least set the sample rate and buffer size
306 m_bbq->set_srate( srate );
307 m_bbq->set_bufsize( buffer_size );
308 m_bbq->set_numbufs( num_buffers );
309 m_bbq->set_inouts( adc, dac );
310 m_bbq->set_chans( adc_chan, dac_chan );
312 // pop log
313 EM_poplog();
315 // TODO: clean up all the dynamic objects here on failure
316 // and in the shutdown function!
318 return m_init = TRUE;
324 //-----------------------------------------------------------------------------
325 // name: initialize_synthesis()
326 // desc: requires type system
327 //-----------------------------------------------------------------------------
328 t_CKBOOL Chuck_VM::initialize_synthesis( )
330 if( !m_init )
332 m_last_error = "VM initialize_synthesis() called on raw VM";
333 return FALSE;
336 if( !g_t_dac || !g_t_adc )
338 m_last_error = "VM initialize_synthesis() called before type system initialized";
339 return FALSE;
342 if( m_dac != NULL )
344 m_last_error = "VM synthesis already initialized";
345 return FALSE;
348 // log
349 EM_log( CK_LOG_SYSTEM, "initializing synthesis engine..." );
350 // push indent
351 EM_pushlog();
353 // log
354 EM_log( CK_LOG_SEVERE, "initializing 'dac'..." );
355 // allocate dac and adc
356 g_t_dac->ugen_info->num_outs =
357 g_t_dac->ugen_info->num_ins = m_num_dac_channels;
358 m_dac = (Chuck_UGen *)instantiate_and_initialize_object( g_t_dac, NULL );
359 object_ctor( m_dac, NULL, NULL ); // TODO: this can't be the place to do this
360 stereo_ctor( m_dac, NULL, NULL ); // TODO: is the NULL shred a problem?
361 multi_ctor( m_dac, NULL, NULL ); // TODO: remove and let type system do this
362 m_dac->add_ref();
363 // lock it
364 m_dac->lock();
366 // log
367 EM_log( CK_LOG_SEVERE, "initializing 'adc'..." );
368 g_t_adc->ugen_info->num_ins =
369 g_t_adc->ugen_info->num_outs = m_num_adc_channels;
370 m_adc = (Chuck_UGen *)instantiate_and_initialize_object( g_t_adc, NULL );
371 object_ctor( m_adc, NULL, NULL ); // TODO: this can't be the place to do this
372 stereo_ctor( m_adc, NULL, NULL );
373 multi_ctor( m_adc, NULL, NULL ); // TODO: remove and let type system do this
374 m_adc->add_ref();
375 // lock it
376 m_adc->lock();
378 // log
379 EM_log( CK_LOG_SEVERE, "initializing 'blackhole'..." );
380 m_bunghole = new Chuck_UGen;
381 m_bunghole->add_ref();
382 m_bunghole->lock();
383 initialize_object( m_bunghole, &t_ugen );
384 m_bunghole->tick = NULL;
385 m_shreduler->m_dac = m_dac;
386 m_shreduler->m_adc = m_adc;
387 m_shreduler->m_bunghole = m_bunghole;
388 m_shreduler->m_num_dac_channels = m_num_dac_channels;
389 m_shreduler->m_num_adc_channels = m_num_adc_channels;
391 // log
392 EM_log( CK_LOG_SYSTEM, "initializing '%s' audio...", m_audio ? "real-time" : "fake-time" );
393 // init bbq
394 if( !m_bbq->initialize( m_num_dac_channels, m_num_adc_channels,
395 Digitalio::m_sampling_rate, 16,
396 Digitalio::m_buffer_size, Digitalio::m_num_buffers,
397 Digitalio::m_dac_n, Digitalio::m_adc_n,
398 m_block, this, m_audio ) )
400 m_last_error = "cannot initialize audio device (try using --silent/-s)";
401 // pop indent
402 EM_poplog();
403 return FALSE;
406 // pop indent
407 EM_poplog();
409 return TRUE;
415 //-----------------------------------------------------------------------------
416 // name: compensate_bbq()
417 // desc: ...
418 //-----------------------------------------------------------------------------
419 void Chuck_VM::compensate_bbq()
421 // set shreduler - the audio was initialized elsewhere
422 m_shreduler->bbq = m_bbq;
428 //-----------------------------------------------------------------------------
429 // name: shutdown()
430 // desc: ...
431 //-----------------------------------------------------------------------------
432 t_CKBOOL Chuck_VM::shutdown()
434 // make sure we are in the initialized state
435 if( !m_init ) return FALSE;
437 // log
438 EM_log( CK_LOG_SYSTEM, "shutting down virtual machine..." );
439 // push indent
440 EM_pushlog();
441 // unlockdown
442 Chuck_VM_Object::unlock_all();
444 // stop
445 if( m_running )
447 this->stop();
448 usleep( 50000 );
451 // shutdown audio
452 if( m_audio )
454 // log
455 EM_log( CK_LOG_SYSTEM, "shutting down real-time audio..." );
457 m_bbq->digi_out()->cleanup();
458 m_bbq->digi_in()->cleanup();
459 m_bbq->shutdown();
460 m_audio = FALSE;
462 // log
463 EM_log( CK_LOG_SYSTEM, "freeing bbq subsystem..." );
464 // clean up
465 SAFE_DELETE( m_bbq );
467 // log
468 EM_log( CK_LOG_SYSTEM, "freeing shreduler..." );
469 // free the shreduler
470 SAFE_DELETE( m_shreduler );
472 // log
473 EM_log( CK_LOG_SYSTEM, "freeing msg/reply/event buffers..." );
474 // free the msg buffer
475 SAFE_DELETE( m_msg_buffer );
476 // free the reply buffer
477 SAFE_DELETE( m_reply_buffer );
478 // free the event buffer
479 SAFE_DELETE( m_event_buffer );
481 // log
482 EM_log( CK_LOG_SEVERE, "clearing shreds..." );
483 // terminate shreds
484 Chuck_VM_Shred * curr = m_shreds, * prev = NULL;
485 while( curr )
487 prev = curr;
488 curr = curr->next;
489 // release shred
490 prev->release();
492 m_shreds = NULL;
493 m_num_shreds = 0;
495 // log
496 EM_pushlog();
497 EM_log( CK_LOG_SEVERE, "freeing dumped shreds..." );
498 // do it
499 this->release_dump();
500 EM_poplog();
502 // log
503 EM_log( CK_LOG_SYSTEM, "freeing special ugens..." );
504 // go
505 SAFE_RELEASE( m_dac );
506 SAFE_RELEASE( m_adc );
507 SAFE_RELEASE( m_bunghole );
509 m_init = FALSE;
511 // pop indent
512 EM_poplog();
514 return TRUE;
520 //-----------------------------------------------------------------------------
521 // name: start_audio()
522 // desc: ...
523 //-----------------------------------------------------------------------------
524 t_CKBOOL Chuck_VM::start_audio( )
526 // audio
527 if( !m_audio_started && m_audio )
529 EM_log( CK_LOG_SEVERE, "starting real-time audio..." );
530 m_bbq->digi_out()->start();
531 m_bbq->digi_in()->start();
534 // set the flag to true to avoid entering this function
535 m_audio_started = TRUE;
537 return TRUE;
543 //-----------------------------------------------------------------------------
544 // name: run()
545 // desc: ...
546 //-----------------------------------------------------------------------------
547 t_CKBOOL Chuck_VM::run( )
549 // check if init
550 if( m_dac == NULL )
552 m_last_error = "VM and/or synthesis not initialized...";
553 return FALSE;
556 // check if already running
557 if( m_running )
559 m_last_error = "virtual machine already running...";
560 return FALSE;
563 m_running = TRUE;
565 // log
566 EM_log( CK_LOG_SYSTEM, "running virtual machine..." );
567 // push indent
568 EM_pushlog();
570 // audio
571 //if( m_audio )
573 // log
574 EM_log( CK_LOG_SEVERE, "initializing audio buffers..." );
575 if( !m_bbq->digi_out()->initialize( ) )
577 m_last_error = "cannot open audio output (option: use --silent/-s)";
578 return FALSE;
581 m_bbq->digi_in()->initialize( );
584 // log
585 EM_log( CK_LOG_SEVERE, "virtual machine running..." );
586 // pop indent
587 EM_poplog();
589 // run
590 if( m_block ) this->run( -1 );
591 else
593 // compute shreds before first sample
594 if( !compute() )
596 // done
597 m_running = FALSE;
598 // log
599 EM_log( CK_LOG_SYSTEM, "virtual machine stopped..." );
601 else
603 // start audio
604 if( !m_audio_started ) start_audio();
606 // wait
607 while( m_running )
608 { usleep( 50000 ); }
612 return TRUE;
615 /*should we comment out what we just did?
616 i can't think of why it might be affecting this part of the vm
617 you never know
618 true*/
623 //-----------------------------------------------------------------------------
624 // name: compute()
625 // desc: ...
626 //-----------------------------------------------------------------------------
627 t_CKBOOL Chuck_VM::compute()
629 Chuck_VM_Shred *& shred = m_shreduler->m_current_shred;
630 Chuck_Msg * msg = NULL;
631 Chuck_Event * event = NULL;
632 t_CKBOOL iterate = TRUE;
634 // iteration until no more shreds/events/messages
635 while( iterate )
637 // get the shreds queued for 'now'
638 while(( shred = m_shreduler->get() ))
640 // set the current time of the shred
641 shred->now = shred->wake_time;
643 // track shred activation
644 CK_TRACK( Chuck_Stats::instance()->activate_shred( shred ) );
646 // run the shred
647 if( !shred->run( this ) )
649 // track shred deactivation
650 CK_TRACK( Chuck_Stats::instance()->deactivate_shred( shred ) );
652 this->free( shred, TRUE );
653 shred = NULL;
654 if( !m_num_shreds && m_halt ) return FALSE;
657 // track shred deactivation
658 CK_TRACK( if( shred ) Chuck_Stats::instance()->deactivate_shred( shred ) );
660 // zero out
661 shred = NULL;
664 // set to false for now
665 iterate = FALSE;
667 // broadcast queued events
668 while( m_event_buffer->get( &event, 1 ) )
669 { event->broadcast(); iterate = TRUE; }
671 // process messages
672 while( m_msg_buffer->get( &msg, 1 ) )
673 { process_msg( msg ); iterate = TRUE; }
675 // clear dumped shreds
676 if( m_num_dumped_shreds > 0 )
677 release_dump();
680 return TRUE;
686 //-----------------------------------------------------------------------------
687 // name: run()
688 // desc: ...
689 //-----------------------------------------------------------------------------
690 t_CKBOOL Chuck_VM::run( t_CKINT num_samps )
692 // loop it
693 while( num_samps )
695 // compute shreds
696 if( !compute() ) goto vm_stop;
698 // start audio
699 if( !m_audio_started ) start_audio();
701 // advance the shreduler
702 m_shreduler->advance();
704 // count
705 if( num_samps > 0 ) num_samps--;
708 return FALSE;
710 // vm stop here
711 vm_stop:
712 m_running = FALSE;
714 // log
715 EM_log( CK_LOG_SYSTEM, "virtual machine stopped..." );
717 return TRUE;
723 //-----------------------------------------------------------------------------
724 // name: pause()
725 // desc: ...
726 //-----------------------------------------------------------------------------
727 t_CKBOOL Chuck_VM::pause( )
729 m_running = FALSE;
731 return TRUE;
737 //-----------------------------------------------------------------------------
738 // name: stop()
739 // desc: ...
740 //-----------------------------------------------------------------------------
741 t_CKBOOL Chuck_VM::stop( )
743 // log
744 EM_log( CK_LOG_SEVERE, "requesting STOP virtual machine..." );
746 m_running = FALSE;
747 Digitalio::m_end = TRUE;
749 return TRUE;
755 //-----------------------------------------------------------------------------
756 // name: gc
757 // desc: ...
758 //-----------------------------------------------------------------------------
759 void Chuck_VM::gc( t_CKUINT amount )
766 //-----------------------------------------------------------------------------
767 // name: gc
768 // desc: ...
769 //-----------------------------------------------------------------------------
770 void Chuck_VM::gc( )
777 //-----------------------------------------------------------------------------
778 // name: queue_msg()
779 // desc: ...
780 //-----------------------------------------------------------------------------
781 t_CKBOOL Chuck_VM::queue_msg( Chuck_Msg * msg, int count )
783 assert( count == 1 );
784 m_msg_buffer->put( &msg, count );
785 return TRUE;
791 //-----------------------------------------------------------------------------
792 // name: queue_event()
793 // desc: ...
794 //-----------------------------------------------------------------------------
795 t_CKBOOL Chuck_VM::queue_event( Chuck_Event * event, int count )
797 assert( count == 1 );
798 m_event_buffer->put( &event, count );
799 return TRUE;
805 //-----------------------------------------------------------------------------
806 // name: get_reply()
807 // desc: ...
808 //-----------------------------------------------------------------------------
809 Chuck_Msg * Chuck_VM::get_reply( )
811 Chuck_Msg * msg = NULL;
812 m_reply_buffer->get( &msg, 1 );
813 return msg;
819 //-----------------------------------------------------------------------------
820 // name: process_msg()
821 // desc: ...
822 //-----------------------------------------------------------------------------
823 t_CKUINT Chuck_VM::process_msg( Chuck_Msg * msg )
825 t_CKUINT retval = 0xfffffff0;
827 if( msg->type == MSG_REPLACE )
829 Chuck_VM_Shred * out = m_shreduler->lookup( msg->param );
830 if( !out )
832 EM_error3( "[chuck](VM): error replacing shred: no shred with id %i...",
833 msg->param );
834 retval = 0;
835 goto done;
838 Chuck_VM_Shred * shred = msg->shred;
839 if( !shred )
841 shred = new Chuck_VM_Shred;
842 shred->initialize( msg->code );
843 shred->name = msg->code->name;
844 shred->base_ref = shred->mem;
845 shred->add_ref();
847 // set the current time
848 shred->start = m_shreduler->now_system;
849 // set the id
850 shred->xid = msg->param;
851 // set the now
852 shred->now = shred->wake_time = m_shreduler->now_system;
853 // set the vm
854 shred->vm_ref = this;
855 // set args
856 if( msg->args ) shred->args = *(msg->args);
857 // add it to the parent
858 if( shred->parent )
859 shred->parent->children[shred->xid] = shred;
861 // replace
862 if( m_shreduler->remove( out ) && m_shreduler->shredule( shred ) )
864 EM_error3( "[chuck](VM): replacing shred %i (%s) with %i (%s)...",
865 out->xid, mini(out->name.c_str()), shred->xid, mini(shred->name.c_str()) );
866 this->free( out, TRUE, FALSE );
867 retval = shred->xid;
869 // tracking new shred
870 CK_TRACK( Chuck_Stats::instance()->add_shred( shred ) );
872 goto done;
874 else
876 EM_error3( "[chuck](VM): shreduler ERROR replacing shred %i...",
877 out->xid );
878 shred->release();
879 retval = 0;
880 goto done;
883 else if( msg->type == MSG_REMOVE )
885 if( msg->param == 0xffffffff )
887 if( !this->m_num_shreds)
889 EM_error3( "[chuck](VM): no shreds to remove..." );
890 retval = 0;
891 goto done;
894 t_CKINT xid = m_shred_id;
895 Chuck_VM_Shred * shred = NULL;
896 while( xid >= 0 && m_shreduler->remove( shred = m_shreduler->lookup( xid ) ) == 0 )
897 xid--;
898 if( xid >= 0 )
900 EM_error3( "[chuck](VM): removing recent shred: %i (%s)...",
901 xid, mini(shred->name.c_str()) );
902 this->free( shred, TRUE );
903 retval = xid;
905 else
907 EM_error3( "[chuck](VM): no shreds removed..." );
908 retval = 0;
909 goto done;
912 else
914 Chuck_VM_Shred * shred = m_shreduler->lookup( msg->param );
915 if( !shred )
917 EM_error3( "[chuck](VM): cannot remove: no shred with id %i...",
918 msg->param );
919 retval = 0;
920 goto done;
922 if( shred != m_shreduler->m_current_shred && !m_shreduler->remove( shred ) ) // was lookup
924 EM_error3( "[chuck](VM): shreduler: cannot remove shred %i...",
925 msg->param );
926 retval = 0;
927 goto done;
929 EM_error3( "[chuck](VM): removing shred: %i (%s)...",
930 msg->param, mini(shred->name.c_str()) );
931 this->free( shred, TRUE );
932 retval = msg->param;
935 else if( msg->type == MSG_REMOVEALL )
937 t_CKUINT xid = m_shred_id;
938 EM_error3( "[chuck](VM): removing all (%i) shreds...", m_num_shreds );
939 Chuck_VM_Shred * shred = NULL;
941 while( m_num_shreds && xid > 0 )
943 if( m_shreduler->remove( shred = m_shreduler->lookup( xid ) ) )
944 this->free( shred, TRUE );
945 xid--;
948 m_shred_id = 0;
949 m_num_shreds = 0;
951 else if( msg->type == MSG_ADD )
953 t_CKUINT xid = 0;
954 Chuck_VM_Shred * shred = NULL;
955 if( msg->shred ) shred = this->spork( msg->shred );
956 else shred = this->spork( msg->code, NULL );
957 xid = shred->xid;
958 if( msg->args ) shred->args = *(msg->args);
960 const char * s = ( msg->shred ? msg->shred->name.c_str() : msg->code->name.c_str() );
961 EM_error3( "[chuck](VM): sporking incoming shred: %i (%s)...", xid, mini(s) );
962 retval = xid;
963 goto done;
965 else if( msg->type == MSG_KILL )
967 EM_error3( "[chuck](VM): KILL received...." );
968 // close file handles and clean up
969 all_detach();
970 // TODO: free more memory?
972 // log
973 EM_log( CK_LOG_INFO, "(VM): exiting..." );
974 // come again
975 exit( 1 );
977 else if( msg->type == MSG_STATUS )
979 // fill in structure
980 if( msg->user && msg->reply )
982 // cast
983 Chuck_VM_Status * status = (Chuck_VM_Status *)msg->user;
984 // get it
985 m_shreduler->status( status );
987 else
989 m_shreduler->status();
992 else if( msg->type == MSG_TIME )
994 float srate = (float)Digitalio::sampling_rate();
995 fprintf( stderr, "[chuck](VM): the values of now:\n" );
996 fprintf( stderr, " now = %.6f (samp)\n", m_shreduler->now_system );
997 fprintf( stderr, " = %.6f (second)\n", m_shreduler->now_system / srate );
998 fprintf( stderr, " = %.6f (minute)\n", m_shreduler->now_system / srate / 60.0f );
999 fprintf( stderr, " = %.6f (hour)\n", m_shreduler->now_system / srate / 60.0f / 60.0f );
1000 fprintf( stderr, " = %.6f (day)\n", m_shreduler->now_system / srate / 60.0f / 60.0f / 24.0f );
1001 fprintf( stderr, " = %.6f (week)\n", m_shreduler->now_system / srate / 60.0f / 60.0f / 24.0f / 7.0f );
1003 else if( msg->type == MSG_RESET_ID )
1005 t_CKUINT n = m_shreduler->highest();
1006 m_shred_id = n;
1007 fprintf( stderr, "[chuck](VM): reseting shred id to %d...\n", m_shred_id + 1 );
1010 done:
1012 if( msg->reply )
1014 msg->replyA = retval;
1015 m_reply_buffer->put( &msg, 1 );
1017 else
1018 SAFE_DELETE(msg);
1020 return retval;
1026 //-----------------------------------------------------------------------------
1027 // name: next_id()
1028 // desc: ...
1029 //-----------------------------------------------------------------------------
1030 t_CKUINT Chuck_VM::next_id( )
1032 return ++m_shred_id;
1038 //-----------------------------------------------------------------------------
1039 // name: shreduler()
1040 // desc: ...
1041 //-----------------------------------------------------------------------------
1042 Chuck_VM_Shreduler * Chuck_VM::shreduler( ) const
1044 return m_shreduler;
1050 //-----------------------------------------------------------------------------
1051 // name: bbq()
1052 // desc: ...
1053 //-----------------------------------------------------------------------------
1054 BBQ * Chuck_VM::bbq( ) const
1056 return m_bbq;
1062 //-----------------------------------------------------------------------------
1063 // name: srate()
1064 // desc: ...
1065 //-----------------------------------------------------------------------------
1066 t_CKUINT Chuck_VM::srate() const
1068 return (t_CKUINT)Digitalio::sampling_rate();
1074 //-----------------------------------------------------------------------------
1075 // name: fork()
1076 // desc: ...
1077 //-----------------------------------------------------------------------------
1078 Chuck_VM_Shred * Chuck_VM::fork( Chuck_VM_Code * code )
1080 return NULL;
1086 //-----------------------------------------------------------------------------
1087 // name: spork()
1088 // desc: ...
1089 //-----------------------------------------------------------------------------
1090 Chuck_VM_Shred * Chuck_VM::spork( Chuck_VM_Code * code, Chuck_VM_Shred * parent )
1092 // allocate a new shred
1093 Chuck_VM_Shred * shred = new Chuck_VM_Shred;
1094 // initialize the shred (default stack size)
1095 shred->initialize( code );
1096 // set the name
1097 shred->name = code->name;
1098 // set the parent
1099 shred->parent = parent;
1100 // set the base ref for global
1101 if( parent ) shred->base_ref = shred->parent->base_ref;
1102 else shred->base_ref = shred->mem;
1103 // spork it
1104 this->spork( shred );
1106 // track new shred
1107 CK_TRACK( Chuck_Stats::instance()->add_shred( shred ) );
1109 return shred;
1115 //-----------------------------------------------------------------------------
1116 // name: spork()
1117 // desc: ...
1118 //-----------------------------------------------------------------------------
1119 Chuck_VM_Shred * Chuck_VM::spork( Chuck_VM_Shred * shred )
1121 // set the current time
1122 shred->start = m_shreduler->now_system;
1123 // set the now
1124 shred->now = shred->wake_time = m_shreduler->now_system;
1125 // set the id
1126 shred->xid = next_id();
1127 // set the vm
1128 shred->vm_ref = this;
1129 // add ref
1130 shred->add_ref();
1131 // add it to the parent
1132 if( shred->parent )
1133 shred->parent->children[shred->xid] = shred;
1134 // shredule it
1135 m_shreduler->shredule( shred );
1136 // count
1137 m_num_shreds++;
1139 return shred;
1145 //-----------------------------------------------------------------------------
1146 // name: free()
1147 // desc: ...
1148 //-----------------------------------------------------------------------------
1149 t_CKBOOL Chuck_VM::free( Chuck_VM_Shred * shred, t_CKBOOL cascade, t_CKBOOL dec )
1151 assert( cascade );
1153 // log
1154 EM_log( CK_LOG_FINER, "freeing shred (id==%d | ptr==%p)", shred->xid,
1155 (t_CKUINT)shred );
1157 // abort on the double free
1158 // TODO: can a shred be dumped, then resporked? from code?
1159 if( shred->is_dumped ) return FALSE;
1161 // mark this done
1162 shred->is_done = TRUE;
1164 // free the children
1165 t_CKINT size = shred->children.size();
1166 if( size )
1168 vector<Chuck_VM_Shred *> list; list.resize( size );
1169 map<t_CKUINT, Chuck_VM_Shred *>::iterator iter; t_CKINT i = 0;
1170 for( iter = shred->children.begin(); iter != shred->children.end(); iter++ )
1171 list[i++] = (*iter).second;
1172 for( i = 0; i < size; i++ )
1173 this->free( list[i], cascade );
1176 // make sure it's done
1177 assert( shred->children.size() == 0 );
1179 // tell parent
1180 if( shred->parent )
1181 shred->parent->children.erase( shred->xid );
1183 // track remove shred
1184 CK_TRACK( Chuck_Stats::instance()->remove_shred( shred ) );
1186 // free!
1187 m_shreduler->remove( shred );
1188 // TODO: remove shred from event, with synchronization (still necessary with dump?)
1189 // if( shred->event ) shred->event->remove( shred );
1190 // OLD: shred->release();
1191 this->dump( shred );
1192 shred = NULL;
1193 if( dec ) m_num_shreds--;
1194 if( !m_num_shreds ) m_shred_id = 0;
1196 return TRUE;
1202 //-----------------------------------------------------------------------------
1203 // name: abort_current_shred()
1204 // desc: ...
1205 //-----------------------------------------------------------------------------
1206 t_CKBOOL Chuck_VM::abort_current_shred( )
1208 // for threading
1209 Chuck_VM_Shred * shred = m_shreduler->m_current_shred;
1211 // if there
1212 if( shred )
1214 // log
1215 EM_log( CK_LOG_SEVERE, "trying to abort current shred (id: %d)", shred->xid );
1216 // flag it
1217 shred->is_abort = TRUE;
1219 else
1221 // log
1222 EM_log( CK_LOG_SEVERE, "cannot abort shred: nothing currently running!" );
1225 return shred != NULL;
1231 //-----------------------------------------------------------------------------
1232 // name: dump()
1233 // desc: ...
1234 //-----------------------------------------------------------------------------
1235 void Chuck_VM::dump( Chuck_VM_Shred * shred )
1237 // log
1238 EM_log( CK_LOG_FINER, "dumping shred (id==%d | ptr==%p)", shred->xid,
1239 (t_CKUINT)shred );
1240 // add
1241 m_shred_dump.push_back( shred );
1242 // stop
1243 shred->is_running = FALSE;
1244 shred->is_done = TRUE;
1245 shred->is_dumped = TRUE;
1246 // TODO: cool?
1247 shred->xid = 0;
1248 // inc
1249 m_num_dumped_shreds++;
1255 //-----------------------------------------------------------------------------
1256 // name: release_dump()
1257 // desc: ...
1258 //-----------------------------------------------------------------------------
1259 void Chuck_VM::release_dump( )
1261 // log
1262 EM_log( CK_LOG_FINER, "releasing dumped shreds..." );
1264 // iterate through dump
1265 for( t_CKUINT i = 0; i < m_shred_dump.size(); i++ )
1266 SAFE_RELEASE( m_shred_dump[i] );
1268 // clear the dump
1269 m_shred_dump.clear();
1270 // reset
1271 m_num_dumped_shreds = 0;
1277 //-----------------------------------------------------------------------------
1278 // name: Chuck_VM_Stack()
1279 // desc: ...
1280 //-----------------------------------------------------------------------------
1281 Chuck_VM_Stack::Chuck_VM_Stack()
1283 stack = sp = sp_max = NULL;
1284 prev = next = NULL;
1285 m_is_init = FALSE;
1291 //-----------------------------------------------------------------------------
1292 // name: ~Chuck_VM_Stack()
1293 // desc: ...
1294 //-----------------------------------------------------------------------------
1295 Chuck_VM_Stack::~Chuck_VM_Stack()
1297 this->shutdown();
1303 //-----------------------------------------------------------------------------
1304 // name: Chuck_VM_Code()
1305 // desc: ...
1306 //-----------------------------------------------------------------------------
1307 Chuck_VM_Code::Chuck_VM_Code()
1309 instr = NULL;
1310 num_instr = 0;
1311 stack_depth = 0;
1312 need_this = FALSE;
1313 native_func = 0;
1314 native_func_type = NATIVE_UNKNOWN;
1320 //-----------------------------------------------------------------------------
1321 // name: ~Chuck_VM_Code()
1322 // desc: ...
1323 //-----------------------------------------------------------------------------
1324 Chuck_VM_Code::~Chuck_VM_Code()
1326 // free instructions
1327 if( instr )
1329 // loop over array
1330 for( t_CKUINT i = 0; i < num_instr; i++ )
1331 delete instr[i];
1333 // free the array
1334 SAFE_DELETE_ARRAY( instr );
1337 num_instr = 0;
1343 // offset in bytes at the beginning of a stack for initializing data
1344 #define VM_STACK_OFFSET 16
1345 // 1/factor of stack is left blank, to give room to detect overflow
1346 #define VM_STACK_PADDING_FACTOR 16
1347 //-----------------------------------------------------------------------------
1348 // name: initialize()
1349 // desc: ...
1350 //-----------------------------------------------------------------------------
1351 t_CKBOOL Chuck_VM_Stack::initialize( t_CKUINT size )
1353 if( m_is_init )
1354 return FALSE;
1356 // make room for header
1357 size += VM_STACK_OFFSET;
1358 // allocate stack
1359 stack = new t_CKBYTE[size];
1360 if( !stack ) goto out_of_memory;
1362 // zero
1363 memset( stack, 0, size );
1365 // advance stack after the header
1366 stack += VM_STACK_OFFSET;
1367 // set the sp
1368 sp = stack;
1369 // upper limit (padding factor)
1370 sp_max = sp + size - (size / VM_STACK_PADDING_FACTOR);
1372 // set flag and return
1373 return m_is_init = TRUE;
1375 out_of_memory:
1377 // we have a problem
1378 fprintf( stderr,
1379 "[chuck](VM): OutOfMemory: while allocating stack '%s'\n" );
1381 // return FALSE
1382 return FALSE;
1388 //-----------------------------------------------------------------------------
1389 // name: shutdown()
1390 // desc: ...
1391 //-----------------------------------------------------------------------------
1392 t_CKBOOL Chuck_VM_Stack::shutdown()
1394 if( !m_is_init )
1395 return FALSE;
1397 // free the stack
1398 stack -= VM_STACK_OFFSET;
1399 SAFE_DELETE_ARRAY( stack );
1400 sp = sp_max = NULL;
1402 // set the flag to false
1403 m_is_init = FALSE;
1405 return TRUE;
1411 //-----------------------------------------------------------------------------
1412 // name: Chuck_VM_Shred()
1413 // desc: ...
1414 //-----------------------------------------------------------------------------
1415 Chuck_VM_Shred::Chuck_VM_Shred()
1417 mem = new Chuck_VM_Stack;
1418 reg = new Chuck_VM_Stack;
1419 code = NULL;
1420 next = prev = NULL;
1421 instr = NULL;
1422 parent = NULL;
1423 // obj_array = NULL;
1424 // obj_array_size = 0;
1425 base_ref = NULL;
1426 vm_ref = NULL;
1427 event = NULL;
1428 xid = 0;
1430 // set
1431 CK_TRACK( stat = NULL );
1437 //-----------------------------------------------------------------------------
1438 // name: ~Chuck_VM_Shred()
1439 // desc: ...
1440 //-----------------------------------------------------------------------------
1441 Chuck_VM_Shred::~Chuck_VM_Shred()
1443 this->shutdown();
1449 //-----------------------------------------------------------------------------
1450 // name: initialize()
1451 // desc: ...
1452 //-----------------------------------------------------------------------------
1453 t_CKBOOL Chuck_VM_Shred::initialize( Chuck_VM_Code * c,
1454 t_CKUINT mem_stack_size,
1455 t_CKUINT reg_stack_size )
1457 // allocate mem and reg
1458 if( !mem->initialize( mem_stack_size ) ) return FALSE;
1459 if( !reg->initialize( reg_stack_size ) ) return FALSE;
1461 // program counter
1462 pc = 0;
1463 next_pc = 1;
1464 // code pointer
1465 code_orig = code = c;
1466 // add reference
1467 code_orig->add_ref();
1468 // shred in dump (all done)
1469 is_dumped = FALSE;
1470 // shred done
1471 is_done = FALSE;
1472 // shred running
1473 is_running = FALSE;
1474 // shred abort
1475 is_abort = FALSE;
1476 // set the instr
1477 instr = c->instr;
1478 // zero out the id
1479 xid = 0;
1481 // initialize
1482 initialize_object( this, &t_shred );
1484 return TRUE;
1490 //-----------------------------------------------------------------------------
1491 // name: shutdown()
1492 // desc: ...
1493 //-----------------------------------------------------------------------------
1494 t_CKBOOL Chuck_VM_Shred::shutdown()
1496 // get iterator to our map
1497 map<Chuck_UGen *, Chuck_UGen *>::iterator iter = m_ugen_map.begin();
1498 while( iter != m_ugen_map.end() )
1500 (*iter).first->disconnect( TRUE );
1501 iter++;
1503 m_ugen_map.clear();
1505 SAFE_DELETE( mem );
1506 SAFE_DELETE( reg );
1507 base_ref = NULL;
1509 // delete temp pointer space
1510 // SAFE_DELETE_ARRAY( obj_array );
1511 // obj_array_size = 0;
1513 // TODO: is this right?
1514 code_orig->release();
1515 code_orig = code = NULL;
1516 // what to do with next and prev?
1518 return TRUE;
1524 //-----------------------------------------------------------------------------
1525 // name: add()
1526 // desc: ...
1527 //-----------------------------------------------------------------------------
1528 t_CKBOOL Chuck_VM_Shred::add( Chuck_UGen * ugen )
1530 if( m_ugen_map[ugen] )
1531 return FALSE;
1533 m_ugen_map[ugen] = ugen;
1534 return TRUE;
1540 //-----------------------------------------------------------------------------
1541 // name: remove()
1542 // desc: ...
1543 //-----------------------------------------------------------------------------
1544 t_CKBOOL Chuck_VM_Shred::remove( Chuck_UGen * ugen )
1546 if( !m_ugen_map[ugen] )
1547 return FALSE;
1549 // remove it
1550 m_ugen_map.erase( ugen );
1551 return TRUE;
1557 //-----------------------------------------------------------------------------
1558 // name: run()
1559 // desc: ...
1560 //-----------------------------------------------------------------------------
1561 t_CKBOOL Chuck_VM_Shred::run( Chuck_VM * vm )
1563 // get the code
1564 instr = code->instr;
1565 is_running = TRUE;
1566 t_CKBOOL * vm_running = &vm->m_running;
1568 // go!
1569 while( is_running && *vm_running && !is_abort )
1571 // execute the instruction
1572 instr[pc]->execute( vm, this );
1574 // set to next_pc;
1575 pc = next_pc;
1576 next_pc++;
1578 // track number of cycles
1579 CK_TRACK( this->stat->cycles++ );
1582 // check abort
1583 if( is_abort )
1585 // log
1586 EM_log( CK_LOG_SYSTEM, "aborting shred (id: %d)", this->xid );
1587 // done
1588 is_done = TRUE;
1591 // is the shred finished
1592 return !is_done;
1598 //-----------------------------------------------------------------------------
1599 // name: Chuck_VM_Shreduler()
1600 // desc: ...
1601 //-----------------------------------------------------------------------------
1602 Chuck_VM_Shreduler::Chuck_VM_Shreduler()
1604 now_system = 0;
1605 rt_audio = FALSE;
1606 bbq = NULL;
1607 shred_list = NULL;
1608 m_current_shred = NULL;
1609 m_dac = NULL;
1610 m_adc = NULL;
1611 m_bunghole = NULL;
1612 m_num_dac_channels = 0;
1613 m_num_adc_channels = 0;
1619 //-----------------------------------------------------------------------------
1620 // name: ~Chuck_VM_Shreduler()
1621 // desc: ...
1622 //-----------------------------------------------------------------------------
1623 Chuck_VM_Shreduler::~Chuck_VM_Shreduler()
1625 this->shutdown();
1631 //-----------------------------------------------------------------------------
1632 // name: initialize()
1633 // desc: ...
1634 //-----------------------------------------------------------------------------
1635 t_CKBOOL Chuck_VM_Shreduler::initialize()
1637 return TRUE;
1643 //-----------------------------------------------------------------------------
1644 // name: shutdown()
1645 // desc: ...
1646 //-----------------------------------------------------------------------------
1647 t_CKBOOL Chuck_VM_Shreduler::shutdown()
1649 return TRUE;
1655 //-----------------------------------------------------------------------------
1656 // name: add_blocked()
1657 // desc: add shred to the shreduler's blocked list
1658 //-----------------------------------------------------------------------------
1659 t_CKBOOL Chuck_VM_Shreduler::add_blocked( Chuck_VM_Shred * shred )
1661 // add shred to map, using pointer
1662 blocked[shred] = shred;
1664 return TRUE;
1670 //-----------------------------------------------------------------------------
1671 // name: remove_blocked()
1672 // desc: remove shred from the shreduler's blocked list
1673 //-----------------------------------------------------------------------------
1674 t_CKBOOL Chuck_VM_Shreduler::remove_blocked( Chuck_VM_Shred * shred )
1676 // remove from hash
1677 std::map<Chuck_VM_Shred *, Chuck_VM_Shred *>::iterator iter;
1678 iter = blocked.find( shred );
1679 blocked.erase( iter );
1681 // remove from event
1682 if( shred->event != NULL ) shred->event->remove( shred );
1684 return TRUE;
1690 //-----------------------------------------------------------------------------
1691 // name: shredule()
1692 // desc: ...
1693 //-----------------------------------------------------------------------------
1694 t_CKBOOL Chuck_VM_Shreduler::shredule( Chuck_VM_Shred * shred )
1696 return this->shredule( shred, now_system );
1702 //-----------------------------------------------------------------------------
1703 // name: shredule()
1704 // desc: ...
1705 //-----------------------------------------------------------------------------
1706 t_CKBOOL Chuck_VM_Shreduler::shredule( Chuck_VM_Shred * shred,
1707 t_CKTIME wake_time )
1709 // sanity check
1710 if( shred->prev || shred->next )
1712 // something is really wrong here - no shred can be
1713 // shreduled more than once
1714 EM_error3( "[chuck](VM): internal sanity check failed in shredule()" );
1715 EM_error3( "[chuck](VM): (shred shreduled while shreduled)" );
1717 return FALSE;
1720 // sanity check
1721 if( wake_time < (this->now_system - .5) )
1723 // trying to enqueue on a time that is less than now
1724 EM_error3( "[chuck](VM): internal sanity check failed in shredule()" );
1725 EM_error3( "[chuck](VM): (wake time is past) - %f : %f", wake_time, this->now_system );
1727 return FALSE;
1730 shred->wake_time = wake_time;
1732 // list empty
1733 if( !shred_list )
1734 shred_list = shred;
1735 else
1737 // pointers to the shred queue
1738 Chuck_VM_Shred * curr = shred_list;
1739 Chuck_VM_Shred * prev = NULL;
1741 while( curr )
1743 // found the place to insert
1744 if( curr->wake_time > wake_time )
1745 break;
1747 prev = curr;
1748 curr = curr->next;
1751 if( !prev )
1753 shred->next = shred_list;
1754 if( shred_list ) shred_list->prev = shred;
1755 shred_list = shred;
1757 else
1759 // insert the shred in sorted order
1760 shred->next = prev->next;
1761 shred->prev = prev;
1762 if( prev->next ) prev->next->prev = shred;
1763 prev->next = shred;
1767 return TRUE;
1772 //-----------------------------------------------------------------------------
1773 // name: advance2()
1774 // desc: ...
1775 //-----------------------------------------------------------------------------
1776 void Chuck_VM_Shreduler::advance2( )
1778 // advance system 'now'
1779 this->now_system += 1;
1781 // tick the dac
1782 SAMPLE l, r;
1783 BBQ * audio = this->bbq;
1785 // tick in
1786 if( rt_audio )
1788 audio->digi_in()->tick_in( &l, &r );
1789 m_adc->m_multi_chan[0]->m_current = l * m_adc->m_multi_chan[0]->m_gain;
1790 m_adc->m_multi_chan[1]->m_current = r * m_adc->m_multi_chan[1]->m_gain;
1791 m_adc->m_current = .5f * ( l + r );
1792 // time it
1793 m_adc->m_multi_chan[0]->m_time = this->now_system;
1794 m_adc->m_multi_chan[1]->m_time = this->now_system;
1795 m_adc->m_time = this->now_system;
1798 // dac
1799 m_dac->system_tick( this->now_system );
1800 l = m_dac->m_multi_chan[0]->m_current;
1801 r = m_dac->m_multi_chan[1]->m_current;
1802 // remove: 1.2.1.2
1803 // l *= .5f; r *= .5f;
1805 // suck samples
1806 m_bunghole->system_tick( this->now_system );
1808 // tick
1809 audio->digi_out()->tick_out( l, r );
1815 //-----------------------------------------------------------------------------
1816 // name: advance()
1817 // desc: ...
1818 //-----------------------------------------------------------------------------
1819 void Chuck_VM_Shreduler::advance( )
1821 // advance system 'now'
1822 this->now_system += 1;
1824 // tick the dac
1825 SAMPLE frame[128];
1826 SAMPLE sum = 0.0f;
1827 BBQ * audio = this->bbq;
1828 t_CKUINT i;
1830 // tick in
1831 if( rt_audio )
1833 audio->digi_in()->tick_in( frame, m_num_adc_channels );
1835 // loop over channels
1836 for( i = 0; i < m_num_adc_channels; i++ )
1838 m_adc->m_multi_chan[i]->m_current = frame[i] * m_adc->m_multi_chan[i]->m_gain * m_adc->m_gain;
1839 m_adc->m_multi_chan[i]->m_time = this->now_system;
1840 sum += m_adc->m_multi_chan[i]->m_current;
1842 m_adc->m_last = m_adc->m_current = sum / m_num_adc_channels;
1843 m_adc->m_time = this->now_system;
1846 // dac
1847 m_dac->system_tick( this->now_system );
1848 for( i = 0; i < m_num_dac_channels; i++ )
1849 frame[i] = m_dac->m_multi_chan[i]->m_current * .5f;
1851 // suck samples
1852 m_bunghole->system_tick( this->now_system );
1854 // tick
1855 audio->digi_out()->tick_out( frame, m_num_dac_channels );
1860 //-----------------------------------------------------------------------------
1861 // name: get()
1862 // desc: ...
1863 //-----------------------------------------------------------------------------
1864 Chuck_VM_Shred * Chuck_VM_Shreduler::get( )
1866 Chuck_VM_Shred * shred = shred_list;
1868 // list empty
1869 if( !shred )
1870 return NULL;
1872 // TODO: should this be <=?
1873 if( shred->wake_time <= ( this->now_system + .5 ) )
1875 // if( shred->wake_time < this->now_system )
1876 // assert( false );
1878 shred_list = shred->next;
1879 shred->next = NULL;
1880 shred->prev = NULL;
1882 if( shred_list )
1883 shred_list->prev = NULL;
1885 return shred;
1888 return NULL;
1894 //-----------------------------------------------------------------------------
1895 // name: highest()
1896 // desc: ...
1897 //-----------------------------------------------------------------------------
1898 t_CKUINT Chuck_VM_Shreduler::highest( )
1900 Chuck_VM_Shred * shred = shred_list;
1901 t_CKUINT n = 0;
1903 while( shred )
1905 if( shred->xid > n ) n = shred->xid;
1906 shred = shred->next;
1909 std::map<Chuck_VM_Shred *, Chuck_VM_Shred *>::iterator iter;
1910 for( iter = blocked.begin(); iter != blocked.end(); iter++ )
1912 shred = (*iter).second;
1913 if( shred->xid > n ) n = shred->xid;
1916 return n;
1922 //-----------------------------------------------------------------------------
1923 // name: replace()
1924 // desc: ...
1925 //-----------------------------------------------------------------------------
1926 t_CKBOOL Chuck_VM_Shreduler::replace( Chuck_VM_Shred * out, Chuck_VM_Shred * in )
1928 assert( FALSE );
1930 // sanity check
1931 if( !out || !in )
1932 return FALSE;
1934 if( !out->prev )
1935 shred_list = in;
1936 else
1937 out->prev->next = in;
1939 if( out->next )
1940 out->next->prev = in;
1942 in->next = out->next;
1943 in->prev = out->prev;
1945 out->next = out->prev = NULL;
1947 in->wake_time = out->wake_time;
1948 in->start = in->wake_time;
1950 return TRUE;
1956 //-----------------------------------------------------------------------------
1957 // name: remove()
1958 // desc: ...
1959 //-----------------------------------------------------------------------------
1960 t_CKBOOL Chuck_VM_Shreduler::remove( Chuck_VM_Shred * out )
1962 if( !out ) return FALSE;
1964 // if blocked
1965 if( out->event != NULL )
1967 return remove_blocked( out );
1970 // sanity check
1971 if( !out->prev && !out->next && out != shred_list )
1972 return FALSE;
1974 if( !out->prev )
1975 shred_list = out->next;
1976 else
1977 out->prev->next = out->next;
1979 if( out->next )
1980 out->next->prev = out->prev;
1982 out->next = out->prev = NULL;
1984 return TRUE;
1990 //-----------------------------------------------------------------------------
1991 // name: get()
1992 // desc: ...
1993 //-----------------------------------------------------------------------------
1994 Chuck_VM_Shred * Chuck_VM_Shreduler::lookup( t_CKUINT xid )
1996 Chuck_VM_Shred * shred = shred_list;
1998 // current shred?
1999 if( m_current_shred != NULL && m_current_shred->xid == xid )
2000 return m_current_shred;
2002 // look for in shreduled list
2003 while( shred )
2005 if( shred->xid == xid )
2006 return shred;
2008 shred = shred->next;
2011 // blocked?
2012 std::map<Chuck_VM_Shred *, Chuck_VM_Shred *>::iterator iter;
2013 for( iter = blocked.begin(); iter != blocked.end(); iter++ )
2015 shred = (*iter).second;
2016 if( shred->xid == xid )
2017 return shred;
2020 return NULL;
2026 //-----------------------------------------------------------------------------
2027 // name: SortByID()
2028 // desc: ...
2029 //-----------------------------------------------------------------------------
2030 struct SortByID
2032 bool operator() ( const Chuck_VM_Shred * lhs, const Chuck_VM_Shred * rhs )
2033 { return lhs->xid < rhs->xid; }
2039 //-----------------------------------------------------------------------------
2040 // name: status()
2041 // desc: ...
2042 //-----------------------------------------------------------------------------
2043 void Chuck_VM_Shreduler::status( Chuck_VM_Status * status )
2045 Chuck_VM_Shred * shred = shred_list;
2046 Chuck_VM_Shred * temp = NULL;
2048 t_CKUINT srate = Digitalio::sampling_rate();
2049 t_CKUINT s = (t_CKUINT)now_system;
2050 t_CKUINT h = s/(srate*3600);
2051 s = s - (h*(srate*3600));
2052 t_CKUINT m = s / (srate*60);
2053 s = s - (m*(srate*60));
2054 t_CKUINT sec = s / srate;
2055 s = s - (sec*(srate));
2056 // float millisecond = s / (float)(srate) * 1000.0f;
2058 status->srate = srate;
2059 status->now_system = now_system;
2060 status->t_second = sec;
2061 status->t_minute = m;
2062 status->t_hour = h;
2064 // get list of shreds
2065 vector<Chuck_VM_Shred *> list;
2066 while( shred )
2068 list.push_back( shred );
2069 shred = shred->next;
2072 // get blocked
2073 std::map<Chuck_VM_Shred *, Chuck_VM_Shred *>::iterator iter;
2074 for( iter = blocked.begin(); iter != blocked.end(); iter++ )
2076 shred = (*iter).second;
2077 list.push_back( shred );
2080 // get current shred
2081 if( temp = m_current_shred )
2082 list.push_back( temp );
2084 // sort the list
2085 SortByID byid;
2086 std::sort( list.begin(), list.end(), byid );
2088 // print status
2089 status->clear();
2090 for( t_CKUINT i = 0; i < list.size(); i++ )
2092 shred = list[i];
2093 status->list.push_back( new Chuck_VM_Shred_Status(
2094 shred->xid, shred->name, shred->start, shred->event != NULL ) );
2101 //-----------------------------------------------------------------------------
2102 // name: status()
2103 // desc: ...
2104 //-----------------------------------------------------------------------------
2105 void Chuck_VM_Shreduler::status( )
2107 Chuck_VM_Shred_Status * shred = NULL;
2109 this->status( &m_status );
2110 t_CKUINT h = m_status.t_hour;
2111 t_CKUINT m = m_status.t_minute;
2112 t_CKUINT sec = m_status.t_second;
2113 fprintf( stdout, "[chuck](VM): status (now == %ldh%ldm%lds, %.1f samps) ...\n",
2114 h, m, sec, m_status.now_system );
2116 // print status
2117 for( t_CKUINT i = 0; i < m_status.list.size(); i++ )
2119 shred = m_status.list[i];
2120 fprintf( stdout,
2121 " [shred id]: %ld [source]: %s [spork time]: %.2fs ago%s\n",
2122 shred->xid, mini( shred->name.c_str() ),
2123 (m_status.now_system - shred->start) / m_status.srate,
2124 shred->has_event ? " (blocked)" : "" );
2131 //-----------------------------------------------------------------------------
2132 // name: Chuck_VM_Status()
2133 // desc: ...
2134 //-----------------------------------------------------------------------------
2135 Chuck_VM_Status::Chuck_VM_Status()
2137 srate = 0;
2138 now_system = 0;
2139 t_second = t_minute = t_hour = 0;
2145 //-----------------------------------------------------------------------------
2146 // name: ~Chuck_VM_Status()
2147 // desc: ...
2148 //-----------------------------------------------------------------------------
2149 Chuck_VM_Status::~Chuck_VM_Status()
2151 this->clear();
2157 //-----------------------------------------------------------------------------
2158 // name: clear()
2159 // desc: ...
2160 //-----------------------------------------------------------------------------
2161 void Chuck_VM_Status::clear()
2163 for( t_CKUINT i = 0; i < list.size(); i++ )
2165 SAFE_DELETE( list[i] );
2168 list.clear();