1 /*----------------------------------------------------------------------------
2 ChucK Concurrent, On-the-fly Audio Programming Language
3 Compiler and Virtual Machine
5 Copyright (c) 2004 Ge Wang and Perry R. Cook. All rights reserved.
6 http://chuck.cs.princeton.edu/
7 http://soundlab.cs.princeton.edu/
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 -----------------------------------------------------------------------------*/
25 //-----------------------------------------------------------------------------
29 // authors: Ge Wang (gewang@cs.princeton.edu)
30 // Perry R. Cook (prc@cs.princeton.edu)
32 //-----------------------------------------------------------------------------
34 #include "chuck_instr.h"
35 #include "chuck_bbq.h"
36 #include "chuck_errmsg.h"
38 #include "chuck_type.h"
39 #include "chuck_globals.h"
40 #include "chuck_lang.h"
45 #if defined(__PLATFORM_WIN32__)
55 //-----------------------------------------------------------------------------
56 // name: struct Chuck_VM_Frame
58 //-----------------------------------------------------------------------------
65 Chuck_VM_Frame() { size
= 0; }
72 //-----------------------------------------------------------------------------
73 // name: struct Chuck_VM_Func
75 //-----------------------------------------------------------------------------
80 Chuck_VM_Frame
* frame
;
84 Chuck_VM_Func() { code
= NULL
; frame
= NULL
; index
= 0; }
91 //-----------------------------------------------------------------------------
92 // name: struct Chuck_VM_FTable
93 // desc: function table
94 //-----------------------------------------------------------------------------
95 struct Chuck_VM_FTable
102 Chuck_VM_Func
* get_func( t_CKUINT index
);
103 t_CKUINT
append( Chuck_VM_Func
* f
);
104 t_CKBOOL
remove( t_CKUINT index
);
107 vector
<Chuck_VM_Func
*> func_table
;
113 //-----------------------------------------------------------------------------
116 //-----------------------------------------------------------------------------
122 m_num_dumped_shreds
= 0;
124 m_reply_buffer
= NULL
;
125 m_event_buffer
= NULL
;
132 m_audio_started
= FALSE
;
136 m_num_dac_channels
= 0;
137 m_num_adc_channels
= 0;
144 //-----------------------------------------------------------------------------
147 //-----------------------------------------------------------------------------
148 Chuck_VM::~Chuck_VM()
150 if( m_init
) shutdown();
155 //UGEN_TICK __dac_tick( Chuck_Object * SELF, SAMPLE in, SAMPLE * out )
156 //{ *out = in; return TRUE; }
157 //UGEN_TICK __bunghole_tick( Chuck_Object * SELF, SAMPLE in, SAMPLE * out )
158 //{ *out = 0.0f; return TRUE; }
162 #ifdef __MACOSX_CORE__
163 t_CKINT
Chuck_VM::our_priority
= 85;
165 t_CKINT
Chuck_VM::our_priority
= 0x7fffffff;
169 #if !defined(__PLATFORM_WIN32__) || defined(__WINDOWS_PTHREAD__)
170 //-----------------------------------------------------------------------------
171 // name: set_priority()
173 //-----------------------------------------------------------------------------
174 t_CKBOOL
Chuck_VM::set_priority( t_CKINT priority
, Chuck_VM
* vm
)
176 struct sched_param param
;
177 pthread_t tid
= pthread_self();
181 EM_log( CK_LOG_INFO
, "setting thread priority to: %ld...", priority
);
184 if( pthread_getschedparam( tid
, &policy
, ¶m
) )
187 vm
->m_last_error
= "could not get current scheduling parameters";
192 param
.sched_priority
= priority
;
196 if( pthread_setschedparam( tid
, policy
, ¶m
) )
199 vm
->m_last_error
= "could not set new scheduling parameters";
206 //-----------------------------------------------------------------------------
207 // name: set_priority()
209 //-----------------------------------------------------------------------------
210 t_CKBOOL
Chuck_VM::set_priority( t_CKINT priority
, Chuck_VM
* vm
)
212 // if priority is 0 then done
213 if( !priority
) return TRUE
;
215 // set the priority class of the process
216 // if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) )
220 EM_log( CK_LOG_INFO
, "setting thread priority to: %ld...", priority
);
222 // set the priority the thread
223 // if( !SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL ) )
224 if( !SetThreadPriority( GetCurrentThread(), priority
) )
227 vm
->m_last_error
= "could not set new scheduling parameters";
238 //-----------------------------------------------------------------------------
239 // name: initialize()
241 //-----------------------------------------------------------------------------
242 t_CKBOOL
Chuck_VM::initialize( t_CKBOOL enable_audio
, t_CKBOOL halt
, t_CKUINT srate
,
243 t_CKUINT buffer_size
, t_CKUINT num_buffers
,
244 t_CKUINT dac
, t_CKUINT adc
, t_CKUINT dac_chan
,
245 t_CKUINT adc_chan
, t_CKBOOL block
)
249 m_last_error
= "VM already initialized!";
253 // boost thread priority
254 // if( priority != 0x7fffffff && !set_priority( priority, this ) )
258 EM_log( CK_LOG_SYSTEM
, "initializing virtual machine..." );
259 EM_pushlog(); // push stack
260 EM_log( CK_LOG_SYSTEM
, "behavior: %s", halt
? "HALT" : "LOOP" );
263 Chuck_VM_Object::lock_all();
268 m_audio
= enable_audio
;
272 EM_log( CK_LOG_SYSTEM
, "allocating shreduler..." );
273 // allocate shreduler
274 m_shreduler
= new Chuck_VM_Shreduler
;
275 m_shreduler
->bbq
= m_bbq
;
276 m_shreduler
->rt_audio
= enable_audio
;
279 EM_log( CK_LOG_SYSTEM
, "allocating messaging buffers..." );
280 // allocate msg buffer
281 m_msg_buffer
= new CBufferSimple
;
282 m_msg_buffer
->initialize( 1024, sizeof(Chuck_Msg
*) );
283 //m_msg_buffer->join(); // this should return 0
284 m_reply_buffer
= new CBufferSimple
;
285 m_reply_buffer
->initialize( 1024, sizeof(Chuck_Msg
*) );
286 //m_reply_buffer->join(); // this should return 0 too
287 m_event_buffer
= new CBufferSimple
;
288 m_event_buffer
->initialize( 1024, sizeof(Chuck_Event
*) );
289 //m_event_buffer->join(); // this should also return 0
292 EM_log( CK_LOG_SYSTEM
, "real-time audio: %s", enable_audio
? "YES" : "NO" );
293 EM_log( CK_LOG_SYSTEM
, "mode: %s", block
? "BLOCKING" : "CALLBACK" );
294 EM_log( CK_LOG_SYSTEM
, "sample rate: %ld", srate
);
295 EM_log( CK_LOG_SYSTEM
, "buffer size: %ld", buffer_size
);
298 EM_log( CK_LOG_SYSTEM
, "num buffers: %ld", num_buffers
);
299 EM_log( CK_LOG_SYSTEM
, "devices adc: %ld dac: %d (default 0)", adc
, dac
);
301 EM_log( CK_LOG_SYSTEM
, "channels in: %ld out: %ld", adc_chan
, dac_chan
);
302 m_num_adc_channels
= adc_chan
;
303 m_num_dac_channels
= dac_chan
;
305 // at least set the sample rate and buffer size
306 m_bbq
->set_srate( srate
);
307 m_bbq
->set_bufsize( buffer_size
);
308 m_bbq
->set_numbufs( num_buffers
);
309 m_bbq
->set_inouts( adc
, dac
);
310 m_bbq
->set_chans( adc_chan
, dac_chan
);
315 // TODO: clean up all the dynamic objects here on failure
316 // and in the shutdown function!
318 return m_init
= TRUE
;
324 //-----------------------------------------------------------------------------
325 // name: initialize_synthesis()
326 // desc: requires type system
327 //-----------------------------------------------------------------------------
328 t_CKBOOL
Chuck_VM::initialize_synthesis( )
332 m_last_error
= "VM initialize_synthesis() called on raw VM";
336 if( !g_t_dac
|| !g_t_adc
)
338 m_last_error
= "VM initialize_synthesis() called before type system initialized";
344 m_last_error
= "VM synthesis already initialized";
349 EM_log( CK_LOG_SYSTEM
, "initializing synthesis engine..." );
354 EM_log( CK_LOG_SEVERE
, "initializing 'dac'..." );
355 // allocate dac and adc
356 g_t_dac
->ugen_info
->num_outs
=
357 g_t_dac
->ugen_info
->num_ins
= m_num_dac_channels
;
358 m_dac
= (Chuck_UGen
*)instantiate_and_initialize_object( g_t_dac
, NULL
);
359 object_ctor( m_dac
, NULL
, NULL
); // TODO: this can't be the place to do this
360 stereo_ctor( m_dac
, NULL
, NULL
); // TODO: is the NULL shred a problem?
361 multi_ctor( m_dac
, NULL
, NULL
); // TODO: remove and let type system do this
367 EM_log( CK_LOG_SEVERE
, "initializing 'adc'..." );
368 g_t_adc
->ugen_info
->num_ins
=
369 g_t_adc
->ugen_info
->num_outs
= m_num_adc_channels
;
370 m_adc
= (Chuck_UGen
*)instantiate_and_initialize_object( g_t_adc
, NULL
);
371 object_ctor( m_adc
, NULL
, NULL
); // TODO: this can't be the place to do this
372 stereo_ctor( m_adc
, NULL
, NULL
);
373 multi_ctor( m_adc
, NULL
, NULL
); // TODO: remove and let type system do this
379 EM_log( CK_LOG_SEVERE
, "initializing 'blackhole'..." );
380 m_bunghole
= new Chuck_UGen
;
381 m_bunghole
->add_ref();
383 initialize_object( m_bunghole
, &t_ugen
);
384 m_bunghole
->tick
= NULL
;
385 m_shreduler
->m_dac
= m_dac
;
386 m_shreduler
->m_adc
= m_adc
;
387 m_shreduler
->m_bunghole
= m_bunghole
;
388 m_shreduler
->m_num_dac_channels
= m_num_dac_channels
;
389 m_shreduler
->m_num_adc_channels
= m_num_adc_channels
;
392 EM_log( CK_LOG_SYSTEM
, "initializing '%s' audio...", m_audio
? "real-time" : "fake-time" );
394 if( !m_bbq
->initialize( m_num_dac_channels
, m_num_adc_channels
,
395 Digitalio::m_sampling_rate
, 16,
396 Digitalio::m_buffer_size
, Digitalio::m_num_buffers
,
397 Digitalio::m_dac_n
, Digitalio::m_adc_n
,
398 m_block
, this, m_audio
) )
400 m_last_error
= "cannot initialize audio device (try using --silent/-s)";
415 //-----------------------------------------------------------------------------
416 // name: compensate_bbq()
418 //-----------------------------------------------------------------------------
419 void Chuck_VM::compensate_bbq()
421 // set shreduler - the audio was initialized elsewhere
422 m_shreduler
->bbq
= m_bbq
;
428 //-----------------------------------------------------------------------------
431 //-----------------------------------------------------------------------------
432 t_CKBOOL
Chuck_VM::shutdown()
434 // make sure we are in the initialized state
435 if( !m_init
) return FALSE
;
438 EM_log( CK_LOG_SYSTEM
, "shutting down virtual machine..." );
442 Chuck_VM_Object::unlock_all();
455 EM_log( CK_LOG_SYSTEM
, "shutting down real-time audio..." );
457 m_bbq
->digi_out()->cleanup();
458 m_bbq
->digi_in()->cleanup();
463 EM_log( CK_LOG_SYSTEM
, "freeing bbq subsystem..." );
465 SAFE_DELETE( m_bbq
);
468 EM_log( CK_LOG_SYSTEM
, "freeing shreduler..." );
469 // free the shreduler
470 SAFE_DELETE( m_shreduler
);
473 EM_log( CK_LOG_SYSTEM
, "freeing msg/reply/event buffers..." );
474 // free the msg buffer
475 SAFE_DELETE( m_msg_buffer
);
476 // free the reply buffer
477 SAFE_DELETE( m_reply_buffer
);
478 // free the event buffer
479 SAFE_DELETE( m_event_buffer
);
482 EM_log( CK_LOG_SEVERE
, "clearing shreds..." );
484 Chuck_VM_Shred
* curr
= m_shreds
, * prev
= NULL
;
497 EM_log( CK_LOG_SEVERE
, "freeing dumped shreds..." );
499 this->release_dump();
503 EM_log( CK_LOG_SYSTEM
, "freeing special ugens..." );
505 SAFE_RELEASE( m_dac
);
506 SAFE_RELEASE( m_adc
);
507 SAFE_RELEASE( m_bunghole
);
520 //-----------------------------------------------------------------------------
521 // name: start_audio()
523 //-----------------------------------------------------------------------------
524 t_CKBOOL
Chuck_VM::start_audio( )
527 if( !m_audio_started
&& m_audio
)
529 EM_log( CK_LOG_SEVERE
, "starting real-time audio..." );
530 m_bbq
->digi_out()->start();
531 m_bbq
->digi_in()->start();
534 // set the flag to true to avoid entering this function
535 m_audio_started
= TRUE
;
543 //-----------------------------------------------------------------------------
546 //-----------------------------------------------------------------------------
547 t_CKBOOL
Chuck_VM::run( )
552 m_last_error
= "VM and/or synthesis not initialized...";
556 // check if already running
559 m_last_error
= "virtual machine already running...";
566 EM_log( CK_LOG_SYSTEM
, "running virtual machine..." );
574 EM_log( CK_LOG_SEVERE
, "initializing audio buffers..." );
575 if( !m_bbq
->digi_out()->initialize( ) )
577 m_last_error
= "cannot open audio output (option: use --silent/-s)";
581 m_bbq
->digi_in()->initialize( );
585 EM_log( CK_LOG_SEVERE
, "virtual machine running..." );
590 if( m_block
) this->run( -1 );
593 // compute shreds before first sample
599 EM_log( CK_LOG_SYSTEM
, "virtual machine stopped..." );
604 if( !m_audio_started
) start_audio();
615 /*should we comment out what we just did?
616 i can't think of why it might be affecting this part of the vm
623 //-----------------------------------------------------------------------------
626 //-----------------------------------------------------------------------------
627 t_CKBOOL
Chuck_VM::compute()
629 Chuck_VM_Shred
*& shred
= m_shreduler
->m_current_shred
;
630 Chuck_Msg
* msg
= NULL
;
631 Chuck_Event
* event
= NULL
;
632 t_CKBOOL iterate
= TRUE
;
634 // iteration until no more shreds/events/messages
637 // get the shreds queued for 'now'
638 while(( shred
= m_shreduler
->get() ))
640 // set the current time of the shred
641 shred
->now
= shred
->wake_time
;
643 // track shred activation
644 CK_TRACK( Chuck_Stats::instance()->activate_shred( shred
) );
647 if( !shred
->run( this ) )
649 // track shred deactivation
650 CK_TRACK( Chuck_Stats::instance()->deactivate_shred( shred
) );
652 this->free( shred
, TRUE
);
654 if( !m_num_shreds
&& m_halt
) return FALSE
;
657 // track shred deactivation
658 CK_TRACK( if( shred
) Chuck_Stats::instance()->deactivate_shred( shred
) );
664 // set to false for now
667 // broadcast queued events
668 while( m_event_buffer
->get( &event
, 1 ) )
669 { event
->broadcast(); iterate
= TRUE
; }
672 while( m_msg_buffer
->get( &msg
, 1 ) )
673 { process_msg( msg
); iterate
= TRUE
; }
675 // clear dumped shreds
676 if( m_num_dumped_shreds
> 0 )
686 //-----------------------------------------------------------------------------
689 //-----------------------------------------------------------------------------
690 t_CKBOOL
Chuck_VM::run( t_CKINT num_samps
)
696 if( !compute() ) goto vm_stop
;
699 if( !m_audio_started
) start_audio();
701 // advance the shreduler
702 m_shreduler
->advance();
705 if( num_samps
> 0 ) num_samps
--;
715 EM_log( CK_LOG_SYSTEM
, "virtual machine stopped..." );
723 //-----------------------------------------------------------------------------
726 //-----------------------------------------------------------------------------
727 t_CKBOOL
Chuck_VM::pause( )
737 //-----------------------------------------------------------------------------
740 //-----------------------------------------------------------------------------
741 t_CKBOOL
Chuck_VM::stop( )
744 EM_log( CK_LOG_SEVERE
, "requesting STOP virtual machine..." );
747 Digitalio::m_end
= TRUE
;
755 //-----------------------------------------------------------------------------
758 //-----------------------------------------------------------------------------
759 void Chuck_VM::gc( t_CKUINT amount
)
766 //-----------------------------------------------------------------------------
769 //-----------------------------------------------------------------------------
777 //-----------------------------------------------------------------------------
780 //-----------------------------------------------------------------------------
781 t_CKBOOL
Chuck_VM::queue_msg( Chuck_Msg
* msg
, int count
)
783 assert( count
== 1 );
784 m_msg_buffer
->put( &msg
, count
);
791 //-----------------------------------------------------------------------------
792 // name: queue_event()
794 //-----------------------------------------------------------------------------
795 t_CKBOOL
Chuck_VM::queue_event( Chuck_Event
* event
, int count
)
797 assert( count
== 1 );
798 m_event_buffer
->put( &event
, count
);
805 //-----------------------------------------------------------------------------
808 //-----------------------------------------------------------------------------
809 Chuck_Msg
* Chuck_VM::get_reply( )
811 Chuck_Msg
* msg
= NULL
;
812 m_reply_buffer
->get( &msg
, 1 );
819 //-----------------------------------------------------------------------------
820 // name: process_msg()
822 //-----------------------------------------------------------------------------
823 t_CKUINT
Chuck_VM::process_msg( Chuck_Msg
* msg
)
825 t_CKUINT retval
= 0xfffffff0;
827 if( msg
->type
== MSG_REPLACE
)
829 Chuck_VM_Shred
* out
= m_shreduler
->lookup( msg
->param
);
832 EM_error3( "[chuck](VM): error replacing shred: no shred with id %i...",
838 Chuck_VM_Shred
* shred
= msg
->shred
;
841 shred
= new Chuck_VM_Shred
;
842 shred
->initialize( msg
->code
);
843 shred
->name
= msg
->code
->name
;
844 shred
->base_ref
= shred
->mem
;
847 // set the current time
848 shred
->start
= m_shreduler
->now_system
;
850 shred
->xid
= msg
->param
;
852 shred
->now
= shred
->wake_time
= m_shreduler
->now_system
;
854 shred
->vm_ref
= this;
856 if( msg
->args
) shred
->args
= *(msg
->args
);
857 // add it to the parent
859 shred
->parent
->children
[shred
->xid
] = shred
;
862 if( m_shreduler
->remove( out
) && m_shreduler
->shredule( shred
) )
864 EM_error3( "[chuck](VM): replacing shred %i (%s) with %i (%s)...",
865 out
->xid
, mini(out
->name
.c_str()), shred
->xid
, mini(shred
->name
.c_str()) );
866 this->free( out
, TRUE
, FALSE
);
869 // tracking new shred
870 CK_TRACK( Chuck_Stats::instance()->add_shred( shred
) );
876 EM_error3( "[chuck](VM): shreduler ERROR replacing shred %i...",
883 else if( msg
->type
== MSG_REMOVE
)
885 if( msg
->param
== 0xffffffff )
887 if( !this->m_num_shreds
)
889 EM_error3( "[chuck](VM): no shreds to remove..." );
894 t_CKINT xid
= m_shred_id
;
895 Chuck_VM_Shred
* shred
= NULL
;
896 while( xid
>= 0 && m_shreduler
->remove( shred
= m_shreduler
->lookup( xid
) ) == 0 )
900 EM_error3( "[chuck](VM): removing recent shred: %i (%s)...",
901 xid
, mini(shred
->name
.c_str()) );
902 this->free( shred
, TRUE
);
907 EM_error3( "[chuck](VM): no shreds removed..." );
914 Chuck_VM_Shred
* shred
= m_shreduler
->lookup( msg
->param
);
917 EM_error3( "[chuck](VM): cannot remove: no shred with id %i...",
922 if( shred
!= m_shreduler
->m_current_shred
&& !m_shreduler
->remove( shred
) ) // was lookup
924 EM_error3( "[chuck](VM): shreduler: cannot remove shred %i...",
929 EM_error3( "[chuck](VM): removing shred: %i (%s)...",
930 msg
->param
, mini(shred
->name
.c_str()) );
931 this->free( shred
, TRUE
);
935 else if( msg
->type
== MSG_REMOVEALL
)
937 t_CKUINT xid
= m_shred_id
;
938 EM_error3( "[chuck](VM): removing all (%i) shreds...", m_num_shreds
);
939 Chuck_VM_Shred
* shred
= NULL
;
941 while( m_num_shreds
&& xid
> 0 )
943 if( m_shreduler
->remove( shred
= m_shreduler
->lookup( xid
) ) )
944 this->free( shred
, TRUE
);
951 else if( msg
->type
== MSG_ADD
)
954 Chuck_VM_Shred
* shred
= NULL
;
955 if( msg
->shred
) shred
= this->spork( msg
->shred
);
956 else shred
= this->spork( msg
->code
, NULL
);
958 if( msg
->args
) shred
->args
= *(msg
->args
);
960 const char * s
= ( msg
->shred
? msg
->shred
->name
.c_str() : msg
->code
->name
.c_str() );
961 EM_error3( "[chuck](VM): sporking incoming shred: %i (%s)...", xid
, mini(s
) );
965 else if( msg
->type
== MSG_KILL
)
967 EM_error3( "[chuck](VM): KILL received...." );
968 // close file handles and clean up
970 // TODO: free more memory?
973 EM_log( CK_LOG_INFO
, "(VM): exiting..." );
977 else if( msg
->type
== MSG_STATUS
)
980 if( msg
->user
&& msg
->reply
)
983 Chuck_VM_Status
* status
= (Chuck_VM_Status
*)msg
->user
;
985 m_shreduler
->status( status
);
989 m_shreduler
->status();
992 else if( msg
->type
== MSG_TIME
)
994 float srate
= (float)Digitalio::sampling_rate();
995 fprintf( stderr
, "[chuck](VM): the values of now:\n" );
996 fprintf( stderr
, " now = %.6f (samp)\n", m_shreduler
->now_system
);
997 fprintf( stderr
, " = %.6f (second)\n", m_shreduler
->now_system
/ srate
);
998 fprintf( stderr
, " = %.6f (minute)\n", m_shreduler
->now_system
/ srate
/ 60.0f
);
999 fprintf( stderr
, " = %.6f (hour)\n", m_shreduler
->now_system
/ srate
/ 60.0f
/ 60.0f
);
1000 fprintf( stderr
, " = %.6f (day)\n", m_shreduler
->now_system
/ srate
/ 60.0f
/ 60.0f
/ 24.0f
);
1001 fprintf( stderr
, " = %.6f (week)\n", m_shreduler
->now_system
/ srate
/ 60.0f
/ 60.0f
/ 24.0f
/ 7.0f
);
1003 else if( msg
->type
== MSG_RESET_ID
)
1005 t_CKUINT n
= m_shreduler
->highest();
1007 fprintf( stderr
, "[chuck](VM): reseting shred id to %d...\n", m_shred_id
+ 1 );
1014 msg
->replyA
= retval
;
1015 m_reply_buffer
->put( &msg
, 1 );
1026 //-----------------------------------------------------------------------------
1029 //-----------------------------------------------------------------------------
1030 t_CKUINT
Chuck_VM::next_id( )
1032 return ++m_shred_id
;
1038 //-----------------------------------------------------------------------------
1039 // name: shreduler()
1041 //-----------------------------------------------------------------------------
1042 Chuck_VM_Shreduler
* Chuck_VM::shreduler( ) const
1050 //-----------------------------------------------------------------------------
1053 //-----------------------------------------------------------------------------
1054 BBQ
* Chuck_VM::bbq( ) const
1062 //-----------------------------------------------------------------------------
1065 //-----------------------------------------------------------------------------
1066 t_CKUINT
Chuck_VM::srate() const
1068 return (t_CKUINT
)Digitalio::sampling_rate();
1074 //-----------------------------------------------------------------------------
1077 //-----------------------------------------------------------------------------
1078 Chuck_VM_Shred
* Chuck_VM::fork( Chuck_VM_Code
* code
)
1086 //-----------------------------------------------------------------------------
1089 //-----------------------------------------------------------------------------
1090 Chuck_VM_Shred
* Chuck_VM::spork( Chuck_VM_Code
* code
, Chuck_VM_Shred
* parent
)
1092 // allocate a new shred
1093 Chuck_VM_Shred
* shred
= new Chuck_VM_Shred
;
1094 // initialize the shred (default stack size)
1095 shred
->initialize( code
);
1097 shred
->name
= code
->name
;
1099 shred
->parent
= parent
;
1100 // set the base ref for global
1101 if( parent
) shred
->base_ref
= shred
->parent
->base_ref
;
1102 else shred
->base_ref
= shred
->mem
;
1104 this->spork( shred
);
1107 CK_TRACK( Chuck_Stats::instance()->add_shred( shred
) );
1115 //-----------------------------------------------------------------------------
1118 //-----------------------------------------------------------------------------
1119 Chuck_VM_Shred
* Chuck_VM::spork( Chuck_VM_Shred
* shred
)
1121 // set the current time
1122 shred
->start
= m_shreduler
->now_system
;
1124 shred
->now
= shred
->wake_time
= m_shreduler
->now_system
;
1126 shred
->xid
= next_id();
1128 shred
->vm_ref
= this;
1131 // add it to the parent
1133 shred
->parent
->children
[shred
->xid
] = shred
;
1135 m_shreduler
->shredule( shred
);
1145 //-----------------------------------------------------------------------------
1148 //-----------------------------------------------------------------------------
1149 t_CKBOOL
Chuck_VM::free( Chuck_VM_Shred
* shred
, t_CKBOOL cascade
, t_CKBOOL dec
)
1154 EM_log( CK_LOG_FINER
, "freeing shred (id==%d | ptr==%p)", shred
->xid
,
1157 // abort on the double free
1158 // TODO: can a shred be dumped, then resporked? from code?
1159 if( shred
->is_dumped
) return FALSE
;
1162 shred
->is_done
= TRUE
;
1164 // free the children
1165 t_CKINT size
= shred
->children
.size();
1168 vector
<Chuck_VM_Shred
*> list
; list
.resize( size
);
1169 map
<t_CKUINT
, Chuck_VM_Shred
*>::iterator iter
; t_CKINT i
= 0;
1170 for( iter
= shred
->children
.begin(); iter
!= shred
->children
.end(); iter
++ )
1171 list
[i
++] = (*iter
).second
;
1172 for( i
= 0; i
< size
; i
++ )
1173 this->free( list
[i
], cascade
);
1176 // make sure it's done
1177 assert( shred
->children
.size() == 0 );
1181 shred
->parent
->children
.erase( shred
->xid
);
1183 // track remove shred
1184 CK_TRACK( Chuck_Stats::instance()->remove_shred( shred
) );
1187 m_shreduler
->remove( shred
);
1188 // TODO: remove shred from event, with synchronization (still necessary with dump?)
1189 // if( shred->event ) shred->event->remove( shred );
1190 // OLD: shred->release();
1191 this->dump( shred
);
1193 if( dec
) m_num_shreds
--;
1194 if( !m_num_shreds
) m_shred_id
= 0;
1202 //-----------------------------------------------------------------------------
1203 // name: abort_current_shred()
1205 //-----------------------------------------------------------------------------
1206 t_CKBOOL
Chuck_VM::abort_current_shred( )
1209 Chuck_VM_Shred
* shred
= m_shreduler
->m_current_shred
;
1215 EM_log( CK_LOG_SEVERE
, "trying to abort current shred (id: %d)", shred
->xid
);
1217 shred
->is_abort
= TRUE
;
1222 EM_log( CK_LOG_SEVERE
, "cannot abort shred: nothing currently running!" );
1225 return shred
!= NULL
;
1231 //-----------------------------------------------------------------------------
1234 //-----------------------------------------------------------------------------
1235 void Chuck_VM::dump( Chuck_VM_Shred
* shred
)
1238 EM_log( CK_LOG_FINER
, "dumping shred (id==%d | ptr==%p)", shred
->xid
,
1241 m_shred_dump
.push_back( shred
);
1243 shred
->is_running
= FALSE
;
1244 shred
->is_done
= TRUE
;
1245 shred
->is_dumped
= TRUE
;
1249 m_num_dumped_shreds
++;
1255 //-----------------------------------------------------------------------------
1256 // name: release_dump()
1258 //-----------------------------------------------------------------------------
1259 void Chuck_VM::release_dump( )
1262 EM_log( CK_LOG_FINER
, "releasing dumped shreds..." );
1264 // iterate through dump
1265 for( t_CKUINT i
= 0; i
< m_shred_dump
.size(); i
++ )
1266 SAFE_RELEASE( m_shred_dump
[i
] );
1269 m_shred_dump
.clear();
1271 m_num_dumped_shreds
= 0;
1277 //-----------------------------------------------------------------------------
1278 // name: Chuck_VM_Stack()
1280 //-----------------------------------------------------------------------------
1281 Chuck_VM_Stack::Chuck_VM_Stack()
1283 stack
= sp
= sp_max
= NULL
;
1291 //-----------------------------------------------------------------------------
1292 // name: ~Chuck_VM_Stack()
1294 //-----------------------------------------------------------------------------
1295 Chuck_VM_Stack::~Chuck_VM_Stack()
1303 //-----------------------------------------------------------------------------
1304 // name: Chuck_VM_Code()
1306 //-----------------------------------------------------------------------------
1307 Chuck_VM_Code::Chuck_VM_Code()
1314 native_func_type
= NATIVE_UNKNOWN
;
1320 //-----------------------------------------------------------------------------
1321 // name: ~Chuck_VM_Code()
1323 //-----------------------------------------------------------------------------
1324 Chuck_VM_Code::~Chuck_VM_Code()
1326 // free instructions
1330 for( t_CKUINT i
= 0; i
< num_instr
; i
++ )
1334 SAFE_DELETE_ARRAY( instr
);
1343 // offset in bytes at the beginning of a stack for initializing data
1344 #define VM_STACK_OFFSET 16
1345 // 1/factor of stack is left blank, to give room to detect overflow
1346 #define VM_STACK_PADDING_FACTOR 16
1347 //-----------------------------------------------------------------------------
1348 // name: initialize()
1350 //-----------------------------------------------------------------------------
1351 t_CKBOOL
Chuck_VM_Stack::initialize( t_CKUINT size
)
1356 // make room for header
1357 size
+= VM_STACK_OFFSET
;
1359 stack
= new t_CKBYTE
[size
];
1360 if( !stack
) goto out_of_memory
;
1363 memset( stack
, 0, size
);
1365 // advance stack after the header
1366 stack
+= VM_STACK_OFFSET
;
1369 // upper limit (padding factor)
1370 sp_max
= sp
+ size
- (size
/ VM_STACK_PADDING_FACTOR
);
1372 // set flag and return
1373 return m_is_init
= TRUE
;
1377 // we have a problem
1379 "[chuck](VM): OutOfMemory: while allocating stack '%s'\n" );
1388 //-----------------------------------------------------------------------------
1391 //-----------------------------------------------------------------------------
1392 t_CKBOOL
Chuck_VM_Stack::shutdown()
1398 stack
-= VM_STACK_OFFSET
;
1399 SAFE_DELETE_ARRAY( stack
);
1402 // set the flag to false
1411 //-----------------------------------------------------------------------------
1412 // name: Chuck_VM_Shred()
1414 //-----------------------------------------------------------------------------
1415 Chuck_VM_Shred::Chuck_VM_Shred()
1417 mem
= new Chuck_VM_Stack
;
1418 reg
= new Chuck_VM_Stack
;
1423 // obj_array = NULL;
1424 // obj_array_size = 0;
1431 CK_TRACK( stat
= NULL
);
1437 //-----------------------------------------------------------------------------
1438 // name: ~Chuck_VM_Shred()
1440 //-----------------------------------------------------------------------------
1441 Chuck_VM_Shred::~Chuck_VM_Shred()
1449 //-----------------------------------------------------------------------------
1450 // name: initialize()
1452 //-----------------------------------------------------------------------------
1453 t_CKBOOL
Chuck_VM_Shred::initialize( Chuck_VM_Code
* c
,
1454 t_CKUINT mem_stack_size
,
1455 t_CKUINT reg_stack_size
)
1457 // allocate mem and reg
1458 if( !mem
->initialize( mem_stack_size
) ) return FALSE
;
1459 if( !reg
->initialize( reg_stack_size
) ) return FALSE
;
1465 code_orig
= code
= c
;
1467 code_orig
->add_ref();
1468 // shred in dump (all done)
1482 initialize_object( this, &t_shred
);
1490 //-----------------------------------------------------------------------------
1493 //-----------------------------------------------------------------------------
1494 t_CKBOOL
Chuck_VM_Shred::shutdown()
1496 // get iterator to our map
1497 map
<Chuck_UGen
*, Chuck_UGen
*>::iterator iter
= m_ugen_map
.begin();
1498 while( iter
!= m_ugen_map
.end() )
1500 (*iter
).first
->disconnect( TRUE
);
1509 // delete temp pointer space
1510 // SAFE_DELETE_ARRAY( obj_array );
1511 // obj_array_size = 0;
1513 // TODO: is this right?
1514 code_orig
->release();
1515 code_orig
= code
= NULL
;
1516 // what to do with next and prev?
1524 //-----------------------------------------------------------------------------
1527 //-----------------------------------------------------------------------------
1528 t_CKBOOL
Chuck_VM_Shred::add( Chuck_UGen
* ugen
)
1530 if( m_ugen_map
[ugen
] )
1533 m_ugen_map
[ugen
] = ugen
;
1540 //-----------------------------------------------------------------------------
1543 //-----------------------------------------------------------------------------
1544 t_CKBOOL
Chuck_VM_Shred::remove( Chuck_UGen
* ugen
)
1546 if( !m_ugen_map
[ugen
] )
1550 m_ugen_map
.erase( ugen
);
1557 //-----------------------------------------------------------------------------
1560 //-----------------------------------------------------------------------------
1561 t_CKBOOL
Chuck_VM_Shred::run( Chuck_VM
* vm
)
1564 instr
= code
->instr
;
1566 t_CKBOOL
* vm_running
= &vm
->m_running
;
1569 while( is_running
&& *vm_running
&& !is_abort
)
1571 // execute the instruction
1572 instr
[pc
]->execute( vm
, this );
1578 // track number of cycles
1579 CK_TRACK( this->stat
->cycles
++ );
1586 EM_log( CK_LOG_SYSTEM
, "aborting shred (id: %d)", this->xid
);
1591 // is the shred finished
1598 //-----------------------------------------------------------------------------
1599 // name: Chuck_VM_Shreduler()
1601 //-----------------------------------------------------------------------------
1602 Chuck_VM_Shreduler::Chuck_VM_Shreduler()
1608 m_current_shred
= NULL
;
1612 m_num_dac_channels
= 0;
1613 m_num_adc_channels
= 0;
1619 //-----------------------------------------------------------------------------
1620 // name: ~Chuck_VM_Shreduler()
1622 //-----------------------------------------------------------------------------
1623 Chuck_VM_Shreduler::~Chuck_VM_Shreduler()
1631 //-----------------------------------------------------------------------------
1632 // name: initialize()
1634 //-----------------------------------------------------------------------------
1635 t_CKBOOL
Chuck_VM_Shreduler::initialize()
1643 //-----------------------------------------------------------------------------
1646 //-----------------------------------------------------------------------------
1647 t_CKBOOL
Chuck_VM_Shreduler::shutdown()
1655 //-----------------------------------------------------------------------------
1656 // name: add_blocked()
1657 // desc: add shred to the shreduler's blocked list
1658 //-----------------------------------------------------------------------------
1659 t_CKBOOL
Chuck_VM_Shreduler::add_blocked( Chuck_VM_Shred
* shred
)
1661 // add shred to map, using pointer
1662 blocked
[shred
] = shred
;
1670 //-----------------------------------------------------------------------------
1671 // name: remove_blocked()
1672 // desc: remove shred from the shreduler's blocked list
1673 //-----------------------------------------------------------------------------
1674 t_CKBOOL
Chuck_VM_Shreduler::remove_blocked( Chuck_VM_Shred
* shred
)
1677 std::map
<Chuck_VM_Shred
*, Chuck_VM_Shred
*>::iterator iter
;
1678 iter
= blocked
.find( shred
);
1679 blocked
.erase( iter
);
1681 // remove from event
1682 if( shred
->event
!= NULL
) shred
->event
->remove( shred
);
1690 //-----------------------------------------------------------------------------
1693 //-----------------------------------------------------------------------------
1694 t_CKBOOL
Chuck_VM_Shreduler::shredule( Chuck_VM_Shred
* shred
)
1696 return this->shredule( shred
, now_system
);
1702 //-----------------------------------------------------------------------------
1705 //-----------------------------------------------------------------------------
1706 t_CKBOOL
Chuck_VM_Shreduler::shredule( Chuck_VM_Shred
* shred
,
1707 t_CKTIME wake_time
)
1710 if( shred
->prev
|| shred
->next
)
1712 // something is really wrong here - no shred can be
1713 // shreduled more than once
1714 EM_error3( "[chuck](VM): internal sanity check failed in shredule()" );
1715 EM_error3( "[chuck](VM): (shred shreduled while shreduled)" );
1721 if( wake_time
< (this->now_system
- .5) )
1723 // trying to enqueue on a time that is less than now
1724 EM_error3( "[chuck](VM): internal sanity check failed in shredule()" );
1725 EM_error3( "[chuck](VM): (wake time is past) - %f : %f", wake_time
, this->now_system
);
1730 shred
->wake_time
= wake_time
;
1737 // pointers to the shred queue
1738 Chuck_VM_Shred
* curr
= shred_list
;
1739 Chuck_VM_Shred
* prev
= NULL
;
1743 // found the place to insert
1744 if( curr
->wake_time
> wake_time
)
1753 shred
->next
= shred_list
;
1754 if( shred_list
) shred_list
->prev
= shred
;
1759 // insert the shred in sorted order
1760 shred
->next
= prev
->next
;
1762 if( prev
->next
) prev
->next
->prev
= shred
;
1772 //-----------------------------------------------------------------------------
1775 //-----------------------------------------------------------------------------
1776 void Chuck_VM_Shreduler::advance2( )
1778 // advance system 'now'
1779 this->now_system
+= 1;
1783 BBQ
* audio
= this->bbq
;
1788 audio
->digi_in()->tick_in( &l
, &r
);
1789 m_adc
->m_multi_chan
[0]->m_current
= l
* m_adc
->m_multi_chan
[0]->m_gain
;
1790 m_adc
->m_multi_chan
[1]->m_current
= r
* m_adc
->m_multi_chan
[1]->m_gain
;
1791 m_adc
->m_current
= .5f
* ( l
+ r
);
1793 m_adc
->m_multi_chan
[0]->m_time
= this->now_system
;
1794 m_adc
->m_multi_chan
[1]->m_time
= this->now_system
;
1795 m_adc
->m_time
= this->now_system
;
1799 m_dac
->system_tick( this->now_system
);
1800 l
= m_dac
->m_multi_chan
[0]->m_current
;
1801 r
= m_dac
->m_multi_chan
[1]->m_current
;
1803 // l *= .5f; r *= .5f;
1806 m_bunghole
->system_tick( this->now_system
);
1809 audio
->digi_out()->tick_out( l
, r
);
1815 //-----------------------------------------------------------------------------
1818 //-----------------------------------------------------------------------------
1819 void Chuck_VM_Shreduler::advance( )
1821 // advance system 'now'
1822 this->now_system
+= 1;
1827 BBQ
* audio
= this->bbq
;
1833 audio
->digi_in()->tick_in( frame
, m_num_adc_channels
);
1835 // loop over channels
1836 for( i
= 0; i
< m_num_adc_channels
; i
++ )
1838 m_adc
->m_multi_chan
[i
]->m_current
= frame
[i
] * m_adc
->m_multi_chan
[i
]->m_gain
* m_adc
->m_gain
;
1839 m_adc
->m_multi_chan
[i
]->m_time
= this->now_system
;
1840 sum
+= m_adc
->m_multi_chan
[i
]->m_current
;
1842 m_adc
->m_last
= m_adc
->m_current
= sum
/ m_num_adc_channels
;
1843 m_adc
->m_time
= this->now_system
;
1847 m_dac
->system_tick( this->now_system
);
1848 for( i
= 0; i
< m_num_dac_channels
; i
++ )
1849 frame
[i
] = m_dac
->m_multi_chan
[i
]->m_current
* .5f
;
1852 m_bunghole
->system_tick( this->now_system
);
1855 audio
->digi_out()->tick_out( frame
, m_num_dac_channels
);
1860 //-----------------------------------------------------------------------------
1863 //-----------------------------------------------------------------------------
1864 Chuck_VM_Shred
* Chuck_VM_Shreduler::get( )
1866 Chuck_VM_Shred
* shred
= shred_list
;
1872 // TODO: should this be <=?
1873 if( shred
->wake_time
<= ( this->now_system
+ .5 ) )
1875 // if( shred->wake_time < this->now_system )
1878 shred_list
= shred
->next
;
1883 shred_list
->prev
= NULL
;
1894 //-----------------------------------------------------------------------------
1897 //-----------------------------------------------------------------------------
1898 t_CKUINT
Chuck_VM_Shreduler::highest( )
1900 Chuck_VM_Shred
* shred
= shred_list
;
1905 if( shred
->xid
> n
) n
= shred
->xid
;
1906 shred
= shred
->next
;
1909 std::map
<Chuck_VM_Shred
*, Chuck_VM_Shred
*>::iterator iter
;
1910 for( iter
= blocked
.begin(); iter
!= blocked
.end(); iter
++ )
1912 shred
= (*iter
).second
;
1913 if( shred
->xid
> n
) n
= shred
->xid
;
1922 //-----------------------------------------------------------------------------
1925 //-----------------------------------------------------------------------------
1926 t_CKBOOL
Chuck_VM_Shreduler::replace( Chuck_VM_Shred
* out
, Chuck_VM_Shred
* in
)
1937 out
->prev
->next
= in
;
1940 out
->next
->prev
= in
;
1942 in
->next
= out
->next
;
1943 in
->prev
= out
->prev
;
1945 out
->next
= out
->prev
= NULL
;
1947 in
->wake_time
= out
->wake_time
;
1948 in
->start
= in
->wake_time
;
1956 //-----------------------------------------------------------------------------
1959 //-----------------------------------------------------------------------------
1960 t_CKBOOL
Chuck_VM_Shreduler::remove( Chuck_VM_Shred
* out
)
1962 if( !out
) return FALSE
;
1965 if( out
->event
!= NULL
)
1967 return remove_blocked( out
);
1971 if( !out
->prev
&& !out
->next
&& out
!= shred_list
)
1975 shred_list
= out
->next
;
1977 out
->prev
->next
= out
->next
;
1980 out
->next
->prev
= out
->prev
;
1982 out
->next
= out
->prev
= NULL
;
1990 //-----------------------------------------------------------------------------
1993 //-----------------------------------------------------------------------------
1994 Chuck_VM_Shred
* Chuck_VM_Shreduler::lookup( t_CKUINT xid
)
1996 Chuck_VM_Shred
* shred
= shred_list
;
1999 if( m_current_shred
!= NULL
&& m_current_shred
->xid
== xid
)
2000 return m_current_shred
;
2002 // look for in shreduled list
2005 if( shred
->xid
== xid
)
2008 shred
= shred
->next
;
2012 std::map
<Chuck_VM_Shred
*, Chuck_VM_Shred
*>::iterator iter
;
2013 for( iter
= blocked
.begin(); iter
!= blocked
.end(); iter
++ )
2015 shred
= (*iter
).second
;
2016 if( shred
->xid
== xid
)
2026 //-----------------------------------------------------------------------------
2029 //-----------------------------------------------------------------------------
2032 bool operator() ( const Chuck_VM_Shred
* lhs
, const Chuck_VM_Shred
* rhs
)
2033 { return lhs
->xid
< rhs
->xid
; }
2039 //-----------------------------------------------------------------------------
2042 //-----------------------------------------------------------------------------
2043 void Chuck_VM_Shreduler::status( Chuck_VM_Status
* status
)
2045 Chuck_VM_Shred
* shred
= shred_list
;
2046 Chuck_VM_Shred
* temp
= NULL
;
2048 t_CKUINT srate
= Digitalio::sampling_rate();
2049 t_CKUINT s
= (t_CKUINT
)now_system
;
2050 t_CKUINT h
= s
/(srate
*3600);
2051 s
= s
- (h
*(srate
*3600));
2052 t_CKUINT m
= s
/ (srate
*60);
2053 s
= s
- (m
*(srate
*60));
2054 t_CKUINT sec
= s
/ srate
;
2055 s
= s
- (sec
*(srate
));
2056 // float millisecond = s / (float)(srate) * 1000.0f;
2058 status
->srate
= srate
;
2059 status
->now_system
= now_system
;
2060 status
->t_second
= sec
;
2061 status
->t_minute
= m
;
2064 // get list of shreds
2065 vector
<Chuck_VM_Shred
*> list
;
2068 list
.push_back( shred
);
2069 shred
= shred
->next
;
2073 std::map
<Chuck_VM_Shred
*, Chuck_VM_Shred
*>::iterator iter
;
2074 for( iter
= blocked
.begin(); iter
!= blocked
.end(); iter
++ )
2076 shred
= (*iter
).second
;
2077 list
.push_back( shred
);
2080 // get current shred
2081 if( temp
= m_current_shred
)
2082 list
.push_back( temp
);
2086 std::sort( list
.begin(), list
.end(), byid
);
2090 for( t_CKUINT i
= 0; i
< list
.size(); i
++ )
2093 status
->list
.push_back( new Chuck_VM_Shred_Status(
2094 shred
->xid
, shred
->name
, shred
->start
, shred
->event
!= NULL
) );
2101 //-----------------------------------------------------------------------------
2104 //-----------------------------------------------------------------------------
2105 void Chuck_VM_Shreduler::status( )
2107 Chuck_VM_Shred_Status
* shred
= NULL
;
2109 this->status( &m_status
);
2110 t_CKUINT h
= m_status
.t_hour
;
2111 t_CKUINT m
= m_status
.t_minute
;
2112 t_CKUINT sec
= m_status
.t_second
;
2113 fprintf( stdout
, "[chuck](VM): status (now == %ldh%ldm%lds, %.1f samps) ...\n",
2114 h
, m
, sec
, m_status
.now_system
);
2117 for( t_CKUINT i
= 0; i
< m_status
.list
.size(); i
++ )
2119 shred
= m_status
.list
[i
];
2121 " [shred id]: %ld [source]: %s [spork time]: %.2fs ago%s\n",
2122 shred
->xid
, mini( shred
->name
.c_str() ),
2123 (m_status
.now_system
- shred
->start
) / m_status
.srate
,
2124 shred
->has_event
? " (blocked)" : "" );
2131 //-----------------------------------------------------------------------------
2132 // name: Chuck_VM_Status()
2134 //-----------------------------------------------------------------------------
2135 Chuck_VM_Status::Chuck_VM_Status()
2139 t_second
= t_minute
= t_hour
= 0;
2145 //-----------------------------------------------------------------------------
2146 // name: ~Chuck_VM_Status()
2148 //-----------------------------------------------------------------------------
2149 Chuck_VM_Status::~Chuck_VM_Status()
2157 //-----------------------------------------------------------------------------
2160 //-----------------------------------------------------------------------------
2161 void Chuck_VM_Status::clear()
2163 for( t_CKUINT i
= 0; i
< list
.size(); i
++ )
2165 SAFE_DELETE( list
[i
] );