*** empty log message ***
[chuck-blob.git] / v2 / chuck_vm.cpp
blob93116c01c1000fe708a0a1f7dcb924b8f728008c
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 #include <algorithm>
44 using namespace std;
46 #if defined(__PLATFORM_WIN32__)
47 #include <windows.h>
48 #else
49 #include <unistd.h>
50 #include <pthread.h>
51 #endif
56 //-----------------------------------------------------------------------------
57 // name: struct Chuck_VM_Frame
58 // desc: func frame
59 //-----------------------------------------------------------------------------
60 struct Chuck_VM_Frame
62 public:
63 t_CKUINT size;
65 public:
66 Chuck_VM_Frame() { size = 0; }
67 ~Chuck_VM_Frame() { }
73 //-----------------------------------------------------------------------------
74 // name: struct Chuck_VM_Func
75 // desc: vm function
76 //-----------------------------------------------------------------------------
77 struct Chuck_VM_Func
79 public:
80 Chuck_VM_Code * code;
81 Chuck_VM_Frame * frame;
82 t_CKUINT index;
84 public:
85 Chuck_VM_Func() { code = NULL; frame = NULL; index = 0; }
86 ~Chuck_VM_Func() { }
92 //-----------------------------------------------------------------------------
93 // name: struct Chuck_VM_FTable
94 // desc: function table
95 //-----------------------------------------------------------------------------
96 struct Chuck_VM_FTable
98 public:
99 Chuck_VM_FTable();
100 ~Chuck_VM_FTable();
102 public:
103 Chuck_VM_Func * get_func( t_CKUINT index );
104 t_CKUINT append( Chuck_VM_Func * f );
105 t_CKBOOL remove( t_CKUINT index );
107 public:
108 vector<Chuck_VM_Func *> func_table;
114 //-----------------------------------------------------------------------------
115 // name: Chuck_VM()
116 // desc: ...
117 //-----------------------------------------------------------------------------
118 Chuck_VM::Chuck_VM()
120 m_shreds = NULL;
121 m_num_shreds = 0;
122 m_shreduler = NULL;
123 m_num_dumped_shreds = 0;
124 m_msg_buffer = NULL;
125 m_reply_buffer = NULL;
126 m_event_buffer = NULL;
127 m_shred_id = 0;
128 m_halt = TRUE;
129 m_audio = FALSE;
130 m_block = TRUE;
131 m_running = FALSE;
133 m_audio_started = FALSE;
134 m_dac = NULL;
135 m_adc = NULL;
136 m_bunghole = NULL;
137 m_num_dac_channels = 0;
138 m_num_adc_channels = 0;
139 m_init = FALSE;
145 //-----------------------------------------------------------------------------
146 // name: ~Chuck_VM()
147 // desc: ...
148 //-----------------------------------------------------------------------------
149 Chuck_VM::~Chuck_VM()
151 if( m_init ) shutdown();
155 // dac tick
156 //UGEN_TICK __dac_tick( Chuck_Object * SELF, SAMPLE in, SAMPLE * out )
157 //{ *out = in; return TRUE; }
158 //UGEN_TICK __bunghole_tick( Chuck_Object * SELF, SAMPLE in, SAMPLE * out )
159 //{ *out = 0.0f; return TRUE; }
162 // static
163 #ifdef __MACOSX_CORE__
164 t_CKINT Chuck_VM::our_priority = 85;
165 #else
166 t_CKINT Chuck_VM::our_priority = 0x7fffffff;
167 #endif
170 #if !defined(__PLATFORM_WIN32__) || defined(__WINDOWS_PTHREAD__)
171 //-----------------------------------------------------------------------------
172 // name: set_priority()
173 // desc: ...
174 //-----------------------------------------------------------------------------
175 t_CKBOOL Chuck_VM::set_priority( t_CKINT priority, Chuck_VM * vm )
177 struct sched_param param;
178 pthread_t tid = pthread_self();
179 int policy;
181 // log
182 EM_log( CK_LOG_INFO, "setting thread priority to: %ld...", priority );
184 // get for thread
185 if( pthread_getschedparam( tid, &policy, &param) )
187 if( vm )
188 vm->m_last_error = "could not get current scheduling parameters";
189 return FALSE;
192 // priority
193 param.sched_priority = priority;
194 // policy
195 policy = SCHED_RR;
196 // set for thread
197 if( pthread_setschedparam( tid, policy, &param ) )
199 if( vm )
200 vm->m_last_error = "could not set new scheduling parameters";
201 return FALSE;
204 return TRUE;
206 #else
207 //-----------------------------------------------------------------------------
208 // name: set_priority()
209 // desc: ...
210 //-----------------------------------------------------------------------------
211 t_CKBOOL Chuck_VM::set_priority( t_CKINT priority, Chuck_VM * vm )
213 // if priority is 0 then done
214 if( !priority ) return TRUE;
216 // set the priority class of the process
217 // if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) )
218 // return FALSE;
220 // log
221 EM_log( CK_LOG_INFO, "setting thread priority to: %ld...", priority );
223 // set the priority the thread
224 // if( !SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL ) )
225 if( !SetThreadPriority( GetCurrentThread(), priority ) )
227 if( vm )
228 vm->m_last_error = "could not set new scheduling parameters";
229 return FALSE;
232 return TRUE;
234 #endif
239 //-----------------------------------------------------------------------------
240 // name: initialize()
241 // desc: ...
242 //-----------------------------------------------------------------------------
243 t_CKBOOL Chuck_VM::initialize( t_CKBOOL enable_audio, t_CKBOOL halt, t_CKUINT srate,
244 t_CKUINT buffer_size, t_CKUINT num_buffers,
245 t_CKUINT dac, t_CKUINT adc, t_CKUINT dac_chan,
246 t_CKUINT adc_chan, t_CKBOOL block )
248 if( m_init )
250 m_last_error = "VM already initialized!";
251 return FALSE;
254 // boost thread priority
255 // if( priority != 0x7fffffff && !set_priority( priority, this ) )
256 // return FALSE;
258 // log
259 EM_log( CK_LOG_SYSTEM, "initializing virtual machine..." );
260 EM_pushlog(); // push stack
261 EM_log( CK_LOG_SYSTEM, "behavior: %s", halt ? "HALT" : "LOOP" );
263 // lockdown
264 Chuck_VM_Object::lock_all();
266 // allocate bbq
267 m_bbq = new BBQ;
268 m_halt = halt;
269 m_audio = enable_audio;
270 m_block = block;
272 // log
273 EM_log( CK_LOG_SYSTEM, "allocating shreduler..." );
274 // allocate shreduler
275 m_shreduler = new Chuck_VM_Shreduler;
276 m_shreduler->bbq = m_bbq;
277 m_shreduler->rt_audio = enable_audio;
279 // log
280 EM_log( CK_LOG_SYSTEM, "allocating messaging buffers..." );
281 // allocate msg buffer
282 m_msg_buffer = new CBufferSimple;
283 m_msg_buffer->initialize( 1024, sizeof(Chuck_Msg *) );
284 //m_msg_buffer->join(); // this should return 0
285 m_reply_buffer = new CBufferSimple;
286 m_reply_buffer->initialize( 1024, sizeof(Chuck_Msg *) );
287 //m_reply_buffer->join(); // this should return 0 too
288 m_event_buffer = new CBufferSimple;
289 m_event_buffer->initialize( 1024, sizeof(Chuck_Event *) );
290 //m_event_buffer->join(); // this should also return 0
292 // log
293 EM_log( CK_LOG_SYSTEM, "real-time audio: %s", enable_audio ? "YES" : "NO" );
294 EM_log( CK_LOG_SYSTEM, "mode: %s", block ? "BLOCKING" : "CALLBACK" );
295 EM_log( CK_LOG_SYSTEM, "sample rate: %ld", srate );
296 EM_log( CK_LOG_SYSTEM, "buffer size: %ld", buffer_size );
297 if( enable_audio )
299 EM_log( CK_LOG_SYSTEM, "num buffers: %ld", num_buffers );
300 EM_log( CK_LOG_SYSTEM, "devices adc: %ld dac: %d (default 0)", adc, dac );
302 EM_log( CK_LOG_SYSTEM, "channels in: %ld out: %ld", adc_chan, dac_chan );
303 m_num_adc_channels = adc_chan;
304 m_num_dac_channels = dac_chan;
306 // at least set the sample rate and buffer size
307 m_bbq->set_srate( srate );
308 m_bbq->set_bufsize( buffer_size );
309 m_bbq->set_numbufs( num_buffers );
310 m_bbq->set_inouts( adc, dac );
311 m_bbq->set_chans( adc_chan, dac_chan );
313 // pop log
314 EM_poplog();
316 // TODO: clean up all the dynamic objects here on failure
317 // and in the shutdown function!
319 return m_init = TRUE;
325 //-----------------------------------------------------------------------------
326 // name: initialize_synthesis()
327 // desc: requires type system
328 //-----------------------------------------------------------------------------
329 t_CKBOOL Chuck_VM::initialize_synthesis( )
331 if( !m_init )
333 m_last_error = "VM initialize_synthesis() called on raw VM";
334 return FALSE;
337 if( !g_t_dac || !g_t_adc )
339 m_last_error = "VM initialize_synthesis() called before type system initialized";
340 return FALSE;
343 if( m_dac != NULL )
345 m_last_error = "VM synthesis already initialized";
346 return FALSE;
349 // log
350 EM_log( CK_LOG_SYSTEM, "initializing synthesis engine..." );
351 // push indent
352 EM_pushlog();
354 // log
355 EM_log( CK_LOG_SEVERE, "initializing 'dac'..." );
356 // allocate dac and adc
357 g_t_dac->ugen_info->num_outs =
358 g_t_dac->ugen_info->num_ins = m_num_dac_channels;
359 m_dac = (Chuck_UGen *)instantiate_and_initialize_object( g_t_dac, NULL );
360 object_ctor( m_dac, NULL, NULL ); // TODO: this can't be the place to do this
361 stereo_ctor( m_dac, NULL, NULL ); // TODO: is the NULL shred a problem?
362 multi_ctor( m_dac, NULL, NULL ); // TODO: remove and let type system do this
363 m_dac->add_ref();
364 // lock it
365 m_dac->lock();
367 // log
368 EM_log( CK_LOG_SEVERE, "initializing 'adc'..." );
369 g_t_adc->ugen_info->num_ins =
370 g_t_adc->ugen_info->num_outs = m_num_adc_channels;
371 m_adc = (Chuck_UGen *)instantiate_and_initialize_object( g_t_adc, NULL );
372 object_ctor( m_adc, NULL, NULL ); // TODO: this can't be the place to do this
373 stereo_ctor( m_adc, NULL, NULL );
374 multi_ctor( m_adc, NULL, NULL ); // TODO: remove and let type system do this
375 m_adc->add_ref();
376 // lock it
377 m_adc->lock();
379 // log
380 EM_log( CK_LOG_SEVERE, "initializing 'blackhole'..." );
381 m_bunghole = new Chuck_UGen;
382 m_bunghole->add_ref();
383 m_bunghole->lock();
384 initialize_object( m_bunghole, &t_ugen );
385 m_bunghole->tick = NULL;
386 m_shreduler->m_dac = m_dac;
387 m_shreduler->m_adc = m_adc;
388 m_shreduler->m_bunghole = m_bunghole;
389 m_shreduler->m_num_dac_channels = m_num_dac_channels;
390 m_shreduler->m_num_adc_channels = m_num_adc_channels;
392 // log
393 EM_log( CK_LOG_SYSTEM, "initializing '%s' audio...", m_audio ? "real-time" : "fake-time" );
394 // init bbq
395 if( !m_bbq->initialize( m_num_dac_channels, m_num_adc_channels,
396 Digitalio::m_sampling_rate, 16,
397 Digitalio::m_buffer_size, Digitalio::m_num_buffers,
398 Digitalio::m_dac_n, Digitalio::m_adc_n,
399 m_block, this, m_audio ) )
401 m_last_error = "cannot initialize audio device (try using --silent/-s)";
402 // pop indent
403 EM_poplog();
404 return FALSE;
407 // pop indent
408 EM_poplog();
410 return TRUE;
416 //-----------------------------------------------------------------------------
417 // name: compensate_bbq()
418 // desc: ...
419 //-----------------------------------------------------------------------------
420 void Chuck_VM::compensate_bbq()
422 // set shreduler - the audio was initialized elsewhere
423 m_shreduler->bbq = m_bbq;
429 //-----------------------------------------------------------------------------
430 // name: shutdown()
431 // desc: ...
432 //-----------------------------------------------------------------------------
433 t_CKBOOL Chuck_VM::shutdown()
435 // make sure we are in the initialized state
436 if( !m_init ) return FALSE;
438 // log
439 EM_log( CK_LOG_SYSTEM, "shutting down virtual machine..." );
440 // push indent
441 EM_pushlog();
442 // unlockdown
443 Chuck_VM_Object::unlock_all();
445 // stop
446 if( m_running )
448 this->stop();
449 usleep( 50000 );
452 // shutdown audio
453 if( m_audio )
455 // log
456 EM_log( CK_LOG_SYSTEM, "shutting down real-time audio..." );
458 m_bbq->digi_out()->cleanup();
459 m_bbq->digi_in()->cleanup();
460 m_bbq->shutdown();
461 m_audio = FALSE;
463 // log
464 EM_log( CK_LOG_SYSTEM, "freeing bbq subsystem..." );
465 // clean up
466 SAFE_DELETE( m_bbq );
468 // log
469 EM_log( CK_LOG_SYSTEM, "freeing shreduler..." );
470 // free the shreduler
471 SAFE_DELETE( m_shreduler );
473 // log
474 EM_log( CK_LOG_SYSTEM, "freeing msg/reply/event buffers..." );
475 // free the msg buffer
476 SAFE_DELETE( m_msg_buffer );
477 // free the reply buffer
478 SAFE_DELETE( m_reply_buffer );
479 // free the event buffer
480 SAFE_DELETE( m_event_buffer );
482 // log
483 EM_log( CK_LOG_SEVERE, "clearing shreds..." );
484 // terminate shreds
485 Chuck_VM_Shred * curr = m_shreds, * prev = NULL;
486 while( curr )
488 prev = curr;
489 curr = curr->next;
490 // release shred
491 prev->release();
493 m_shreds = NULL;
494 m_num_shreds = 0;
496 // log
497 EM_pushlog();
498 EM_log( CK_LOG_SEVERE, "freeing dumped shreds..." );
499 // do it
500 this->release_dump();
501 EM_poplog();
503 // log
504 EM_log( CK_LOG_SYSTEM, "freeing special ugens..." );
505 // go
506 SAFE_RELEASE( m_dac );
507 SAFE_RELEASE( m_adc );
508 SAFE_RELEASE( m_bunghole );
510 m_init = FALSE;
512 // pop indent
513 EM_poplog();
515 return TRUE;
521 //-----------------------------------------------------------------------------
522 // name: start_audio()
523 // desc: ...
524 //-----------------------------------------------------------------------------
525 t_CKBOOL Chuck_VM::start_audio( )
527 // audio
528 if( !m_audio_started && m_audio )
530 EM_log( CK_LOG_SEVERE, "starting real-time audio..." );
531 m_bbq->digi_out()->start();
532 m_bbq->digi_in()->start();
535 // set the flag to true to avoid entering this function
536 m_audio_started = TRUE;
538 return TRUE;
544 //-----------------------------------------------------------------------------
545 // name: run()
546 // desc: ...
547 //-----------------------------------------------------------------------------
548 t_CKBOOL Chuck_VM::run( )
550 // check if init
551 if( m_dac == NULL )
553 m_last_error = "VM and/or synthesis not initialized...";
554 return FALSE;
557 // check if already running
558 if( m_running )
560 m_last_error = "virtual machine already running...";
561 return FALSE;
564 m_running = TRUE;
566 // log
567 EM_log( CK_LOG_SYSTEM, "running virtual machine..." );
568 // push indent
569 EM_pushlog();
571 // audio
572 //if( m_audio )
574 // log
575 EM_log( CK_LOG_SEVERE, "initializing audio buffers..." );
576 if( !m_bbq->digi_out()->initialize( ) )
578 m_last_error = "cannot open audio output (option: use --silent/-s)";
579 return FALSE;
582 m_bbq->digi_in()->initialize( );
585 // log
586 EM_log( CK_LOG_SEVERE, "virtual machine running..." );
587 // pop indent
588 EM_poplog();
590 // run
591 if( m_block ) this->run( -1 );
592 else
594 // compute shreds before first sample
595 if( !compute() )
597 // done
598 m_running = FALSE;
599 // log
600 EM_log( CK_LOG_SYSTEM, "virtual machine stopped..." );
602 else
604 // start audio
605 if( !m_audio_started ) start_audio();
607 // wait
608 while( m_running )
609 { usleep( 50000 ); }
613 return TRUE;
616 /*should we comment out what we just did?
617 i can't think of why it might be affecting this part of the vm
618 you never know
619 true*/
624 //-----------------------------------------------------------------------------
625 // name: compute()
626 // desc: ...
627 //-----------------------------------------------------------------------------
628 t_CKBOOL Chuck_VM::compute()
630 Chuck_VM_Shred *& shred = m_shreduler->m_current_shred;
631 Chuck_Msg * msg = NULL;
632 Chuck_Event * event = NULL;
633 t_CKBOOL iterate = TRUE;
635 // iteration until no more shreds/events/messages
636 while( iterate )
638 // get the shreds queued for 'now'
639 while(( shred = m_shreduler->get() ))
641 // set the current time of the shred
642 shred->now = shred->wake_time;
644 // track shred activation
645 CK_TRACK( Chuck_Stats::instance()->activate_shred( shred ) );
647 // run the shred
648 if( !shred->run( this ) )
650 // track shred deactivation
651 CK_TRACK( Chuck_Stats::instance()->deactivate_shred( shred ) );
653 this->free( shred, TRUE );
654 shred = NULL;
655 if( !m_num_shreds && m_halt ) return FALSE;
658 // track shred deactivation
659 CK_TRACK( if( shred ) Chuck_Stats::instance()->deactivate_shred( shred ) );
661 // zero out
662 shred = NULL;
665 // set to false for now
666 iterate = FALSE;
668 // broadcast queued events
669 while( m_event_buffer->get( &event, 1 ) )
670 { event->broadcast(); iterate = TRUE; }
672 // process messages
673 while( m_msg_buffer->get( &msg, 1 ) )
674 { process_msg( msg ); iterate = TRUE; }
676 // clear dumped shreds
677 if( m_num_dumped_shreds > 0 )
678 release_dump();
681 return TRUE;
687 //-----------------------------------------------------------------------------
688 // name: run()
689 // desc: ...
690 //-----------------------------------------------------------------------------
691 t_CKBOOL Chuck_VM::run( t_CKINT num_samps )
693 // loop it
694 while( num_samps )
696 // compute shreds
697 if( !compute() ) goto vm_stop;
699 // start audio
700 if( !m_audio_started ) start_audio();
702 // advance the shreduler
703 m_shreduler->advance();
705 // count
706 if( num_samps > 0 ) num_samps--;
709 return FALSE;
711 // vm stop here
712 vm_stop:
713 m_running = FALSE;
715 // log
716 EM_log( CK_LOG_SYSTEM, "virtual machine stopped..." );
718 return TRUE;
724 //-----------------------------------------------------------------------------
725 // name: pause()
726 // desc: ...
727 //-----------------------------------------------------------------------------
728 t_CKBOOL Chuck_VM::pause( )
730 m_running = FALSE;
732 return TRUE;
738 //-----------------------------------------------------------------------------
739 // name: stop()
740 // desc: ...
741 //-----------------------------------------------------------------------------
742 t_CKBOOL Chuck_VM::stop( )
744 // log
745 EM_log( CK_LOG_SEVERE, "requesting STOP virtual machine..." );
747 m_running = FALSE;
748 Digitalio::m_end = TRUE;
750 return TRUE;
756 //-----------------------------------------------------------------------------
757 // name: gc
758 // desc: ...
759 //-----------------------------------------------------------------------------
760 void Chuck_VM::gc( t_CKUINT amount )
767 //-----------------------------------------------------------------------------
768 // name: gc
769 // desc: ...
770 //-----------------------------------------------------------------------------
771 void Chuck_VM::gc( )
778 //-----------------------------------------------------------------------------
779 // name: queue_msg()
780 // desc: ...
781 //-----------------------------------------------------------------------------
782 t_CKBOOL Chuck_VM::queue_msg( Chuck_Msg * msg, int count )
784 assert( count == 1 );
785 m_msg_buffer->put( &msg, count );
786 return TRUE;
792 //-----------------------------------------------------------------------------
793 // name: queue_event()
794 // desc: ...
795 //-----------------------------------------------------------------------------
796 t_CKBOOL Chuck_VM::queue_event( Chuck_Event * event, int count )
798 assert( count == 1 );
799 m_event_buffer->put( &event, count );
800 return TRUE;
806 //-----------------------------------------------------------------------------
807 // name: get_reply()
808 // desc: ...
809 //-----------------------------------------------------------------------------
810 Chuck_Msg * Chuck_VM::get_reply( )
812 Chuck_Msg * msg = NULL;
813 m_reply_buffer->get( &msg, 1 );
814 return msg;
820 //-----------------------------------------------------------------------------
821 // name: process_msg()
822 // desc: ...
823 //-----------------------------------------------------------------------------
824 t_CKUINT Chuck_VM::process_msg( Chuck_Msg * msg )
826 t_CKUINT retval = 0xfffffff0;
828 if( msg->type == MSG_REPLACE )
830 Chuck_VM_Shred * out = m_shreduler->lookup( msg->param );
831 if( !out )
833 EM_error3( "[chuck](VM): error replacing shred: no shred with id %i...",
834 msg->param );
835 retval = 0;
836 goto done;
839 Chuck_VM_Shred * shred = msg->shred;
840 if( !shred )
842 shred = new Chuck_VM_Shred;
843 shred->initialize( msg->code );
844 shred->name = msg->code->name;
845 shred->base_ref = shred->mem;
846 shred->add_ref();
848 // set the current time
849 shred->start = m_shreduler->now_system;
850 // set the id
851 shred->xid = msg->param;
852 // set the now
853 shred->now = shred->wake_time = m_shreduler->now_system;
854 // set the vm
855 shred->vm_ref = this;
856 // set args
857 if( msg->args ) shred->args = *(msg->args);
858 // add it to the parent
859 if( shred->parent )
860 shred->parent->children[shred->xid] = shred;
862 // replace
863 if( m_shreduler->remove( out ) && m_shreduler->shredule( shred ) )
865 EM_error3( "[chuck](VM): replacing shred %i (%s) with %i (%s)...",
866 out->xid, mini(out->name.c_str()), shred->xid, mini(shred->name.c_str()) );
867 this->free( out, TRUE, FALSE );
868 retval = shred->xid;
870 // tracking new shred
871 CK_TRACK( Chuck_Stats::instance()->add_shred( shred ) );
873 goto done;
875 else
877 EM_error3( "[chuck](VM): shreduler ERROR replacing shred %i...",
878 out->xid );
879 shred->release();
880 retval = 0;
881 goto done;
884 else if( msg->type == MSG_REMOVE )
886 if( msg->param == 0xffffffff )
888 if( !this->m_num_shreds)
890 EM_error3( "[chuck](VM): no shreds to remove..." );
891 retval = 0;
892 goto done;
895 t_CKINT xid = m_shred_id;
896 Chuck_VM_Shred * shred = NULL;
897 while( xid >= 0 && m_shreduler->remove( shred = m_shreduler->lookup( xid ) ) == 0 )
898 xid--;
899 if( xid >= 0 )
901 EM_error3( "[chuck](VM): removing recent shred: %i (%s)...",
902 xid, mini(shred->name.c_str()) );
903 this->free( shred, TRUE );
904 retval = xid;
906 else
908 EM_error3( "[chuck](VM): no shreds removed..." );
909 retval = 0;
910 goto done;
913 else
915 Chuck_VM_Shred * shred = m_shreduler->lookup( msg->param );
916 if( !shred )
918 EM_error3( "[chuck](VM): cannot remove: no shred with id %i...",
919 msg->param );
920 retval = 0;
921 goto done;
923 if( shred != m_shreduler->m_current_shred && !m_shreduler->remove( shred ) ) // was lookup
925 EM_error3( "[chuck](VM): shreduler: cannot remove shred %i...",
926 msg->param );
927 retval = 0;
928 goto done;
930 EM_error3( "[chuck](VM): removing shred: %i (%s)...",
931 msg->param, mini(shred->name.c_str()) );
932 this->free( shred, TRUE );
933 retval = msg->param;
936 else if( msg->type == MSG_REMOVEALL )
938 t_CKUINT xid = m_shred_id;
939 EM_error3( "[chuck](VM): removing all (%i) shreds...", m_num_shreds );
940 Chuck_VM_Shred * shred = NULL;
942 while( m_num_shreds && xid > 0 )
944 if( m_shreduler->remove( shred = m_shreduler->lookup( xid ) ) )
945 this->free( shred, TRUE );
946 xid--;
949 m_shred_id = 0;
950 m_num_shreds = 0;
952 else if( msg->type == MSG_ADD )
954 t_CKUINT xid = 0;
955 Chuck_VM_Shred * shred = NULL;
956 if( msg->shred ) shred = this->spork( msg->shred );
957 else shred = this->spork( msg->code, NULL );
958 xid = shred->xid;
959 if( msg->args ) shred->args = *(msg->args);
961 const char * s = ( msg->shred ? msg->shred->name.c_str() : msg->code->name.c_str() );
962 EM_error3( "[chuck](VM): sporking incoming shred: %i (%s)...", xid, mini(s) );
963 retval = xid;
964 goto done;
966 else if( msg->type == MSG_KILL )
968 EM_error3( "[chuck](VM): KILL received...." );
969 // close file handles and clean up
970 all_detach();
971 // TODO: free more memory?
973 // log
974 EM_log( CK_LOG_INFO, "(VM): exiting..." );
975 // come again
976 exit( 1 );
978 else if( msg->type == MSG_STATUS )
980 // fill in structure
981 if( msg->user && msg->reply )
983 // cast
984 Chuck_VM_Status * status = (Chuck_VM_Status *)msg->user;
985 // get it
986 m_shreduler->status( status );
988 else
990 m_shreduler->status();
993 else if( msg->type == MSG_TIME )
995 float srate = (float)Digitalio::sampling_rate();
996 fprintf( stderr, "[chuck](VM): the values of now:\n" );
997 fprintf( stderr, " now = %.6f (samp)\n", m_shreduler->now_system );
998 fprintf( stderr, " = %.6f (second)\n", m_shreduler->now_system / srate );
999 fprintf( stderr, " = %.6f (minute)\n", m_shreduler->now_system / srate / 60.0f );
1000 fprintf( stderr, " = %.6f (hour)\n", m_shreduler->now_system / srate / 60.0f / 60.0f );
1001 fprintf( stderr, " = %.6f (day)\n", m_shreduler->now_system / srate / 60.0f / 60.0f / 24.0f );
1002 fprintf( stderr, " = %.6f (week)\n", m_shreduler->now_system / srate / 60.0f / 60.0f / 24.0f / 7.0f );
1004 else if( msg->type == MSG_RESET_ID )
1006 t_CKUINT n = m_shreduler->highest();
1007 m_shred_id = n;
1008 fprintf( stderr, "[chuck](VM): reseting shred id to %d...\n", m_shred_id + 1 );
1011 done:
1013 if( msg->reply )
1015 msg->replyA = retval;
1016 m_reply_buffer->put( &msg, 1 );
1018 else
1019 SAFE_DELETE(msg);
1021 return retval;
1027 //-----------------------------------------------------------------------------
1028 // name: next_id()
1029 // desc: ...
1030 //-----------------------------------------------------------------------------
1031 t_CKUINT Chuck_VM::next_id( )
1033 return ++m_shred_id;
1039 //-----------------------------------------------------------------------------
1040 // name: shreduler()
1041 // desc: ...
1042 //-----------------------------------------------------------------------------
1043 Chuck_VM_Shreduler * Chuck_VM::shreduler( ) const
1045 return m_shreduler;
1051 //-----------------------------------------------------------------------------
1052 // name: bbq()
1053 // desc: ...
1054 //-----------------------------------------------------------------------------
1055 BBQ * Chuck_VM::bbq( ) const
1057 return m_bbq;
1063 //-----------------------------------------------------------------------------
1064 // name: srate()
1065 // desc: ...
1066 //-----------------------------------------------------------------------------
1067 t_CKUINT Chuck_VM::srate() const
1069 return (t_CKUINT)Digitalio::sampling_rate();
1075 //-----------------------------------------------------------------------------
1076 // name: fork()
1077 // desc: ...
1078 //-----------------------------------------------------------------------------
1079 Chuck_VM_Shred * Chuck_VM::fork( Chuck_VM_Code * code )
1081 return NULL;
1087 //-----------------------------------------------------------------------------
1088 // name: spork()
1089 // desc: ...
1090 //-----------------------------------------------------------------------------
1091 Chuck_VM_Shred * Chuck_VM::spork( Chuck_VM_Code * code, Chuck_VM_Shred * parent )
1093 // allocate a new shred
1094 Chuck_VM_Shred * shred = new Chuck_VM_Shred;
1095 // initialize the shred (default stack size)
1096 shred->initialize( code );
1097 // set the name
1098 shred->name = code->name;
1099 // set the parent
1100 shred->parent = parent;
1101 // set the base ref for global
1102 if( parent ) shred->base_ref = shred->parent->base_ref;
1103 else shred->base_ref = shred->mem;
1104 // spork it
1105 this->spork( shred );
1107 // track new shred
1108 CK_TRACK( Chuck_Stats::instance()->add_shred( shred ) );
1110 return shred;
1116 //-----------------------------------------------------------------------------
1117 // name: spork()
1118 // desc: ...
1119 //-----------------------------------------------------------------------------
1120 Chuck_VM_Shred * Chuck_VM::spork( Chuck_VM_Shred * shred )
1122 // set the current time
1123 shred->start = m_shreduler->now_system;
1124 // set the now
1125 shred->now = shred->wake_time = m_shreduler->now_system;
1126 // set the id
1127 shred->xid = next_id();
1128 // set the vm
1129 shred->vm_ref = this;
1130 // add ref
1131 shred->add_ref();
1132 // add it to the parent
1133 if( shred->parent )
1134 shred->parent->children[shred->xid] = shred;
1135 // shredule it
1136 m_shreduler->shredule( shred );
1137 // count
1138 m_num_shreds++;
1140 return shred;
1146 //-----------------------------------------------------------------------------
1147 // name: free()
1148 // desc: ...
1149 //-----------------------------------------------------------------------------
1150 t_CKBOOL Chuck_VM::free( Chuck_VM_Shred * shred, t_CKBOOL cascade, t_CKBOOL dec )
1152 assert( cascade );
1154 // log
1155 EM_log( CK_LOG_FINER, "freeing shred (id==%d | ptr==%p)", shred->xid,
1156 (t_CKUINT)shred );
1158 // abort on the double free
1159 // TODO: can a shred be dumped, then resporked? from code?
1160 if( shred->is_dumped ) return FALSE;
1162 // mark this done
1163 shred->is_done = TRUE;
1165 // free the children
1166 t_CKINT size = shred->children.size();
1167 if( size )
1169 vector<Chuck_VM_Shred *> list; list.resize( size );
1170 map<t_CKUINT, Chuck_VM_Shred *>::iterator iter; t_CKINT i = 0;
1171 for( iter = shred->children.begin(); iter != shred->children.end(); iter++ )
1172 list[i++] = (*iter).second;
1173 for( i = 0; i < size; i++ )
1174 this->free( list[i], cascade );
1177 // make sure it's done
1178 assert( shred->children.size() == 0 );
1180 // tell parent
1181 if( shred->parent )
1182 shred->parent->children.erase( shred->xid );
1184 // track remove shred
1185 CK_TRACK( Chuck_Stats::instance()->remove_shred( shred ) );
1187 // free!
1188 m_shreduler->remove( shred );
1189 // TODO: remove shred from event, with synchronization (still necessary with dump?)
1190 // if( shred->event ) shred->event->remove( shred );
1191 // OLD: shred->release();
1192 this->dump( shred );
1193 shred = NULL;
1194 if( dec ) m_num_shreds--;
1195 if( !m_num_shreds ) m_shred_id = 0;
1197 return TRUE;
1203 //-----------------------------------------------------------------------------
1204 // name: abort_current_shred()
1205 // desc: ...
1206 //-----------------------------------------------------------------------------
1207 t_CKBOOL Chuck_VM::abort_current_shred( )
1209 // for threading
1210 Chuck_VM_Shred * shred = m_shreduler->m_current_shred;
1212 // if there
1213 if( shred )
1215 // log
1216 EM_log( CK_LOG_SEVERE, "trying to abort current shred (id: %d)", shred->xid );
1217 // flag it
1218 shred->is_abort = TRUE;
1220 else
1222 // log
1223 EM_log( CK_LOG_SEVERE, "cannot abort shred: nothing currently running!" );
1226 return shred != NULL;
1232 //-----------------------------------------------------------------------------
1233 // name: dump()
1234 // desc: ...
1235 //-----------------------------------------------------------------------------
1236 void Chuck_VM::dump( Chuck_VM_Shred * shred )
1238 // log
1239 EM_log( CK_LOG_FINER, "dumping shred (id==%d | ptr==%p)", shred->xid,
1240 (t_CKUINT)shred );
1241 // add
1242 m_shred_dump.push_back( shred );
1243 // stop
1244 shred->is_running = FALSE;
1245 shred->is_done = TRUE;
1246 shred->is_dumped = TRUE;
1247 // TODO: cool?
1248 shred->xid = 0;
1249 // inc
1250 m_num_dumped_shreds++;
1256 //-----------------------------------------------------------------------------
1257 // name: release_dump()
1258 // desc: ...
1259 //-----------------------------------------------------------------------------
1260 void Chuck_VM::release_dump( )
1262 // log
1263 EM_log( CK_LOG_FINER, "releasing dumped shreds..." );
1265 // iterate through dump
1266 for( t_CKUINT i = 0; i < m_shred_dump.size(); i++ )
1267 SAFE_RELEASE( m_shred_dump[i] );
1269 // clear the dump
1270 m_shred_dump.clear();
1271 // reset
1272 m_num_dumped_shreds = 0;
1278 //-----------------------------------------------------------------------------
1279 // name: Chuck_VM_Stack()
1280 // desc: ...
1281 //-----------------------------------------------------------------------------
1282 Chuck_VM_Stack::Chuck_VM_Stack()
1284 stack = sp = sp_max = NULL;
1285 prev = next = NULL;
1286 m_is_init = FALSE;
1292 //-----------------------------------------------------------------------------
1293 // name: ~Chuck_VM_Stack()
1294 // desc: ...
1295 //-----------------------------------------------------------------------------
1296 Chuck_VM_Stack::~Chuck_VM_Stack()
1298 this->shutdown();
1304 //-----------------------------------------------------------------------------
1305 // name: Chuck_VM_Code()
1306 // desc: ...
1307 //-----------------------------------------------------------------------------
1308 Chuck_VM_Code::Chuck_VM_Code()
1310 instr = NULL;
1311 num_instr = 0;
1312 stack_depth = 0;
1313 need_this = FALSE;
1314 native_func = 0;
1315 native_func_type = NATIVE_UNKNOWN;
1321 //-----------------------------------------------------------------------------
1322 // name: ~Chuck_VM_Code()
1323 // desc: ...
1324 //-----------------------------------------------------------------------------
1325 Chuck_VM_Code::~Chuck_VM_Code()
1327 // free instructions
1328 if( instr )
1330 // loop over array
1331 for( t_CKUINT i = 0; i < num_instr; i++ )
1332 delete instr[i];
1334 // free the array
1335 SAFE_DELETE_ARRAY( instr );
1338 num_instr = 0;
1344 // offset in bytes at the beginning of a stack for initializing data
1345 #define VM_STACK_OFFSET 16
1346 // 1/factor of stack is left blank, to give room to detect overflow
1347 #define VM_STACK_PADDING_FACTOR 16
1348 //-----------------------------------------------------------------------------
1349 // name: initialize()
1350 // desc: ...
1351 //-----------------------------------------------------------------------------
1352 t_CKBOOL Chuck_VM_Stack::initialize( t_CKUINT size )
1354 if( m_is_init )
1355 return FALSE;
1357 // make room for header
1358 size += VM_STACK_OFFSET;
1359 // allocate stack
1360 stack = new t_CKBYTE[size];
1361 if( !stack ) goto out_of_memory;
1363 // zero
1364 memset( stack, 0, size );
1366 // advance stack after the header
1367 stack += VM_STACK_OFFSET;
1368 // set the sp
1369 sp = stack;
1370 // upper limit (padding factor)
1371 sp_max = sp + size - (size / VM_STACK_PADDING_FACTOR);
1373 // set flag and return
1374 return m_is_init = TRUE;
1376 out_of_memory:
1378 // we have a problem
1379 fprintf( stderr,
1380 "[chuck](VM): OutOfMemory: while allocating stack '%s'\n" );
1382 // return FALSE
1383 return FALSE;
1389 //-----------------------------------------------------------------------------
1390 // name: shutdown()
1391 // desc: ...
1392 //-----------------------------------------------------------------------------
1393 t_CKBOOL Chuck_VM_Stack::shutdown()
1395 if( !m_is_init )
1396 return FALSE;
1398 // free the stack
1399 stack -= VM_STACK_OFFSET;
1400 SAFE_DELETE_ARRAY( stack );
1401 sp = sp_max = NULL;
1403 // set the flag to false
1404 m_is_init = FALSE;
1406 return TRUE;
1412 //-----------------------------------------------------------------------------
1413 // name: Chuck_VM_Shred()
1414 // desc: ...
1415 //-----------------------------------------------------------------------------
1416 Chuck_VM_Shred::Chuck_VM_Shred()
1418 mem = new Chuck_VM_Stack;
1419 reg = new Chuck_VM_Stack;
1420 code = NULL;
1421 next = prev = NULL;
1422 instr = NULL;
1423 parent = NULL;
1424 // obj_array = NULL;
1425 // obj_array_size = 0;
1426 base_ref = NULL;
1427 vm_ref = NULL;
1428 event = NULL;
1429 xid = 0;
1431 // set
1432 CK_TRACK( stat = NULL );
1438 //-----------------------------------------------------------------------------
1439 // name: ~Chuck_VM_Shred()
1440 // desc: ...
1441 //-----------------------------------------------------------------------------
1442 Chuck_VM_Shred::~Chuck_VM_Shred()
1444 this->shutdown();
1450 //-----------------------------------------------------------------------------
1451 // name: initialize()
1452 // desc: ...
1453 //-----------------------------------------------------------------------------
1454 t_CKBOOL Chuck_VM_Shred::initialize( Chuck_VM_Code * c,
1455 t_CKUINT mem_stack_size,
1456 t_CKUINT reg_stack_size )
1458 // allocate mem and reg
1459 if( !mem->initialize( mem_stack_size ) ) return FALSE;
1460 if( !reg->initialize( reg_stack_size ) ) return FALSE;
1462 // program counter
1463 pc = 0;
1464 next_pc = 1;
1465 // code pointer
1466 code_orig = code = c;
1467 // add reference
1468 code_orig->add_ref();
1469 // shred in dump (all done)
1470 is_dumped = FALSE;
1471 // shred done
1472 is_done = FALSE;
1473 // shred running
1474 is_running = FALSE;
1475 // shred abort
1476 is_abort = FALSE;
1477 // set the instr
1478 instr = c->instr;
1479 // zero out the id
1480 xid = 0;
1482 // initialize
1483 initialize_object( this, &t_shred );
1485 return TRUE;
1491 //-----------------------------------------------------------------------------
1492 // name: shutdown()
1493 // desc: ...
1494 //-----------------------------------------------------------------------------
1495 t_CKBOOL Chuck_VM_Shred::shutdown()
1497 // get iterator to our map
1498 map<Chuck_UGen *, Chuck_UGen *>::iterator iter = m_ugen_map.begin();
1499 while( iter != m_ugen_map.end() )
1501 (*iter).first->disconnect( TRUE );
1502 iter++;
1504 m_ugen_map.clear();
1506 SAFE_DELETE( mem );
1507 SAFE_DELETE( reg );
1508 base_ref = NULL;
1510 // delete temp pointer space
1511 // SAFE_DELETE_ARRAY( obj_array );
1512 // obj_array_size = 0;
1514 // TODO: is this right?
1515 code_orig->release();
1516 code_orig = code = NULL;
1517 // what to do with next and prev?
1519 return TRUE;
1525 //-----------------------------------------------------------------------------
1526 // name: add()
1527 // desc: ...
1528 //-----------------------------------------------------------------------------
1529 t_CKBOOL Chuck_VM_Shred::add( Chuck_UGen * ugen )
1531 if( m_ugen_map[ugen] )
1532 return FALSE;
1534 m_ugen_map[ugen] = ugen;
1535 return TRUE;
1541 //-----------------------------------------------------------------------------
1542 // name: remove()
1543 // desc: ...
1544 //-----------------------------------------------------------------------------
1545 t_CKBOOL Chuck_VM_Shred::remove( Chuck_UGen * ugen )
1547 if( !m_ugen_map[ugen] )
1548 return FALSE;
1550 // remove it
1551 m_ugen_map.erase( ugen );
1552 return TRUE;
1558 //-----------------------------------------------------------------------------
1559 // name: run()
1560 // desc: ...
1561 //-----------------------------------------------------------------------------
1562 t_CKBOOL Chuck_VM_Shred::run( Chuck_VM * vm )
1564 // get the code
1565 instr = code->instr;
1566 is_running = TRUE;
1567 t_CKBOOL * vm_running = &vm->m_running;
1569 // go!
1570 while( is_running && *vm_running && !is_abort )
1572 // execute the instruction
1573 instr[pc]->execute( vm, this );
1575 // set to next_pc;
1576 pc = next_pc;
1577 next_pc++;
1579 // track number of cycles
1580 CK_TRACK( this->stat->cycles++ );
1583 // check abort
1584 if( is_abort )
1586 // log
1587 EM_log( CK_LOG_SYSTEM, "aborting shred (id: %d)", this->xid );
1588 // done
1589 is_done = TRUE;
1592 // is the shred finished
1593 return !is_done;
1599 //-----------------------------------------------------------------------------
1600 // name: Chuck_VM_Shreduler()
1601 // desc: ...
1602 //-----------------------------------------------------------------------------
1603 Chuck_VM_Shreduler::Chuck_VM_Shreduler()
1605 now_system = 0;
1606 rt_audio = FALSE;
1607 bbq = NULL;
1608 shred_list = NULL;
1609 m_current_shred = NULL;
1610 m_dac = NULL;
1611 m_adc = NULL;
1612 m_bunghole = NULL;
1613 m_num_dac_channels = 0;
1614 m_num_adc_channels = 0;
1620 //-----------------------------------------------------------------------------
1621 // name: ~Chuck_VM_Shreduler()
1622 // desc: ...
1623 //-----------------------------------------------------------------------------
1624 Chuck_VM_Shreduler::~Chuck_VM_Shreduler()
1626 this->shutdown();
1632 //-----------------------------------------------------------------------------
1633 // name: initialize()
1634 // desc: ...
1635 //-----------------------------------------------------------------------------
1636 t_CKBOOL Chuck_VM_Shreduler::initialize()
1638 return TRUE;
1644 //-----------------------------------------------------------------------------
1645 // name: shutdown()
1646 // desc: ...
1647 //-----------------------------------------------------------------------------
1648 t_CKBOOL Chuck_VM_Shreduler::shutdown()
1650 return TRUE;
1656 //-----------------------------------------------------------------------------
1657 // name: add_blocked()
1658 // desc: add shred to the shreduler's blocked list
1659 //-----------------------------------------------------------------------------
1660 t_CKBOOL Chuck_VM_Shreduler::add_blocked( Chuck_VM_Shred * shred )
1662 // add shred to map, using pointer
1663 blocked[shred] = shred;
1665 return TRUE;
1671 //-----------------------------------------------------------------------------
1672 // name: remove_blocked()
1673 // desc: remove shred from the shreduler's blocked list
1674 //-----------------------------------------------------------------------------
1675 t_CKBOOL Chuck_VM_Shreduler::remove_blocked( Chuck_VM_Shred * shred )
1677 // remove from hash
1678 std::map<Chuck_VM_Shred *, Chuck_VM_Shred *>::iterator iter;
1679 iter = blocked.find( shred );
1680 blocked.erase( iter );
1682 // remove from event
1683 if( shred->event != NULL ) shred->event->remove( shred );
1685 return TRUE;
1691 //-----------------------------------------------------------------------------
1692 // name: shredule()
1693 // desc: ...
1694 //-----------------------------------------------------------------------------
1695 t_CKBOOL Chuck_VM_Shreduler::shredule( Chuck_VM_Shred * shred )
1697 return this->shredule( shred, now_system );
1703 //-----------------------------------------------------------------------------
1704 // name: shredule()
1705 // desc: ...
1706 //-----------------------------------------------------------------------------
1707 t_CKBOOL Chuck_VM_Shreduler::shredule( Chuck_VM_Shred * shred,
1708 t_CKTIME wake_time )
1710 // sanity check
1711 if( shred->prev || shred->next )
1713 // something is really wrong here - no shred can be
1714 // shreduled more than once
1715 EM_error3( "[chuck](VM): internal sanity check failed in shredule()" );
1716 EM_error3( "[chuck](VM): (shred shreduled while shreduled)" );
1718 return FALSE;
1721 // sanity check
1722 if( wake_time < (this->now_system - .5) )
1724 // trying to enqueue on a time that is less than now
1725 EM_error3( "[chuck](VM): internal sanity check failed in shredule()" );
1726 EM_error3( "[chuck](VM): (wake time is past) - %f : %f", wake_time, this->now_system );
1728 return FALSE;
1731 shred->wake_time = wake_time;
1733 // list empty
1734 if( !shred_list )
1735 shred_list = shred;
1736 else
1738 // pointers to the shred queue
1739 Chuck_VM_Shred * curr = shred_list;
1740 Chuck_VM_Shred * prev = NULL;
1742 while( curr )
1744 // found the place to insert
1745 if( curr->wake_time > wake_time )
1746 break;
1748 prev = curr;
1749 curr = curr->next;
1752 if( !prev )
1754 shred->next = shred_list;
1755 if( shred_list ) shred_list->prev = shred;
1756 shred_list = shred;
1758 else
1760 // insert the shred in sorted order
1761 shred->next = prev->next;
1762 shred->prev = prev;
1763 if( prev->next ) prev->next->prev = shred;
1764 prev->next = shred;
1768 return TRUE;
1773 //-----------------------------------------------------------------------------
1774 // name: advance2()
1775 // desc: ...
1776 //-----------------------------------------------------------------------------
1777 void Chuck_VM_Shreduler::advance2( )
1779 // advance system 'now'
1780 this->now_system += 1;
1782 // tick the dac
1783 SAMPLE l, r;
1784 BBQ * audio = this->bbq;
1786 // tick in
1787 if( rt_audio )
1789 audio->digi_in()->tick_in( &l, &r );
1790 m_adc->m_multi_chan[0]->m_current = l * m_adc->m_multi_chan[0]->m_gain;
1791 m_adc->m_multi_chan[1]->m_current = r * m_adc->m_multi_chan[1]->m_gain;
1792 m_adc->m_current = .5f * ( l + r );
1793 // time it
1794 m_adc->m_multi_chan[0]->m_time = this->now_system;
1795 m_adc->m_multi_chan[1]->m_time = this->now_system;
1796 m_adc->m_time = this->now_system;
1799 // dac
1800 m_dac->system_tick( this->now_system );
1801 l = m_dac->m_multi_chan[0]->m_current;
1802 r = m_dac->m_multi_chan[1]->m_current;
1803 // remove: 1.2.1.2
1804 // l *= .5f; r *= .5f;
1806 // suck samples
1807 m_bunghole->system_tick( this->now_system );
1809 // tick
1810 audio->digi_out()->tick_out( l, r );
1816 //-----------------------------------------------------------------------------
1817 // name: advance()
1818 // desc: ...
1819 //-----------------------------------------------------------------------------
1820 void Chuck_VM_Shreduler::advance( )
1822 // advance system 'now'
1823 this->now_system += 1;
1825 // tick the dac
1826 SAMPLE frame[128];
1827 SAMPLE sum = 0.0f;
1828 BBQ * audio = this->bbq;
1829 t_CKUINT i;
1831 // tick in
1832 if( rt_audio )
1834 audio->digi_in()->tick_in( frame, m_num_adc_channels );
1836 // loop over channels
1837 for( i = 0; i < m_num_adc_channels; i++ )
1839 m_adc->m_multi_chan[i]->m_current = frame[i] * m_adc->m_multi_chan[i]->m_gain * m_adc->m_gain;
1840 m_adc->m_multi_chan[i]->m_time = this->now_system;
1841 sum += m_adc->m_multi_chan[i]->m_current;
1843 m_adc->m_last = m_adc->m_current = sum / m_num_adc_channels;
1844 m_adc->m_time = this->now_system;
1847 // dac
1848 m_dac->system_tick( this->now_system );
1849 for( i = 0; i < m_num_dac_channels; i++ )
1850 frame[i] = m_dac->m_multi_chan[i]->m_current * .5f;
1852 // suck samples
1853 m_bunghole->system_tick( this->now_system );
1855 // tick
1856 audio->digi_out()->tick_out( frame, m_num_dac_channels );
1861 //-----------------------------------------------------------------------------
1862 // name: get()
1863 // desc: ...
1864 //-----------------------------------------------------------------------------
1865 Chuck_VM_Shred * Chuck_VM_Shreduler::get( )
1867 Chuck_VM_Shred * shred = shred_list;
1869 // list empty
1870 if( !shred )
1871 return NULL;
1873 // TODO: should this be <=?
1874 if( shred->wake_time <= ( this->now_system + .5 ) )
1876 // if( shred->wake_time < this->now_system )
1877 // assert( false );
1879 shred_list = shred->next;
1880 shred->next = NULL;
1881 shred->prev = NULL;
1883 if( shred_list )
1884 shred_list->prev = NULL;
1886 return shred;
1889 return NULL;
1895 //-----------------------------------------------------------------------------
1896 // name: highest()
1897 // desc: ...
1898 //-----------------------------------------------------------------------------
1899 t_CKUINT Chuck_VM_Shreduler::highest( )
1901 Chuck_VM_Shred * shred = shred_list;
1902 t_CKUINT n = 0;
1904 while( shred )
1906 if( shred->xid > n ) n = shred->xid;
1907 shred = shred->next;
1910 std::map<Chuck_VM_Shred *, Chuck_VM_Shred *>::iterator iter;
1911 for( iter = blocked.begin(); iter != blocked.end(); iter++ )
1913 shred = (*iter).second;
1914 if( shred->xid > n ) n = shred->xid;
1917 return n;
1923 //-----------------------------------------------------------------------------
1924 // name: replace()
1925 // desc: ...
1926 //-----------------------------------------------------------------------------
1927 t_CKBOOL Chuck_VM_Shreduler::replace( Chuck_VM_Shred * out, Chuck_VM_Shred * in )
1929 assert( FALSE );
1931 // sanity check
1932 if( !out || !in )
1933 return FALSE;
1935 if( !out->prev )
1936 shred_list = in;
1937 else
1938 out->prev->next = in;
1940 if( out->next )
1941 out->next->prev = in;
1943 in->next = out->next;
1944 in->prev = out->prev;
1946 out->next = out->prev = NULL;
1948 in->wake_time = out->wake_time;
1949 in->start = in->wake_time;
1951 return TRUE;
1957 //-----------------------------------------------------------------------------
1958 // name: remove()
1959 // desc: ...
1960 //-----------------------------------------------------------------------------
1961 t_CKBOOL Chuck_VM_Shreduler::remove( Chuck_VM_Shred * out )
1963 if( !out ) return FALSE;
1965 // if blocked
1966 if( out->event != NULL )
1968 return remove_blocked( out );
1971 // sanity check
1972 if( !out->prev && !out->next && out != shred_list )
1973 return FALSE;
1975 if( !out->prev )
1976 shred_list = out->next;
1977 else
1978 out->prev->next = out->next;
1980 if( out->next )
1981 out->next->prev = out->prev;
1983 out->next = out->prev = NULL;
1985 return TRUE;
1991 //-----------------------------------------------------------------------------
1992 // name: get()
1993 // desc: ...
1994 //-----------------------------------------------------------------------------
1995 Chuck_VM_Shred * Chuck_VM_Shreduler::lookup( t_CKUINT xid )
1997 Chuck_VM_Shred * shred = shred_list;
1999 // current shred?
2000 if( m_current_shred != NULL && m_current_shred->xid == xid )
2001 return m_current_shred;
2003 // look for in shreduled list
2004 while( shred )
2006 if( shred->xid == xid )
2007 return shred;
2009 shred = shred->next;
2012 // blocked?
2013 std::map<Chuck_VM_Shred *, Chuck_VM_Shred *>::iterator iter;
2014 for( iter = blocked.begin(); iter != blocked.end(); iter++ )
2016 shred = (*iter).second;
2017 if( shred->xid == xid )
2018 return shred;
2021 return NULL;
2027 //-----------------------------------------------------------------------------
2028 // name: SortByID()
2029 // desc: ...
2030 //-----------------------------------------------------------------------------
2031 struct SortByID
2033 bool operator() ( const Chuck_VM_Shred * lhs, const Chuck_VM_Shred * rhs )
2034 { return lhs->xid < rhs->xid; }
2040 //-----------------------------------------------------------------------------
2041 // name: status()
2042 // desc: ...
2043 //-----------------------------------------------------------------------------
2044 void Chuck_VM_Shreduler::status( Chuck_VM_Status * status )
2046 Chuck_VM_Shred * shred = shred_list;
2047 Chuck_VM_Shred * temp = NULL;
2049 t_CKUINT srate = Digitalio::sampling_rate();
2050 t_CKUINT s = (t_CKUINT)now_system;
2051 t_CKUINT h = s/(srate*3600);
2052 s = s - (h*(srate*3600));
2053 t_CKUINT m = s / (srate*60);
2054 s = s - (m*(srate*60));
2055 t_CKUINT sec = s / srate;
2056 s = s - (sec*(srate));
2057 // float millisecond = s / (float)(srate) * 1000.0f;
2059 status->srate = srate;
2060 status->now_system = now_system;
2061 status->t_second = sec;
2062 status->t_minute = m;
2063 status->t_hour = h;
2065 // get list of shreds
2066 vector<Chuck_VM_Shred *> list;
2067 while( shred )
2069 list.push_back( shred );
2070 shred = shred->next;
2073 // get blocked
2074 std::map<Chuck_VM_Shred *, Chuck_VM_Shred *>::iterator iter;
2075 for( iter = blocked.begin(); iter != blocked.end(); iter++ )
2077 shred = (*iter).second;
2078 list.push_back( shred );
2081 // get current shred
2082 if( temp = m_current_shred )
2083 list.push_back( temp );
2085 // sort the list
2086 SortByID byid;
2087 std::sort( list.begin(), list.end(), byid );
2089 // print status
2090 status->clear();
2091 for( t_CKUINT i = 0; i < list.size(); i++ )
2093 shred = list[i];
2094 status->list.push_back( new Chuck_VM_Shred_Status(
2095 shred->xid, shred->name, shred->start, shred->event != NULL ) );
2102 //-----------------------------------------------------------------------------
2103 // name: status()
2104 // desc: ...
2105 //-----------------------------------------------------------------------------
2106 void Chuck_VM_Shreduler::status( )
2108 Chuck_VM_Shred_Status * shred = NULL;
2110 this->status( &m_status );
2111 t_CKUINT h = m_status.t_hour;
2112 t_CKUINT m = m_status.t_minute;
2113 t_CKUINT sec = m_status.t_second;
2114 fprintf( stdout, "[chuck](VM): status (now == %ldh%ldm%lds, %.1f samps) ...\n",
2115 h, m, sec, m_status.now_system );
2117 // print status
2118 for( t_CKUINT i = 0; i < m_status.list.size(); i++ )
2120 shred = m_status.list[i];
2121 fprintf( stdout,
2122 " [shred id]: %ld [source]: %s [spork time]: %.2fs ago%s\n",
2123 shred->xid, mini( shred->name.c_str() ),
2124 (m_status.now_system - shred->start) / m_status.srate,
2125 shred->has_event ? " (blocked)" : "" );
2132 //-----------------------------------------------------------------------------
2133 // name: Chuck_VM_Status()
2134 // desc: ...
2135 //-----------------------------------------------------------------------------
2136 Chuck_VM_Status::Chuck_VM_Status()
2138 srate = 0;
2139 now_system = 0;
2140 t_second = t_minute = t_hour = 0;
2146 //-----------------------------------------------------------------------------
2147 // name: ~Chuck_VM_Status()
2148 // desc: ...
2149 //-----------------------------------------------------------------------------
2150 Chuck_VM_Status::~Chuck_VM_Status()
2152 this->clear();
2158 //-----------------------------------------------------------------------------
2159 // name: clear()
2160 // desc: ...
2161 //-----------------------------------------------------------------------------
2162 void Chuck_VM_Status::clear()
2164 for( t_CKUINT i = 0; i < list.size(); i++ )
2166 SAFE_DELETE( list[i] );
2169 list.clear();