*** empty log message ***
[chuck-blob.git] / v2 / chuck_vm.cpp
blob191524ae607beae4cd8c4f18bf989b539d60b7f3
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();
157 // dac tick
158 //UGEN_TICK __dac_tick( Chuck_Object * SELF, SAMPLE in, SAMPLE * out )
159 //{ *out = in; return TRUE; }
160 //UGEN_TICK __bunghole_tick( Chuck_Object * SELF, SAMPLE in, SAMPLE * out )
161 //{ *out = 0.0f; return TRUE; }
164 // static
165 #ifdef __MACOSX_CORE__
166 t_CKINT Chuck_VM::our_priority = 85;
167 #else
168 t_CKINT Chuck_VM::our_priority = 0x7fffffff;
169 #endif
172 #if !defined(__PLATFORM_WIN32__) || defined(__WINDOWS_PTHREAD__)
173 //-----------------------------------------------------------------------------
174 // name: set_priority()
175 // desc: ...
176 //-----------------------------------------------------------------------------
177 t_CKBOOL Chuck_VM::set_priority( t_CKINT priority, Chuck_VM * vm )
179 struct sched_param param;
180 pthread_t tid = pthread_self();
181 int policy;
183 // log
184 EM_log( CK_LOG_INFO, "setting thread priority to: %ld...", priority );
186 // get for thread
187 if( pthread_getschedparam( tid, &policy, &param) )
189 if( vm )
190 vm->m_last_error = "could not get current scheduling parameters";
191 return FALSE;
194 // priority
195 param.sched_priority = priority;
196 // policy
197 policy = SCHED_RR;
198 // set for thread
199 if( pthread_setschedparam( tid, policy, &param ) )
201 if( vm )
202 vm->m_last_error = "could not set new scheduling parameters";
203 return FALSE;
206 return TRUE;
208 #else
209 //-----------------------------------------------------------------------------
210 // name: set_priority()
211 // desc: ...
212 //-----------------------------------------------------------------------------
213 t_CKBOOL Chuck_VM::set_priority( t_CKINT priority, Chuck_VM * vm )
215 // if priority is 0 then done
216 if( !priority ) return TRUE;
218 // set the priority class of the process
219 // if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) )
220 // return FALSE;
222 // log
223 EM_log( CK_LOG_INFO, "setting thread priority to: %ld...", priority );
225 // set the priority the thread
226 // if( !SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL ) )
227 if( !SetThreadPriority( GetCurrentThread(), priority ) )
229 if( vm )
230 vm->m_last_error = "could not set new scheduling parameters";
231 return FALSE;
234 return TRUE;
236 #endif
241 //-----------------------------------------------------------------------------
242 // name: initialize()
243 // desc: ...
244 //-----------------------------------------------------------------------------
245 t_CKBOOL Chuck_VM::initialize( t_CKBOOL enable_audio, t_CKBOOL halt, t_CKUINT srate,
246 t_CKUINT buffer_size, t_CKUINT num_buffers,
247 t_CKUINT dac, t_CKUINT adc, t_CKUINT dac_chan,
248 t_CKUINT adc_chan, t_CKBOOL block, t_CKUINT adaptive )
250 if( m_init )
252 m_last_error = "VM already initialized!";
253 return FALSE;
256 // boost thread priority
257 // if( priority != 0x7fffffff && !set_priority( priority, this ) )
258 // return FALSE;
260 // log
261 EM_log( CK_LOG_SYSTEM, "initializing virtual machine..." );
262 EM_pushlog(); // push stack
263 EM_log( CK_LOG_SYSTEM, "behavior: %s", halt ? "HALT" : "LOOP" );
265 // lockdown
266 Chuck_VM_Object::lock_all();
268 // allocate bbq
269 m_bbq = new BBQ;
270 m_halt = halt;
271 m_audio = enable_audio;
272 m_block = block;
274 // log
275 EM_log( CK_LOG_SYSTEM, "allocating shreduler..." );
276 // allocate shreduler
277 m_shreduler = new Chuck_VM_Shreduler;
278 m_shreduler->bbq = m_bbq;
279 m_shreduler->rt_audio = enable_audio;
280 m_shreduler->set_adaptive( adaptive > 0 ? adaptive : 0 );
282 // log
283 EM_log( CK_LOG_SYSTEM, "allocating messaging buffers..." );
284 // allocate msg buffer
285 m_msg_buffer = new CBufferSimple;
286 m_msg_buffer->initialize( 1024, sizeof(Chuck_Msg *) );
287 //m_msg_buffer->join(); // this should return 0
288 m_reply_buffer = new CBufferSimple;
289 m_reply_buffer->initialize( 1024, sizeof(Chuck_Msg *) );
290 //m_reply_buffer->join(); // this should return 0 too
291 m_event_buffer = new CBufferSimple;
292 m_event_buffer->initialize( 1024, sizeof(Chuck_Event *) );
293 //m_event_buffer->join(); // this should also return 0
295 // log
296 EM_log( CK_LOG_SYSTEM, "real-time audio: %s", enable_audio ? "YES" : "NO" );
297 EM_log( CK_LOG_SYSTEM, "mode: %s", block ? "BLOCKING" : "CALLBACK" );
298 EM_log( CK_LOG_SYSTEM, "sample rate: %ld", srate );
299 EM_log( CK_LOG_SYSTEM, "buffer size: %ld", buffer_size );
300 if( enable_audio )
302 EM_log( CK_LOG_SYSTEM, "num buffers: %ld", num_buffers );
303 EM_log( CK_LOG_SYSTEM, "devices adc: %ld dac: %d (default 0)", adc, dac );
304 EM_log( CK_LOG_SYSTEM, "adaptive block processing: %ld", adaptive > 1 ? adaptive : 0 );
306 EM_log( CK_LOG_SYSTEM, "channels in: %ld out: %ld", adc_chan, dac_chan );
307 m_num_adc_channels = adc_chan;
308 m_num_dac_channels = dac_chan;
310 // at least set the sample rate and buffer size
311 m_bbq->set_srate( srate );
312 m_bbq->set_bufsize( buffer_size );
313 m_bbq->set_numbufs( num_buffers );
314 m_bbq->set_inouts( adc, dac );
315 m_bbq->set_chans( adc_chan, dac_chan );
317 // pop log
318 EM_poplog();
320 // TODO: clean up all the dynamic objects here on failure
321 // and in the shutdown function!
323 return m_init = TRUE;
329 //-----------------------------------------------------------------------------
330 // name: initialize_synthesis()
331 // desc: requires type system
332 //-----------------------------------------------------------------------------
333 t_CKBOOL Chuck_VM::initialize_synthesis( )
335 if( !m_init )
337 m_last_error = "VM initialize_synthesis() called on raw VM";
338 return FALSE;
341 if( !g_t_dac || !g_t_adc )
343 m_last_error = "VM initialize_synthesis() called before type system initialized";
344 return FALSE;
347 if( m_dac != NULL )
349 m_last_error = "VM synthesis already initialized";
350 return FALSE;
353 // log
354 EM_log( CK_LOG_SYSTEM, "initializing synthesis engine..." );
355 // push indent
356 EM_pushlog();
358 // log
359 EM_log( CK_LOG_SEVERE, "initializing 'dac'..." );
360 // allocate dac and adc
361 g_t_dac->ugen_info->num_outs =
362 g_t_dac->ugen_info->num_ins = m_num_dac_channels;
363 m_dac = (Chuck_UGen *)instantiate_and_initialize_object( g_t_dac, NULL );
364 object_ctor( m_dac, NULL, NULL ); // TODO: this can't be the place to do this
365 stereo_ctor( m_dac, NULL, NULL ); // TODO: is the NULL shred a problem?
366 multi_ctor( m_dac, NULL, NULL ); // TODO: remove and let type system do this
367 m_dac->add_ref();
368 // lock it
369 m_dac->lock();
371 // log
372 EM_log( CK_LOG_SEVERE, "initializing 'adc'..." );
373 g_t_adc->ugen_info->num_ins =
374 g_t_adc->ugen_info->num_outs = m_num_adc_channels;
375 m_adc = (Chuck_UGen *)instantiate_and_initialize_object( g_t_adc, NULL );
376 object_ctor( m_adc, NULL, NULL ); // TODO: this can't be the place to do this
377 stereo_ctor( m_adc, NULL, NULL );
378 multi_ctor( m_adc, NULL, NULL ); // TODO: remove and let type system do this
379 m_adc->add_ref();
380 // lock it
381 m_adc->lock();
383 // log
384 EM_log( CK_LOG_SEVERE, "initializing 'blackhole'..." );
385 m_bunghole = new Chuck_UGen;
386 m_bunghole->add_ref();
387 m_bunghole->lock();
388 initialize_object( m_bunghole, &t_ugen );
389 m_bunghole->tick = NULL;
390 m_bunghole->alloc_v( m_shreduler->m_max_block_size );
391 m_shreduler->m_dac = m_dac;
392 m_shreduler->m_adc = m_adc;
393 m_shreduler->m_bunghole = m_bunghole;
394 m_shreduler->m_num_dac_channels = m_num_dac_channels;
395 m_shreduler->m_num_adc_channels = m_num_adc_channels;
397 // log
398 EM_log( CK_LOG_SYSTEM, "initializing '%s' audio...", m_audio ? "real-time" : "fake-time" );
399 // init bbq
400 if( !m_bbq->initialize( m_num_dac_channels, m_num_adc_channels,
401 Digitalio::m_sampling_rate, 16,
402 Digitalio::m_buffer_size, Digitalio::m_num_buffers,
403 Digitalio::m_dac_n, Digitalio::m_adc_n,
404 m_block, this, m_audio ) )
406 m_last_error = "cannot initialize audio device (try using --silent/-s)";
407 // pop indent
408 EM_poplog();
409 return FALSE;
412 // pop indent
413 EM_poplog();
415 return TRUE;
421 //-----------------------------------------------------------------------------
422 // name: compensate_bbq()
423 // desc: ...
424 //-----------------------------------------------------------------------------
425 void Chuck_VM::compensate_bbq()
427 // set shreduler - the audio was initialized elsewhere
428 m_shreduler->bbq = m_bbq;
434 //-----------------------------------------------------------------------------
435 // name: shutdown()
436 // desc: ...
437 //-----------------------------------------------------------------------------
438 t_CKBOOL Chuck_VM::shutdown()
440 // make sure we are in the initialized state
441 if( !m_init ) return FALSE;
443 // log
444 EM_log( CK_LOG_SYSTEM, "shutting down virtual machine..." );
445 // push indent
446 EM_pushlog();
447 // unlockdown
448 Chuck_VM_Object::unlock_all();
450 // stop
451 if( m_running )
453 this->stop();
454 usleep( 50000 );
457 // shutdown audio
458 if( m_audio )
460 // log
461 EM_log( CK_LOG_SYSTEM, "shutting down real-time audio..." );
463 m_bbq->digi_out()->cleanup();
464 m_bbq->digi_in()->cleanup();
465 m_bbq->shutdown();
466 m_audio = FALSE;
468 // log
469 EM_log( CK_LOG_SYSTEM, "freeing bbq subsystem..." );
470 // clean up
471 SAFE_DELETE( m_bbq );
473 // log
474 EM_log( CK_LOG_SYSTEM, "freeing shreduler..." );
475 // free the shreduler
476 SAFE_DELETE( m_shreduler );
478 // log
479 EM_log( CK_LOG_SYSTEM, "freeing msg/reply/event buffers..." );
480 // free the msg buffer
481 SAFE_DELETE( m_msg_buffer );
482 // free the reply buffer
483 SAFE_DELETE( m_reply_buffer );
484 // free the event buffer
485 SAFE_DELETE( m_event_buffer );
487 // log
488 EM_log( CK_LOG_SEVERE, "clearing shreds..." );
489 // terminate shreds
490 Chuck_VM_Shred * curr = m_shreds, * prev = NULL;
491 while( curr )
493 prev = curr;
494 curr = curr->next;
495 // release shred
496 prev->release();
498 m_shreds = NULL;
499 m_num_shreds = 0;
501 // log
502 EM_pushlog();
503 EM_log( CK_LOG_SEVERE, "freeing dumped shreds..." );
504 // do it
505 this->release_dump();
506 EM_poplog();
508 // log
509 EM_log( CK_LOG_SYSTEM, "freeing special ugens..." );
510 // go
511 SAFE_RELEASE( m_dac );
512 SAFE_RELEASE( m_adc );
513 SAFE_RELEASE( m_bunghole );
515 m_init = FALSE;
517 // pop indent
518 EM_poplog();
520 return TRUE;
526 //-----------------------------------------------------------------------------
527 // name: start_audio()
528 // desc: ...
529 //-----------------------------------------------------------------------------
530 t_CKBOOL Chuck_VM::start_audio( )
532 // audio
533 if( !m_audio_started && m_audio )
535 EM_log( CK_LOG_SEVERE, "starting real-time audio..." );
536 m_bbq->digi_out()->start();
537 m_bbq->digi_in()->start();
540 // set the flag to true to avoid entering this function
541 m_audio_started = TRUE;
543 return TRUE;
549 //-----------------------------------------------------------------------------
550 // name: run()
551 // desc: ...
552 //-----------------------------------------------------------------------------
553 t_CKBOOL Chuck_VM::run( )
555 // check if init
556 if( m_dac == NULL )
558 m_last_error = "VM and/or synthesis not initialized...";
559 return FALSE;
562 // check if already running
563 if( m_running )
565 m_last_error = "virtual machine already running...";
566 return FALSE;
569 m_running = TRUE;
571 // log
572 EM_log( CK_LOG_SYSTEM, "running virtual machine..." );
573 // push indent
574 EM_pushlog();
576 // audio
577 //if( m_audio )
579 // log
580 EM_log( CK_LOG_SEVERE, "initializing audio buffers..." );
581 if( !m_bbq->digi_out()->initialize( ) )
583 m_last_error = "cannot open audio output (option: use --silent/-s)";
584 return FALSE;
587 m_bbq->digi_in()->initialize( );
590 // log
591 EM_log( CK_LOG_SEVERE, "virtual machine running..." );
592 // pop indent
593 EM_poplog();
595 // run
596 if( m_block ) this->run( -1 );
597 else
599 // compute shreds before first sample
600 if( !compute() )
602 // done
603 m_running = FALSE;
604 // log
605 EM_log( CK_LOG_SYSTEM, "virtual machine stopped..." );
607 else
609 // start audio
610 if( !m_audio_started ) start_audio();
612 // wait
613 while( m_running )
614 { usleep( 50000 ); }
618 return TRUE;
621 /*should we comment out what we just did?
622 i can't think of why it might be affecting this part of the vm
623 you never know
624 true*/
629 //-----------------------------------------------------------------------------
630 // name: compute()
631 // desc: ...
632 //-----------------------------------------------------------------------------
633 t_CKBOOL Chuck_VM::compute()
635 Chuck_VM_Shred *& shred = m_shreduler->m_current_shred;
636 Chuck_Msg * msg = NULL;
637 Chuck_Event * event = NULL;
638 t_CKBOOL iterate = TRUE;
640 // iteration until no more shreds/events/messages
641 while( iterate )
643 // get the shreds queued for 'now'
644 while(( shred = m_shreduler->get() ))
646 // set the current time of the shred
647 shred->now = shred->wake_time;
649 // track shred activation
650 CK_TRACK( Chuck_Stats::instance()->activate_shred( shred ) );
652 // run the shred
653 if( !shred->run( this ) )
655 // track shred deactivation
656 CK_TRACK( Chuck_Stats::instance()->deactivate_shred( shred ) );
658 this->free( shred, TRUE );
659 shred = NULL;
660 if( !m_num_shreds && m_halt ) return FALSE;
663 // track shred deactivation
664 CK_TRACK( if( shred ) Chuck_Stats::instance()->deactivate_shred( shred ) );
666 // zero out
667 shred = NULL;
670 // set to false for now
671 iterate = FALSE;
673 // broadcast queued events
674 while( m_event_buffer->get( &event, 1 ) )
675 { event->broadcast(); iterate = TRUE; }
677 // process messages
678 while( m_msg_buffer->get( &msg, 1 ) )
679 { process_msg( msg ); iterate = TRUE; }
681 // clear dumped shreds
682 if( m_num_dumped_shreds > 0 )
683 release_dump();
686 return TRUE;
692 //-----------------------------------------------------------------------------
693 // name: run()
694 // desc: ...
695 //-----------------------------------------------------------------------------
696 t_CKBOOL Chuck_VM::run( t_CKINT num_samps )
698 // loop it
699 while( num_samps )
701 // compute shreds
702 if( !compute() ) goto vm_stop;
704 // start audio
705 if( !m_audio_started ) start_audio();
707 // advance the shreduler
708 if( !m_shreduler->m_adaptive )
710 m_shreduler->advance();
711 if( num_samps > 0 ) num_samps--;
713 else m_shreduler->advance_v( num_samps );
716 return FALSE;
718 // vm stop here
719 vm_stop:
720 m_running = FALSE;
722 // log
723 EM_log( CK_LOG_SYSTEM, "virtual machine stopped..." );
725 return TRUE;
731 //-----------------------------------------------------------------------------
732 // name: pause()
733 // desc: ...
734 //-----------------------------------------------------------------------------
735 t_CKBOOL Chuck_VM::pause( )
737 m_running = FALSE;
739 return TRUE;
745 //-----------------------------------------------------------------------------
746 // name: stop()
747 // desc: ...
748 //-----------------------------------------------------------------------------
749 t_CKBOOL Chuck_VM::stop( )
751 // log
752 EM_log( CK_LOG_SEVERE, "requesting STOP virtual machine..." );
754 m_running = FALSE;
755 Digitalio::m_end = TRUE;
757 return TRUE;
763 //-----------------------------------------------------------------------------
764 // name: gc
765 // desc: ...
766 //-----------------------------------------------------------------------------
767 void Chuck_VM::gc( t_CKUINT amount )
774 //-----------------------------------------------------------------------------
775 // name: gc
776 // desc: ...
777 //-----------------------------------------------------------------------------
778 void Chuck_VM::gc( )
785 //-----------------------------------------------------------------------------
786 // name: queue_msg()
787 // desc: ...
788 //-----------------------------------------------------------------------------
789 t_CKBOOL Chuck_VM::queue_msg( Chuck_Msg * msg, int count )
791 assert( count == 1 );
792 m_msg_buffer->put( &msg, count );
793 return TRUE;
799 //-----------------------------------------------------------------------------
800 // name: queue_event()
801 // desc: ...
802 //-----------------------------------------------------------------------------
803 t_CKBOOL Chuck_VM::queue_event( Chuck_Event * event, int count )
805 assert( count == 1 );
806 m_event_buffer->put( &event, count );
807 return TRUE;
813 //-----------------------------------------------------------------------------
814 // name: get_reply()
815 // desc: ...
816 //-----------------------------------------------------------------------------
817 Chuck_Msg * Chuck_VM::get_reply( )
819 Chuck_Msg * msg = NULL;
820 m_reply_buffer->get( &msg, 1 );
821 return msg;
827 //-----------------------------------------------------------------------------
828 // name: process_msg()
829 // desc: ...
830 //-----------------------------------------------------------------------------
831 t_CKUINT Chuck_VM::process_msg( Chuck_Msg * msg )
833 t_CKUINT retval = 0xfffffff0;
835 if( msg->type == MSG_REPLACE )
837 Chuck_VM_Shred * out = m_shreduler->lookup( msg->param );
838 if( !out )
840 EM_error3( "[chuck](VM): error replacing shred: no shred with id %i...",
841 msg->param );
842 retval = 0;
843 goto done;
846 Chuck_VM_Shred * shred = msg->shred;
847 if( !shred )
849 shred = new Chuck_VM_Shred;
850 shred->initialize( msg->code );
851 shred->name = msg->code->name;
852 shred->base_ref = shred->mem;
853 shred->add_ref();
855 // set the current time
856 shred->start = m_shreduler->now_system;
857 // set the id
858 shred->xid = msg->param;
859 // set the now
860 shred->now = shred->wake_time = m_shreduler->now_system;
861 // set the vm
862 shred->vm_ref = this;
863 // set args
864 if( msg->args ) shred->args = *(msg->args);
865 // add it to the parent
866 if( shred->parent )
867 shred->parent->children[shred->xid] = shred;
869 // replace
870 if( m_shreduler->remove( out ) && m_shreduler->shredule( shred ) )
872 EM_error3( "[chuck](VM): replacing shred %i (%s) with %i (%s)...",
873 out->xid, mini(out->name.c_str()), shred->xid, mini(shred->name.c_str()) );
874 this->free( out, TRUE, FALSE );
875 retval = shred->xid;
877 // tracking new shred
878 CK_TRACK( Chuck_Stats::instance()->add_shred( shred ) );
880 goto done;
882 else
884 EM_error3( "[chuck](VM): shreduler ERROR replacing shred %i...",
885 out->xid );
886 shred->release();
887 retval = 0;
888 goto done;
891 else if( msg->type == MSG_REMOVE )
893 if( msg->param == 0xffffffff )
895 if( !this->m_num_shreds)
897 EM_error3( "[chuck](VM): no shreds to remove..." );
898 retval = 0;
899 goto done;
902 t_CKINT xid = m_shred_id;
903 Chuck_VM_Shred * shred = NULL;
904 while( xid >= 0 && m_shreduler->remove( shred = m_shreduler->lookup( xid ) ) == 0 )
905 xid--;
906 if( xid >= 0 )
908 EM_error3( "[chuck](VM): removing recent shred: %i (%s)...",
909 xid, mini(shred->name.c_str()) );
910 this->free( shred, TRUE );
911 retval = xid;
913 else
915 EM_error3( "[chuck](VM): no shreds removed..." );
916 retval = 0;
917 goto done;
920 else
922 Chuck_VM_Shred * shred = m_shreduler->lookup( msg->param );
923 if( !shred )
925 EM_error3( "[chuck](VM): cannot remove: no shred with id %i...",
926 msg->param );
927 retval = 0;
928 goto done;
930 if( shred != m_shreduler->m_current_shred && !m_shreduler->remove( shred ) ) // was lookup
932 EM_error3( "[chuck](VM): shreduler: cannot remove shred %i...",
933 msg->param );
934 retval = 0;
935 goto done;
937 EM_error3( "[chuck](VM): removing shred: %i (%s)...",
938 msg->param, mini(shred->name.c_str()) );
939 this->free( shred, TRUE );
940 retval = msg->param;
943 else if( msg->type == MSG_REMOVEALL )
945 t_CKUINT xid = m_shred_id;
946 EM_error3( "[chuck](VM): removing all (%i) shreds...", m_num_shreds );
947 Chuck_VM_Shred * shred = NULL;
949 while( m_num_shreds && xid > 0 )
951 if( m_shreduler->remove( shred = m_shreduler->lookup( xid ) ) )
952 this->free( shred, TRUE );
953 xid--;
956 m_shred_id = 0;
957 m_num_shreds = 0;
959 else if( msg->type == MSG_ADD )
961 t_CKUINT xid = 0;
962 Chuck_VM_Shred * shred = NULL;
963 if( msg->shred ) shred = this->spork( msg->shred );
964 else shred = this->spork( msg->code, NULL );
965 xid = shred->xid;
966 if( msg->args ) shred->args = *(msg->args);
968 const char * s = ( msg->shred ? msg->shred->name.c_str() : msg->code->name.c_str() );
969 EM_error3( "[chuck](VM): sporking incoming shred: %i (%s)...", xid, mini(s) );
970 retval = xid;
971 goto done;
973 else if( msg->type == MSG_KILL )
975 EM_error3( "[chuck](VM): KILL received...." );
976 // close file handles and clean up
977 all_detach();
978 // TODO: free more memory?
980 // log
981 EM_log( CK_LOG_INFO, "(VM): exiting..." );
982 // come again
983 exit( 1 );
985 else if( msg->type == MSG_STATUS )
987 // fill in structure
988 if( msg->user && msg->reply )
990 // cast
991 Chuck_VM_Status * status = (Chuck_VM_Status *)msg->user;
992 // get it
993 m_shreduler->status( status );
995 else
997 m_shreduler->status();
1000 else if( msg->type == MSG_TIME )
1002 float srate = (float)Digitalio::sampling_rate();
1003 fprintf( stderr, "[chuck](VM): the values of now:\n" );
1004 fprintf( stderr, " now = %.6f (samp)\n", m_shreduler->now_system );
1005 fprintf( stderr, " = %.6f (second)\n", m_shreduler->now_system / srate );
1006 fprintf( stderr, " = %.6f (minute)\n", m_shreduler->now_system / srate / 60.0f );
1007 fprintf( stderr, " = %.6f (hour)\n", m_shreduler->now_system / srate / 60.0f / 60.0f );
1008 fprintf( stderr, " = %.6f (day)\n", m_shreduler->now_system / srate / 60.0f / 60.0f / 24.0f );
1009 fprintf( stderr, " = %.6f (week)\n", m_shreduler->now_system / srate / 60.0f / 60.0f / 24.0f / 7.0f );
1011 else if( msg->type == MSG_RESET_ID )
1013 t_CKUINT n = m_shreduler->highest();
1014 m_shred_id = n;
1015 fprintf( stderr, "[chuck](VM): reseting shred id to %d...\n", m_shred_id + 1 );
1018 done:
1020 if( msg->reply )
1022 msg->replyA = retval;
1023 m_reply_buffer->put( &msg, 1 );
1025 else
1026 SAFE_DELETE(msg);
1028 return retval;
1034 //-----------------------------------------------------------------------------
1035 // name: next_id()
1036 // desc: ...
1037 //-----------------------------------------------------------------------------
1038 t_CKUINT Chuck_VM::next_id( )
1040 return ++m_shred_id;
1046 //-----------------------------------------------------------------------------
1047 // name: shreduler()
1048 // desc: ...
1049 //-----------------------------------------------------------------------------
1050 Chuck_VM_Shreduler * Chuck_VM::shreduler( ) const
1052 return m_shreduler;
1058 //-----------------------------------------------------------------------------
1059 // name: bbq()
1060 // desc: ...
1061 //-----------------------------------------------------------------------------
1062 BBQ * Chuck_VM::bbq( ) const
1064 return m_bbq;
1070 //-----------------------------------------------------------------------------
1071 // name: srate()
1072 // desc: ...
1073 //-----------------------------------------------------------------------------
1074 t_CKUINT Chuck_VM::srate() const
1076 return (t_CKUINT)Digitalio::sampling_rate();
1082 //-----------------------------------------------------------------------------
1083 // name: fork()
1084 // desc: ...
1085 //-----------------------------------------------------------------------------
1086 Chuck_VM_Shred * Chuck_VM::fork( Chuck_VM_Code * code )
1088 return NULL;
1094 //-----------------------------------------------------------------------------
1095 // name: spork()
1096 // desc: ...
1097 //-----------------------------------------------------------------------------
1098 Chuck_VM_Shred * Chuck_VM::spork( Chuck_VM_Code * code, Chuck_VM_Shred * parent )
1100 // allocate a new shred
1101 Chuck_VM_Shred * shred = new Chuck_VM_Shred;
1102 // initialize the shred (default stack size)
1103 shred->initialize( code );
1104 // set the name
1105 shred->name = code->name;
1106 // set the parent
1107 shred->parent = parent;
1108 // set the base ref for global
1109 if( parent ) shred->base_ref = shred->parent->base_ref;
1110 else shred->base_ref = shred->mem;
1111 // spork it
1112 this->spork( shred );
1114 // track new shred
1115 CK_TRACK( Chuck_Stats::instance()->add_shred( shred ) );
1117 return shred;
1123 //-----------------------------------------------------------------------------
1124 // name: spork()
1125 // desc: ...
1126 //-----------------------------------------------------------------------------
1127 Chuck_VM_Shred * Chuck_VM::spork( Chuck_VM_Shred * shred )
1129 // set the current time
1130 shred->start = m_shreduler->now_system;
1131 // set the now
1132 shred->now = shred->wake_time = m_shreduler->now_system;
1133 // set the id
1134 shred->xid = next_id();
1135 // set the vm
1136 shred->vm_ref = this;
1137 // add ref
1138 shred->add_ref();
1139 // add it to the parent
1140 if( shred->parent )
1141 shred->parent->children[shred->xid] = shred;
1142 // shredule it
1143 m_shreduler->shredule( shred );
1144 // count
1145 m_num_shreds++;
1147 return shred;
1153 //-----------------------------------------------------------------------------
1154 // name: free()
1155 // desc: ...
1156 //-----------------------------------------------------------------------------
1157 t_CKBOOL Chuck_VM::free( Chuck_VM_Shred * shred, t_CKBOOL cascade, t_CKBOOL dec )
1159 assert( cascade );
1161 // log
1162 EM_log( CK_LOG_FINER, "freeing shred (id==%d | ptr==%p)", shred->xid,
1163 (t_CKUINT)shred );
1165 // abort on the double free
1166 // TODO: can a shred be dumped, then resporked? from code?
1167 if( shred->is_dumped ) return FALSE;
1169 // mark this done
1170 shred->is_done = TRUE;
1172 // free the children
1173 t_CKINT size = shred->children.size();
1174 if( size )
1176 vector<Chuck_VM_Shred *> list; list.resize( size );
1177 map<t_CKUINT, Chuck_VM_Shred *>::iterator iter; t_CKINT i = 0;
1178 for( iter = shred->children.begin(); iter != shred->children.end(); iter++ )
1179 list[i++] = (*iter).second;
1180 for( i = 0; i < size; i++ )
1181 this->free( list[i], cascade );
1184 // make sure it's done
1185 assert( shred->children.size() == 0 );
1187 // tell parent
1188 if( shred->parent )
1189 shred->parent->children.erase( shred->xid );
1191 // track remove shred
1192 CK_TRACK( Chuck_Stats::instance()->remove_shred( shred ) );
1194 // free!
1195 m_shreduler->remove( shred );
1196 // TODO: remove shred from event, with synchronization (still necessary with dump?)
1197 // if( shred->event ) shred->event->remove( shred );
1198 // OLD: shred->release();
1199 this->dump( shred );
1200 shred = NULL;
1201 if( dec ) m_num_shreds--;
1202 if( !m_num_shreds ) m_shred_id = 0;
1204 return TRUE;
1210 //-----------------------------------------------------------------------------
1211 // name: abort_current_shred()
1212 // desc: ...
1213 //-----------------------------------------------------------------------------
1214 t_CKBOOL Chuck_VM::abort_current_shred( )
1216 // for threading
1217 Chuck_VM_Shred * shred = m_shreduler->m_current_shred;
1219 // if there
1220 if( shred )
1222 // log
1223 EM_log( CK_LOG_SEVERE, "trying to abort current shred (id: %d)", shred->xid );
1224 // flag it
1225 shred->is_abort = TRUE;
1227 else
1229 // log
1230 EM_log( CK_LOG_SEVERE, "cannot abort shred: nothing currently running!" );
1233 return shred != NULL;
1239 //-----------------------------------------------------------------------------
1240 // name: dump()
1241 // desc: ...
1242 //-----------------------------------------------------------------------------
1243 void Chuck_VM::dump( Chuck_VM_Shred * shred )
1245 // log
1246 EM_log( CK_LOG_FINER, "dumping shred (id==%d | ptr==%p)", shred->xid,
1247 (t_CKUINT)shred );
1248 // add
1249 m_shred_dump.push_back( shred );
1250 // stop
1251 shred->is_running = FALSE;
1252 shred->is_done = TRUE;
1253 shred->is_dumped = TRUE;
1254 // TODO: cool?
1255 shred->xid = 0;
1256 // inc
1257 m_num_dumped_shreds++;
1263 //-----------------------------------------------------------------------------
1264 // name: release_dump()
1265 // desc: ...
1266 //-----------------------------------------------------------------------------
1267 void Chuck_VM::release_dump( )
1269 // log
1270 EM_log( CK_LOG_FINER, "releasing dumped shreds..." );
1272 // iterate through dump
1273 for( t_CKUINT i = 0; i < m_shred_dump.size(); i++ )
1274 SAFE_RELEASE( m_shred_dump[i] );
1276 // clear the dump
1277 m_shred_dump.clear();
1278 // reset
1279 m_num_dumped_shreds = 0;
1285 //-----------------------------------------------------------------------------
1286 // name: Chuck_VM_Stack()
1287 // desc: ...
1288 //-----------------------------------------------------------------------------
1289 Chuck_VM_Stack::Chuck_VM_Stack()
1291 stack = sp = sp_max = NULL;
1292 prev = next = NULL;
1293 m_is_init = FALSE;
1299 //-----------------------------------------------------------------------------
1300 // name: ~Chuck_VM_Stack()
1301 // desc: ...
1302 //-----------------------------------------------------------------------------
1303 Chuck_VM_Stack::~Chuck_VM_Stack()
1305 this->shutdown();
1311 //-----------------------------------------------------------------------------
1312 // name: Chuck_VM_Code()
1313 // desc: ...
1314 //-----------------------------------------------------------------------------
1315 Chuck_VM_Code::Chuck_VM_Code()
1317 instr = NULL;
1318 num_instr = 0;
1319 stack_depth = 0;
1320 need_this = FALSE;
1321 native_func = 0;
1322 native_func_type = NATIVE_UNKNOWN;
1328 //-----------------------------------------------------------------------------
1329 // name: ~Chuck_VM_Code()
1330 // desc: ...
1331 //-----------------------------------------------------------------------------
1332 Chuck_VM_Code::~Chuck_VM_Code()
1334 // free instructions
1335 if( instr )
1337 // loop over array
1338 for( t_CKUINT i = 0; i < num_instr; i++ )
1339 delete instr[i];
1341 // free the array
1342 SAFE_DELETE_ARRAY( instr );
1345 num_instr = 0;
1351 // offset in bytes at the beginning of a stack for initializing data
1352 #define VM_STACK_OFFSET 16
1353 // 1/factor of stack is left blank, to give room to detect overflow
1354 #define VM_STACK_PADDING_FACTOR 16
1355 //-----------------------------------------------------------------------------
1356 // name: initialize()
1357 // desc: ...
1358 //-----------------------------------------------------------------------------
1359 t_CKBOOL Chuck_VM_Stack::initialize( t_CKUINT size )
1361 if( m_is_init )
1362 return FALSE;
1364 // make room for header
1365 size += VM_STACK_OFFSET;
1366 // allocate stack
1367 stack = new t_CKBYTE[size];
1368 if( !stack ) goto out_of_memory;
1370 // zero
1371 memset( stack, 0, size );
1373 // advance stack after the header
1374 stack += VM_STACK_OFFSET;
1375 // set the sp
1376 sp = stack;
1377 // upper limit (padding factor)
1378 sp_max = sp + size - (size / VM_STACK_PADDING_FACTOR);
1380 // set flag and return
1381 return m_is_init = TRUE;
1383 out_of_memory:
1385 // we have a problem
1386 fprintf( stderr,
1387 "[chuck](VM): OutOfMemory: while allocating stack '%s'\n" );
1389 // return FALSE
1390 return FALSE;
1396 //-----------------------------------------------------------------------------
1397 // name: shutdown()
1398 // desc: ...
1399 //-----------------------------------------------------------------------------
1400 t_CKBOOL Chuck_VM_Stack::shutdown()
1402 if( !m_is_init )
1403 return FALSE;
1405 // free the stack
1406 stack -= VM_STACK_OFFSET;
1407 SAFE_DELETE_ARRAY( stack );
1408 sp = sp_max = NULL;
1410 // set the flag to false
1411 m_is_init = FALSE;
1413 return TRUE;
1419 //-----------------------------------------------------------------------------
1420 // name: Chuck_VM_Shred()
1421 // desc: ...
1422 //-----------------------------------------------------------------------------
1423 Chuck_VM_Shred::Chuck_VM_Shred()
1425 mem = new Chuck_VM_Stack;
1426 reg = new Chuck_VM_Stack;
1427 code = NULL;
1428 next = prev = NULL;
1429 instr = NULL;
1430 parent = NULL;
1431 // obj_array = NULL;
1432 // obj_array_size = 0;
1433 base_ref = NULL;
1434 vm_ref = NULL;
1435 event = NULL;
1436 xid = 0;
1438 // set
1439 CK_TRACK( stat = NULL );
1445 //-----------------------------------------------------------------------------
1446 // name: ~Chuck_VM_Shred()
1447 // desc: ...
1448 //-----------------------------------------------------------------------------
1449 Chuck_VM_Shred::~Chuck_VM_Shred()
1451 this->shutdown();
1457 //-----------------------------------------------------------------------------
1458 // name: initialize()
1459 // desc: ...
1460 //-----------------------------------------------------------------------------
1461 t_CKBOOL Chuck_VM_Shred::initialize( Chuck_VM_Code * c,
1462 t_CKUINT mem_stack_size,
1463 t_CKUINT reg_stack_size )
1465 // allocate mem and reg
1466 if( !mem->initialize( mem_stack_size ) ) return FALSE;
1467 if( !reg->initialize( reg_stack_size ) ) return FALSE;
1469 // program counter
1470 pc = 0;
1471 next_pc = 1;
1472 // code pointer
1473 code_orig = code = c;
1474 // add reference
1475 code_orig->add_ref();
1476 // shred in dump (all done)
1477 is_dumped = FALSE;
1478 // shred done
1479 is_done = FALSE;
1480 // shred running
1481 is_running = FALSE;
1482 // shred abort
1483 is_abort = FALSE;
1484 // set the instr
1485 instr = c->instr;
1486 // zero out the id
1487 xid = 0;
1489 // initialize
1490 initialize_object( this, &t_shred );
1492 return TRUE;
1498 //-----------------------------------------------------------------------------
1499 // name: shutdown()
1500 // desc: ...
1501 //-----------------------------------------------------------------------------
1502 t_CKBOOL Chuck_VM_Shred::shutdown()
1504 // get iterator to our map
1505 map<Chuck_UGen *, Chuck_UGen *>::iterator iter = m_ugen_map.begin();
1506 while( iter != m_ugen_map.end() )
1508 (*iter).first->disconnect( TRUE );
1509 iter++;
1511 m_ugen_map.clear();
1513 SAFE_DELETE( mem );
1514 SAFE_DELETE( reg );
1515 base_ref = NULL;
1517 // delete temp pointer space
1518 // SAFE_DELETE_ARRAY( obj_array );
1519 // obj_array_size = 0;
1521 // TODO: is this right?
1522 code_orig->release();
1523 code_orig = code = NULL;
1524 // what to do with next and prev?
1526 return TRUE;
1532 //-----------------------------------------------------------------------------
1533 // name: add()
1534 // desc: ...
1535 //-----------------------------------------------------------------------------
1536 t_CKBOOL Chuck_VM_Shred::add( Chuck_UGen * ugen )
1538 if( m_ugen_map[ugen] )
1539 return FALSE;
1541 m_ugen_map[ugen] = ugen;
1542 return TRUE;
1548 //-----------------------------------------------------------------------------
1549 // name: remove()
1550 // desc: ...
1551 //-----------------------------------------------------------------------------
1552 t_CKBOOL Chuck_VM_Shred::remove( Chuck_UGen * ugen )
1554 if( !m_ugen_map[ugen] )
1555 return FALSE;
1557 // remove it
1558 m_ugen_map.erase( ugen );
1559 return TRUE;
1565 //-----------------------------------------------------------------------------
1566 // name: run()
1567 // desc: ...
1568 //-----------------------------------------------------------------------------
1569 t_CKBOOL Chuck_VM_Shred::run( Chuck_VM * vm )
1571 // get the code
1572 instr = code->instr;
1573 is_running = TRUE;
1574 t_CKBOOL * vm_running = &vm->m_running;
1576 // go!
1577 while( is_running && *vm_running && !is_abort )
1579 // execute the instruction
1580 instr[pc]->execute( vm, this );
1582 // set to next_pc;
1583 pc = next_pc;
1584 next_pc++;
1586 // track number of cycles
1587 CK_TRACK( this->stat->cycles++ );
1590 // check abort
1591 if( is_abort )
1593 // log
1594 EM_log( CK_LOG_SYSTEM, "aborting shred (id: %d)", this->xid );
1595 // done
1596 is_done = TRUE;
1599 // is the shred finished
1600 return !is_done;
1606 //-----------------------------------------------------------------------------
1607 // name: Chuck_VM_Shreduler()
1608 // desc: ...
1609 //-----------------------------------------------------------------------------
1610 Chuck_VM_Shreduler::Chuck_VM_Shreduler()
1612 now_system = 0;
1613 rt_audio = FALSE;
1614 bbq = NULL;
1615 shred_list = NULL;
1616 m_current_shred = NULL;
1617 m_dac = NULL;
1618 m_adc = NULL;
1619 m_bunghole = NULL;
1620 m_num_dac_channels = 0;
1621 m_num_adc_channels = 0;
1623 set_adaptive( 0 );
1629 //-----------------------------------------------------------------------------
1630 // name: ~Chuck_VM_Shreduler()
1631 // desc: ...
1632 //-----------------------------------------------------------------------------
1633 Chuck_VM_Shreduler::~Chuck_VM_Shreduler()
1635 this->shutdown();
1641 //-----------------------------------------------------------------------------
1642 // name: initialize()
1643 // desc: ...
1644 //-----------------------------------------------------------------------------
1645 t_CKBOOL Chuck_VM_Shreduler::initialize()
1647 return TRUE;
1653 //-----------------------------------------------------------------------------
1654 // name: shutdown()
1655 // desc: ...
1656 //-----------------------------------------------------------------------------
1657 t_CKBOOL Chuck_VM_Shreduler::shutdown()
1659 return TRUE;
1665 //-----------------------------------------------------------------------------
1666 // name: set_adaptive()
1667 // desc: ...
1668 //-----------------------------------------------------------------------------
1669 void Chuck_VM_Shreduler::set_adaptive( t_CKUINT max_block_size )
1671 m_max_block_size = max_block_size > 1 ? max_block_size : 0;
1672 m_adaptive = m_max_block_size > 1;
1673 m_samps_until_next = -1;
1679 //-----------------------------------------------------------------------------
1680 // name: add_blocked()
1681 // desc: add shred to the shreduler's blocked list
1682 //-----------------------------------------------------------------------------
1683 t_CKBOOL Chuck_VM_Shreduler::add_blocked( Chuck_VM_Shred * shred )
1685 // add shred to map, using pointer
1686 blocked[shred] = shred;
1688 return TRUE;
1694 //-----------------------------------------------------------------------------
1695 // name: remove_blocked()
1696 // desc: remove shred from the shreduler's blocked list
1697 //-----------------------------------------------------------------------------
1698 t_CKBOOL Chuck_VM_Shreduler::remove_blocked( Chuck_VM_Shred * shred )
1700 // remove from hash
1701 std::map<Chuck_VM_Shred *, Chuck_VM_Shred *>::iterator iter;
1702 iter = blocked.find( shred );
1703 blocked.erase( iter );
1705 // remove from event
1706 if( shred->event != NULL ) shred->event->remove( shred );
1708 return TRUE;
1714 //-----------------------------------------------------------------------------
1715 // name: shredule()
1716 // desc: ...
1717 //-----------------------------------------------------------------------------
1718 t_CKBOOL Chuck_VM_Shreduler::shredule( Chuck_VM_Shred * shred )
1720 return this->shredule( shred, now_system );
1726 //-----------------------------------------------------------------------------
1727 // name: shredule()
1728 // desc: ...
1729 //-----------------------------------------------------------------------------
1730 t_CKBOOL Chuck_VM_Shreduler::shredule( Chuck_VM_Shred * shred,
1731 t_CKTIME wake_time )
1733 // sanity check
1734 if( shred->prev || shred->next )
1736 // something is really wrong here - no shred can be
1737 // shreduled more than once
1738 EM_error3( "[chuck](VM): internal sanity check failed in shredule()" );
1739 EM_error3( "[chuck](VM): (shred shreduled while shreduled)" );
1741 return FALSE;
1744 // sanity check
1745 if( wake_time < (this->now_system - .5) )
1747 // trying to enqueue on a time that is less than now
1748 EM_error3( "[chuck](VM): internal sanity check failed in shredule()" );
1749 EM_error3( "[chuck](VM): (wake time is past) - %f : %f", wake_time, this->now_system );
1751 return FALSE;
1754 shred->wake_time = wake_time;
1756 // list empty
1757 if( !shred_list )
1758 shred_list = shred;
1759 else
1761 // pointers to the shred queue
1762 Chuck_VM_Shred * curr = shred_list;
1763 Chuck_VM_Shred * prev = NULL;
1765 while( curr )
1767 // found the place to insert
1768 if( curr->wake_time > wake_time )
1769 break;
1771 prev = curr;
1772 curr = curr->next;
1775 if( !prev )
1777 shred->next = shred_list;
1778 if( shred_list ) shred_list->prev = shred;
1779 shred_list = shred;
1781 else
1783 // insert the shred in sorted order
1784 shred->next = prev->next;
1785 shred->prev = prev;
1786 if( prev->next ) prev->next->prev = shred;
1787 prev->next = shred;
1791 t_CKTIME diff = shred_list->wake_time - this->now_system;
1792 if( diff < 0 ) diff = 0;
1793 // if( diff < m_samps_until_next )
1794 m_samps_until_next = diff;
1796 return TRUE;
1802 //-----------------------------------------------------------------------------
1803 // name: advance_v()
1804 // desc: ...
1805 //-----------------------------------------------------------------------------
1806 void Chuck_VM_Shreduler::advance_v( t_CKINT & numLeft )
1808 t_CKINT i, j, numFrames;
1809 SAMPLE frame[128], gain[128], sum;
1810 BBQ * audio = this->bbq;
1812 // compute number of frames to compute; update
1813 numFrames = ck_min( m_max_block_size, numLeft );
1814 if( this->m_samps_until_next >= 0 )
1816 numFrames = (t_CKINT)(ck_min( numFrames, this->m_samps_until_next ));
1817 if( numFrames == 0 ) numFrames = 1;
1818 this->m_samps_until_next -= numFrames;
1820 numLeft -= numFrames;
1822 // advance system 'now'
1823 this->now_system += numFrames;
1825 // tick in
1826 if( rt_audio )
1828 for( j = 0; j < m_num_adc_channels; j++ )
1830 // update channel
1831 m_adc->m_multi_chan[j]->m_time = this->now_system;
1832 // cache gain
1833 gain[j] = m_adc->m_multi_chan[j]->m_gain;
1836 // adaptive block
1837 for( i = 0; i < numFrames; i++ )
1839 // get input
1840 audio->digi_in()->tick_in( frame, m_num_adc_channels );
1841 // clear
1842 sum = 0.0f;
1843 // loop over channels
1844 for( j = 0; j < m_num_adc_channels; j++ )
1846 m_adc->m_multi_chan[j]->m_current_v[i] = frame[j] * gain[j] * m_adc->m_gain;
1847 sum += m_adc->m_multi_chan[j]->m_current_v[i];
1849 m_adc->m_current_v[i] = sum / m_num_adc_channels;
1852 for( j = 0; j < m_num_adc_channels; j++ )
1854 // update last
1855 m_adc->m_multi_chan[j]->m_last = m_adc->m_multi_chan[j]->m_current_v[numFrames-1];
1857 // update last
1858 m_adc->m_last = m_adc->m_current_v[numFrames-1];
1859 // update time
1860 m_adc->m_time = this->now_system;
1863 // dac
1864 m_dac->system_tick_v( this->now_system, numFrames );
1866 // suck samples
1867 m_bunghole->system_tick_v( this->now_system, numFrames );
1869 // adaptive block
1870 for( i = 0; i < numFrames; i++ )
1872 for( j = 0; j < m_num_dac_channels; j++ )
1873 frame[j] = m_dac->m_multi_chan[j]->m_current_v[i];
1875 // tick
1876 audio->digi_out()->tick_out( frame, m_num_dac_channels );
1883 //-----------------------------------------------------------------------------
1884 // name: advance2()
1885 // desc: ...
1886 //-----------------------------------------------------------------------------
1887 void Chuck_VM_Shreduler::advance2( )
1889 // advance system 'now'
1890 this->now_system += 1;
1892 // tick the dac
1893 SAMPLE l, r;
1894 BBQ * audio = this->bbq;
1896 // tick in
1897 if( rt_audio )
1899 audio->digi_in()->tick_in( &l, &r );
1900 m_adc->m_multi_chan[0]->m_current = l * m_adc->m_multi_chan[0]->m_gain;
1901 m_adc->m_multi_chan[1]->m_current = r * m_adc->m_multi_chan[1]->m_gain;
1902 m_adc->m_current = .5f * ( l + r );
1903 // time it
1904 m_adc->m_multi_chan[0]->m_time = this->now_system;
1905 m_adc->m_multi_chan[1]->m_time = this->now_system;
1906 m_adc->m_time = this->now_system;
1909 // dac
1910 m_dac->system_tick( this->now_system );
1911 l = m_dac->m_multi_chan[0]->m_current;
1912 r = m_dac->m_multi_chan[1]->m_current;
1913 // remove: 1.2.1.2
1914 // l *= .5f; r *= .5f;
1916 // suck samples
1917 m_bunghole->system_tick( this->now_system );
1919 // tick
1920 audio->digi_out()->tick_out( l, r );
1926 //-----------------------------------------------------------------------------
1927 // name: advance()
1928 // desc: ...
1929 //-----------------------------------------------------------------------------
1930 void Chuck_VM_Shreduler::advance( )
1932 // advance system 'now'
1933 this->now_system += 1;
1935 // tick the dac
1936 SAMPLE frame[128];
1937 SAMPLE sum = 0.0f;
1938 BBQ * audio = this->bbq;
1939 t_CKUINT i;
1941 // tick in
1942 if( rt_audio )
1944 audio->digi_in()->tick_in( frame, m_num_adc_channels );
1946 // loop over channels
1947 for( i = 0; i < m_num_adc_channels; i++ )
1949 m_adc->m_multi_chan[i]->m_current = frame[i] * m_adc->m_multi_chan[i]->m_gain * m_adc->m_gain;
1950 m_adc->m_multi_chan[i]->m_last = m_adc->m_multi_chan[i]->m_current;
1951 m_adc->m_multi_chan[i]->m_time = this->now_system;
1952 sum += m_adc->m_multi_chan[i]->m_current;
1954 m_adc->m_last = m_adc->m_current = sum / m_num_adc_channels;
1955 m_adc->m_time = this->now_system;
1958 // dac
1959 m_dac->system_tick( this->now_system );
1960 for( i = 0; i < m_num_dac_channels; i++ )
1961 frame[i] = m_dac->m_multi_chan[i]->m_current; // * .5f;
1963 // suck samples
1964 m_bunghole->system_tick( this->now_system );
1966 // tick
1967 audio->digi_out()->tick_out( frame, m_num_dac_channels );
1973 //-----------------------------------------------------------------------------
1974 // name: get()
1975 // desc: ...
1976 //-----------------------------------------------------------------------------
1977 Chuck_VM_Shred * Chuck_VM_Shreduler::get( )
1979 Chuck_VM_Shred * shred = shred_list;
1981 // list empty
1982 if( !shred )
1984 m_samps_until_next = -1;
1985 return NULL;
1988 // TODO: should this be <=?
1989 if( shred->wake_time <= ( this->now_system + .5 ) )
1991 // if( shred->wake_time < this->now_system )
1992 // assert( false );
1994 shred_list = shred->next;
1995 shred->next = NULL;
1996 shred->prev = NULL;
1998 if( shred_list )
2000 shred_list->prev = NULL;
2001 m_samps_until_next = shred_list->wake_time - this->now_system;
2002 if( m_samps_until_next < 0 ) m_samps_until_next = 0;
2005 return shred;
2008 return NULL;
2014 //-----------------------------------------------------------------------------
2015 // name: highest()
2016 // desc: ...
2017 //-----------------------------------------------------------------------------
2018 t_CKUINT Chuck_VM_Shreduler::highest( )
2020 Chuck_VM_Shred * shred = shred_list;
2021 t_CKUINT n = 0;
2023 while( shred )
2025 if( shred->xid > n ) n = shred->xid;
2026 shred = shred->next;
2029 std::map<Chuck_VM_Shred *, Chuck_VM_Shred *>::iterator iter;
2030 for( iter = blocked.begin(); iter != blocked.end(); iter++ )
2032 shred = (*iter).second;
2033 if( shred->xid > n ) n = shred->xid;
2036 return n;
2042 //-----------------------------------------------------------------------------
2043 // name: replace()
2044 // desc: ...
2045 //-----------------------------------------------------------------------------
2046 t_CKBOOL Chuck_VM_Shreduler::replace( Chuck_VM_Shred * out, Chuck_VM_Shred * in )
2048 assert( FALSE );
2050 // sanity check
2051 if( !out || !in )
2052 return FALSE;
2054 if( !out->prev )
2055 shred_list = in;
2056 else
2057 out->prev->next = in;
2059 if( out->next )
2060 out->next->prev = in;
2062 in->next = out->next;
2063 in->prev = out->prev;
2065 out->next = out->prev = NULL;
2067 in->wake_time = out->wake_time;
2068 in->start = in->wake_time;
2070 return TRUE;
2076 //-----------------------------------------------------------------------------
2077 // name: remove()
2078 // desc: ...
2079 //-----------------------------------------------------------------------------
2080 t_CKBOOL Chuck_VM_Shreduler::remove( Chuck_VM_Shred * out )
2082 if( !out ) return FALSE;
2084 // if blocked
2085 if( out->event != NULL )
2087 return remove_blocked( out );
2090 // sanity check
2091 if( !out->prev && !out->next && out != shred_list )
2092 return FALSE;
2094 if( !out->prev )
2095 shred_list = out->next;
2096 else
2097 out->prev->next = out->next;
2099 if( out->next )
2100 out->next->prev = out->prev;
2102 out->next = out->prev = NULL;
2104 return TRUE;
2110 //-----------------------------------------------------------------------------
2111 // name: get()
2112 // desc: ...
2113 //-----------------------------------------------------------------------------
2114 Chuck_VM_Shred * Chuck_VM_Shreduler::lookup( t_CKUINT xid )
2116 Chuck_VM_Shred * shred = shred_list;
2118 // current shred?
2119 if( m_current_shred != NULL && m_current_shred->xid == xid )
2120 return m_current_shred;
2122 // look for in shreduled list
2123 while( shred )
2125 if( shred->xid == xid )
2126 return shred;
2128 shred = shred->next;
2131 // blocked?
2132 std::map<Chuck_VM_Shred *, Chuck_VM_Shred *>::iterator iter;
2133 for( iter = blocked.begin(); iter != blocked.end(); iter++ )
2135 shred = (*iter).second;
2136 if( shred->xid == xid )
2137 return shred;
2140 return NULL;
2146 //-----------------------------------------------------------------------------
2147 // name: SortByID()
2148 // desc: ...
2149 //-----------------------------------------------------------------------------
2150 struct SortByID
2152 bool operator() ( const Chuck_VM_Shred * lhs, const Chuck_VM_Shred * rhs )
2153 { return lhs->xid < rhs->xid; }
2159 //-----------------------------------------------------------------------------
2160 // name: status()
2161 // desc: ...
2162 //-----------------------------------------------------------------------------
2163 void Chuck_VM_Shreduler::status( Chuck_VM_Status * status )
2165 Chuck_VM_Shred * shred = shred_list;
2166 Chuck_VM_Shred * temp = NULL;
2168 t_CKUINT srate = Digitalio::sampling_rate();
2169 t_CKUINT s = (t_CKUINT)now_system;
2170 t_CKUINT h = s/(srate*3600);
2171 s = s - (h*(srate*3600));
2172 t_CKUINT m = s / (srate*60);
2173 s = s - (m*(srate*60));
2174 t_CKUINT sec = s / srate;
2175 s = s - (sec*(srate));
2176 // float millisecond = s / (float)(srate) * 1000.0f;
2178 status->srate = srate;
2179 status->now_system = now_system;
2180 status->t_second = sec;
2181 status->t_minute = m;
2182 status->t_hour = h;
2184 // get list of shreds
2185 vector<Chuck_VM_Shred *> list;
2186 while( shred )
2188 list.push_back( shred );
2189 shred = shred->next;
2192 // get blocked
2193 std::map<Chuck_VM_Shred *, Chuck_VM_Shred *>::iterator iter;
2194 for( iter = blocked.begin(); iter != blocked.end(); iter++ )
2196 shred = (*iter).second;
2197 list.push_back( shred );
2200 // get current shred
2201 if( temp = m_current_shred )
2202 list.push_back( temp );
2204 // sort the list
2205 SortByID byid;
2206 std::sort( list.begin(), list.end(), byid );
2208 // print status
2209 status->clear();
2210 for( t_CKUINT i = 0; i < list.size(); i++ )
2212 shred = list[i];
2213 status->list.push_back( new Chuck_VM_Shred_Status(
2214 shred->xid, shred->name, shred->start, shred->event != NULL ) );
2221 //-----------------------------------------------------------------------------
2222 // name: status()
2223 // desc: ...
2224 //-----------------------------------------------------------------------------
2225 void Chuck_VM_Shreduler::status( )
2227 Chuck_VM_Shred_Status * shred = NULL;
2229 this->status( &m_status );
2230 t_CKUINT h = m_status.t_hour;
2231 t_CKUINT m = m_status.t_minute;
2232 t_CKUINT sec = m_status.t_second;
2233 fprintf( stdout, "[chuck](VM): status (now == %ldh%ldm%lds, %.1f samps) ...\n",
2234 h, m, sec, m_status.now_system );
2236 // print status
2237 for( t_CKUINT i = 0; i < m_status.list.size(); i++ )
2239 shred = m_status.list[i];
2240 fprintf( stdout,
2241 " [shred id]: %ld [source]: %s [spork time]: %.2fs ago%s\n",
2242 shred->xid, mini( shred->name.c_str() ),
2243 (m_status.now_system - shred->start) / m_status.srate,
2244 shred->has_event ? " (blocked)" : "" );
2251 //-----------------------------------------------------------------------------
2252 // name: Chuck_VM_Status()
2253 // desc: ...
2254 //-----------------------------------------------------------------------------
2255 Chuck_VM_Status::Chuck_VM_Status()
2257 srate = 0;
2258 now_system = 0;
2259 t_second = t_minute = t_hour = 0;
2265 //-----------------------------------------------------------------------------
2266 // name: ~Chuck_VM_Status()
2267 // desc: ...
2268 //-----------------------------------------------------------------------------
2269 Chuck_VM_Status::~Chuck_VM_Status()
2271 this->clear();
2277 //-----------------------------------------------------------------------------
2278 // name: clear()
2279 // desc: ...
2280 //-----------------------------------------------------------------------------
2281 void Chuck_VM_Status::clear()
2283 for( t_CKUINT i = 0; i < list.size(); i++ )
2285 SAFE_DELETE( list[i] );
2288 list.clear();