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"
46 #if defined(__PLATFORM_WIN32__)
56 //-----------------------------------------------------------------------------
57 // name: struct Chuck_VM_Frame
59 //-----------------------------------------------------------------------------
66 Chuck_VM_Frame() { size
= 0; }
73 //-----------------------------------------------------------------------------
74 // name: struct Chuck_VM_Func
76 //-----------------------------------------------------------------------------
81 Chuck_VM_Frame
* frame
;
85 Chuck_VM_Func() { code
= NULL
; frame
= NULL
; index
= 0; }
92 //-----------------------------------------------------------------------------
93 // name: struct Chuck_VM_FTable
94 // desc: function table
95 //-----------------------------------------------------------------------------
96 struct Chuck_VM_FTable
103 Chuck_VM_Func
* get_func( t_CKUINT index
);
104 t_CKUINT
append( Chuck_VM_Func
* f
);
105 t_CKBOOL
remove( t_CKUINT index
);
108 vector
<Chuck_VM_Func
*> func_table
;
114 //-----------------------------------------------------------------------------
117 //-----------------------------------------------------------------------------
123 m_num_dumped_shreds
= 0;
125 m_reply_buffer
= NULL
;
126 m_event_buffer
= NULL
;
133 m_audio_started
= FALSE
;
137 m_num_dac_channels
= 0;
138 m_num_adc_channels
= 0;
145 //-----------------------------------------------------------------------------
148 //-----------------------------------------------------------------------------
149 Chuck_VM::~Chuck_VM()
151 if( m_init
) shutdown();
156 //UGEN_TICK __dac_tick( Chuck_Object * SELF, SAMPLE in, SAMPLE * out )
157 //{ *out = in; return TRUE; }
158 //UGEN_TICK __bunghole_tick( Chuck_Object * SELF, SAMPLE in, SAMPLE * out )
159 //{ *out = 0.0f; return TRUE; }
163 #ifdef __MACOSX_CORE__
164 t_CKINT
Chuck_VM::our_priority
= 85;
166 t_CKINT
Chuck_VM::our_priority
= 0x7fffffff;
170 #if !defined(__PLATFORM_WIN32__) || defined(__WINDOWS_PTHREAD__)
171 //-----------------------------------------------------------------------------
172 // name: set_priority()
174 //-----------------------------------------------------------------------------
175 t_CKBOOL
Chuck_VM::set_priority( t_CKINT priority
, Chuck_VM
* vm
)
177 struct sched_param param
;
178 pthread_t tid
= pthread_self();
182 EM_log( CK_LOG_INFO
, "setting thread priority to: %ld...", priority
);
185 if( pthread_getschedparam( tid
, &policy
, ¶m
) )
188 vm
->m_last_error
= "could not get current scheduling parameters";
193 param
.sched_priority
= priority
;
197 if( pthread_setschedparam( tid
, policy
, ¶m
) )
200 vm
->m_last_error
= "could not set new scheduling parameters";
207 //-----------------------------------------------------------------------------
208 // name: set_priority()
210 //-----------------------------------------------------------------------------
211 t_CKBOOL
Chuck_VM::set_priority( t_CKINT priority
, Chuck_VM
* vm
)
213 // if priority is 0 then done
214 if( !priority
) return TRUE
;
216 // set the priority class of the process
217 // if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) )
221 EM_log( CK_LOG_INFO
, "setting thread priority to: %ld...", priority
);
223 // set the priority the thread
224 // if( !SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL ) )
225 if( !SetThreadPriority( GetCurrentThread(), priority
) )
228 vm
->m_last_error
= "could not set new scheduling parameters";
239 //-----------------------------------------------------------------------------
240 // name: initialize()
242 //-----------------------------------------------------------------------------
243 t_CKBOOL
Chuck_VM::initialize( t_CKBOOL enable_audio
, t_CKBOOL halt
, t_CKUINT srate
,
244 t_CKUINT buffer_size
, t_CKUINT num_buffers
,
245 t_CKUINT dac
, t_CKUINT adc
, t_CKUINT dac_chan
,
246 t_CKUINT adc_chan
, t_CKBOOL block
)
250 m_last_error
= "VM already initialized!";
254 // boost thread priority
255 // if( priority != 0x7fffffff && !set_priority( priority, this ) )
259 EM_log( CK_LOG_SYSTEM
, "initializing virtual machine..." );
260 EM_pushlog(); // push stack
261 EM_log( CK_LOG_SYSTEM
, "behavior: %s", halt
? "HALT" : "LOOP" );
264 Chuck_VM_Object::lock_all();
269 m_audio
= enable_audio
;
273 EM_log( CK_LOG_SYSTEM
, "allocating shreduler..." );
274 // allocate shreduler
275 m_shreduler
= new Chuck_VM_Shreduler
;
276 m_shreduler
->bbq
= m_bbq
;
277 m_shreduler
->rt_audio
= enable_audio
;
280 EM_log( CK_LOG_SYSTEM
, "allocating messaging buffers..." );
281 // allocate msg buffer
282 m_msg_buffer
= new CBufferSimple
;
283 m_msg_buffer
->initialize( 1024, sizeof(Chuck_Msg
*) );
284 //m_msg_buffer->join(); // this should return 0
285 m_reply_buffer
= new CBufferSimple
;
286 m_reply_buffer
->initialize( 1024, sizeof(Chuck_Msg
*) );
287 //m_reply_buffer->join(); // this should return 0 too
288 m_event_buffer
= new CBufferSimple
;
289 m_event_buffer
->initialize( 1024, sizeof(Chuck_Event
*) );
290 //m_event_buffer->join(); // this should also return 0
293 EM_log( CK_LOG_SYSTEM
, "real-time audio: %s", enable_audio
? "YES" : "NO" );
294 EM_log( CK_LOG_SYSTEM
, "mode: %s", block
? "BLOCKING" : "CALLBACK" );
295 EM_log( CK_LOG_SYSTEM
, "sample rate: %ld", srate
);
296 EM_log( CK_LOG_SYSTEM
, "buffer size: %ld", buffer_size
);
299 EM_log( CK_LOG_SYSTEM
, "num buffers: %ld", num_buffers
);
300 EM_log( CK_LOG_SYSTEM
, "devices adc: %ld dac: %d (default 0)", adc
, dac
);
302 EM_log( CK_LOG_SYSTEM
, "channels in: %ld out: %ld", adc_chan
, dac_chan
);
303 m_num_adc_channels
= adc_chan
;
304 m_num_dac_channels
= dac_chan
;
306 // at least set the sample rate and buffer size
307 m_bbq
->set_srate( srate
);
308 m_bbq
->set_bufsize( buffer_size
);
309 m_bbq
->set_numbufs( num_buffers
);
310 m_bbq
->set_inouts( adc
, dac
);
311 m_bbq
->set_chans( adc_chan
, dac_chan
);
316 // TODO: clean up all the dynamic objects here on failure
317 // and in the shutdown function!
319 return m_init
= TRUE
;
325 //-----------------------------------------------------------------------------
326 // name: initialize_synthesis()
327 // desc: requires type system
328 //-----------------------------------------------------------------------------
329 t_CKBOOL
Chuck_VM::initialize_synthesis( )
333 m_last_error
= "VM initialize_synthesis() called on raw VM";
337 if( !g_t_dac
|| !g_t_adc
)
339 m_last_error
= "VM initialize_synthesis() called before type system initialized";
345 m_last_error
= "VM synthesis already initialized";
350 EM_log( CK_LOG_SYSTEM
, "initializing synthesis engine..." );
355 EM_log( CK_LOG_SEVERE
, "initializing 'dac'..." );
356 // allocate dac and adc
357 g_t_dac
->ugen_info
->num_outs
=
358 g_t_dac
->ugen_info
->num_ins
= m_num_dac_channels
;
359 m_dac
= (Chuck_UGen
*)instantiate_and_initialize_object( g_t_dac
, NULL
);
360 object_ctor( m_dac
, NULL
, NULL
); // TODO: this can't be the place to do this
361 stereo_ctor( m_dac
, NULL
, NULL
); // TODO: is the NULL shred a problem?
362 multi_ctor( m_dac
, NULL
, NULL
); // TODO: remove and let type system do this
368 EM_log( CK_LOG_SEVERE
, "initializing 'adc'..." );
369 g_t_adc
->ugen_info
->num_ins
=
370 g_t_adc
->ugen_info
->num_outs
= m_num_adc_channels
;
371 m_adc
= (Chuck_UGen
*)instantiate_and_initialize_object( g_t_adc
, NULL
);
372 object_ctor( m_adc
, NULL
, NULL
); // TODO: this can't be the place to do this
373 stereo_ctor( m_adc
, NULL
, NULL
);
374 multi_ctor( m_adc
, NULL
, NULL
); // TODO: remove and let type system do this
380 EM_log( CK_LOG_SEVERE
, "initializing 'blackhole'..." );
381 m_bunghole
= new Chuck_UGen
;
382 m_bunghole
->add_ref();
384 initialize_object( m_bunghole
, &t_ugen
);
385 m_bunghole
->tick
= NULL
;
386 m_shreduler
->m_dac
= m_dac
;
387 m_shreduler
->m_adc
= m_adc
;
388 m_shreduler
->m_bunghole
= m_bunghole
;
389 m_shreduler
->m_num_dac_channels
= m_num_dac_channels
;
390 m_shreduler
->m_num_adc_channels
= m_num_adc_channels
;
393 EM_log( CK_LOG_SYSTEM
, "initializing '%s' audio...", m_audio
? "real-time" : "fake-time" );
395 if( !m_bbq
->initialize( m_num_dac_channels
, m_num_adc_channels
,
396 Digitalio::m_sampling_rate
, 16,
397 Digitalio::m_buffer_size
, Digitalio::m_num_buffers
,
398 Digitalio::m_dac_n
, Digitalio::m_adc_n
,
399 m_block
, this, m_audio
) )
401 m_last_error
= "cannot initialize audio device (try using --silent/-s)";
416 //-----------------------------------------------------------------------------
417 // name: compensate_bbq()
419 //-----------------------------------------------------------------------------
420 void Chuck_VM::compensate_bbq()
422 // set shreduler - the audio was initialized elsewhere
423 m_shreduler
->bbq
= m_bbq
;
429 //-----------------------------------------------------------------------------
432 //-----------------------------------------------------------------------------
433 t_CKBOOL
Chuck_VM::shutdown()
435 // make sure we are in the initialized state
436 if( !m_init
) return FALSE
;
439 EM_log( CK_LOG_SYSTEM
, "shutting down virtual machine..." );
443 Chuck_VM_Object::unlock_all();
456 EM_log( CK_LOG_SYSTEM
, "shutting down real-time audio..." );
458 m_bbq
->digi_out()->cleanup();
459 m_bbq
->digi_in()->cleanup();
464 EM_log( CK_LOG_SYSTEM
, "freeing bbq subsystem..." );
466 SAFE_DELETE( m_bbq
);
469 EM_log( CK_LOG_SYSTEM
, "freeing shreduler..." );
470 // free the shreduler
471 SAFE_DELETE( m_shreduler
);
474 EM_log( CK_LOG_SYSTEM
, "freeing msg/reply/event buffers..." );
475 // free the msg buffer
476 SAFE_DELETE( m_msg_buffer
);
477 // free the reply buffer
478 SAFE_DELETE( m_reply_buffer
);
479 // free the event buffer
480 SAFE_DELETE( m_event_buffer
);
483 EM_log( CK_LOG_SEVERE
, "clearing shreds..." );
485 Chuck_VM_Shred
* curr
= m_shreds
, * prev
= NULL
;
498 EM_log( CK_LOG_SEVERE
, "freeing dumped shreds..." );
500 this->release_dump();
504 EM_log( CK_LOG_SYSTEM
, "freeing special ugens..." );
506 SAFE_RELEASE( m_dac
);
507 SAFE_RELEASE( m_adc
);
508 SAFE_RELEASE( m_bunghole
);
521 //-----------------------------------------------------------------------------
522 // name: start_audio()
524 //-----------------------------------------------------------------------------
525 t_CKBOOL
Chuck_VM::start_audio( )
528 if( !m_audio_started
&& m_audio
)
530 EM_log( CK_LOG_SEVERE
, "starting real-time audio..." );
531 m_bbq
->digi_out()->start();
532 m_bbq
->digi_in()->start();
535 // set the flag to true to avoid entering this function
536 m_audio_started
= TRUE
;
544 //-----------------------------------------------------------------------------
547 //-----------------------------------------------------------------------------
548 t_CKBOOL
Chuck_VM::run( )
553 m_last_error
= "VM and/or synthesis not initialized...";
557 // check if already running
560 m_last_error
= "virtual machine already running...";
567 EM_log( CK_LOG_SYSTEM
, "running virtual machine..." );
575 EM_log( CK_LOG_SEVERE
, "initializing audio buffers..." );
576 if( !m_bbq
->digi_out()->initialize( ) )
578 m_last_error
= "cannot open audio output (option: use --silent/-s)";
582 m_bbq
->digi_in()->initialize( );
586 EM_log( CK_LOG_SEVERE
, "virtual machine running..." );
591 if( m_block
) this->run( -1 );
594 // compute shreds before first sample
600 EM_log( CK_LOG_SYSTEM
, "virtual machine stopped..." );
605 if( !m_audio_started
) start_audio();
616 /*should we comment out what we just did?
617 i can't think of why it might be affecting this part of the vm
624 //-----------------------------------------------------------------------------
627 //-----------------------------------------------------------------------------
628 t_CKBOOL
Chuck_VM::compute()
630 Chuck_VM_Shred
*& shred
= m_shreduler
->m_current_shred
;
631 Chuck_Msg
* msg
= NULL
;
632 Chuck_Event
* event
= NULL
;
633 t_CKBOOL iterate
= TRUE
;
635 // iteration until no more shreds/events/messages
638 // get the shreds queued for 'now'
639 while(( shred
= m_shreduler
->get() ))
641 // set the current time of the shred
642 shred
->now
= shred
->wake_time
;
644 // track shred activation
645 CK_TRACK( Chuck_Stats::instance()->activate_shred( shred
) );
648 if( !shred
->run( this ) )
650 // track shred deactivation
651 CK_TRACK( Chuck_Stats::instance()->deactivate_shred( shred
) );
653 this->free( shred
, TRUE
);
655 if( !m_num_shreds
&& m_halt
) return FALSE
;
658 // track shred deactivation
659 CK_TRACK( if( shred
) Chuck_Stats::instance()->deactivate_shred( shred
) );
665 // set to false for now
668 // broadcast queued events
669 while( m_event_buffer
->get( &event
, 1 ) )
670 { event
->broadcast(); iterate
= TRUE
; }
673 while( m_msg_buffer
->get( &msg
, 1 ) )
674 { process_msg( msg
); iterate
= TRUE
; }
676 // clear dumped shreds
677 if( m_num_dumped_shreds
> 0 )
687 //-----------------------------------------------------------------------------
690 //-----------------------------------------------------------------------------
691 t_CKBOOL
Chuck_VM::run( t_CKINT num_samps
)
697 if( !compute() ) goto vm_stop
;
700 if( !m_audio_started
) start_audio();
702 // advance the shreduler
703 m_shreduler
->advance();
706 if( num_samps
> 0 ) num_samps
--;
716 EM_log( CK_LOG_SYSTEM
, "virtual machine stopped..." );
724 //-----------------------------------------------------------------------------
727 //-----------------------------------------------------------------------------
728 t_CKBOOL
Chuck_VM::pause( )
738 //-----------------------------------------------------------------------------
741 //-----------------------------------------------------------------------------
742 t_CKBOOL
Chuck_VM::stop( )
745 EM_log( CK_LOG_SEVERE
, "requesting STOP virtual machine..." );
748 Digitalio::m_end
= TRUE
;
756 //-----------------------------------------------------------------------------
759 //-----------------------------------------------------------------------------
760 void Chuck_VM::gc( t_CKUINT amount
)
767 //-----------------------------------------------------------------------------
770 //-----------------------------------------------------------------------------
778 //-----------------------------------------------------------------------------
781 //-----------------------------------------------------------------------------
782 t_CKBOOL
Chuck_VM::queue_msg( Chuck_Msg
* msg
, int count
)
784 assert( count
== 1 );
785 m_msg_buffer
->put( &msg
, count
);
792 //-----------------------------------------------------------------------------
793 // name: queue_event()
795 //-----------------------------------------------------------------------------
796 t_CKBOOL
Chuck_VM::queue_event( Chuck_Event
* event
, int count
)
798 assert( count
== 1 );
799 m_event_buffer
->put( &event
, count
);
806 //-----------------------------------------------------------------------------
809 //-----------------------------------------------------------------------------
810 Chuck_Msg
* Chuck_VM::get_reply( )
812 Chuck_Msg
* msg
= NULL
;
813 m_reply_buffer
->get( &msg
, 1 );
820 //-----------------------------------------------------------------------------
821 // name: process_msg()
823 //-----------------------------------------------------------------------------
824 t_CKUINT
Chuck_VM::process_msg( Chuck_Msg
* msg
)
826 t_CKUINT retval
= 0xfffffff0;
828 if( msg
->type
== MSG_REPLACE
)
830 Chuck_VM_Shred
* out
= m_shreduler
->lookup( msg
->param
);
833 EM_error3( "[chuck](VM): error replacing shred: no shred with id %i...",
839 Chuck_VM_Shred
* shred
= msg
->shred
;
842 shred
= new Chuck_VM_Shred
;
843 shred
->initialize( msg
->code
);
844 shred
->name
= msg
->code
->name
;
845 shred
->base_ref
= shred
->mem
;
848 // set the current time
849 shred
->start
= m_shreduler
->now_system
;
851 shred
->xid
= msg
->param
;
853 shred
->now
= shred
->wake_time
= m_shreduler
->now_system
;
855 shred
->vm_ref
= this;
857 if( msg
->args
) shred
->args
= *(msg
->args
);
858 // add it to the parent
860 shred
->parent
->children
[shred
->xid
] = shred
;
863 if( m_shreduler
->remove( out
) && m_shreduler
->shredule( shred
) )
865 EM_error3( "[chuck](VM): replacing shred %i (%s) with %i (%s)...",
866 out
->xid
, mini(out
->name
.c_str()), shred
->xid
, mini(shred
->name
.c_str()) );
867 this->free( out
, TRUE
, FALSE
);
870 // tracking new shred
871 CK_TRACK( Chuck_Stats::instance()->add_shred( shred
) );
877 EM_error3( "[chuck](VM): shreduler ERROR replacing shred %i...",
884 else if( msg
->type
== MSG_REMOVE
)
886 if( msg
->param
== 0xffffffff )
888 if( !this->m_num_shreds
)
890 EM_error3( "[chuck](VM): no shreds to remove..." );
895 t_CKINT xid
= m_shred_id
;
896 Chuck_VM_Shred
* shred
= NULL
;
897 while( xid
>= 0 && m_shreduler
->remove( shred
= m_shreduler
->lookup( xid
) ) == 0 )
901 EM_error3( "[chuck](VM): removing recent shred: %i (%s)...",
902 xid
, mini(shred
->name
.c_str()) );
903 this->free( shred
, TRUE
);
908 EM_error3( "[chuck](VM): no shreds removed..." );
915 Chuck_VM_Shred
* shred
= m_shreduler
->lookup( msg
->param
);
918 EM_error3( "[chuck](VM): cannot remove: no shred with id %i...",
923 if( shred
!= m_shreduler
->m_current_shred
&& !m_shreduler
->remove( shred
) ) // was lookup
925 EM_error3( "[chuck](VM): shreduler: cannot remove shred %i...",
930 EM_error3( "[chuck](VM): removing shred: %i (%s)...",
931 msg
->param
, mini(shred
->name
.c_str()) );
932 this->free( shred
, TRUE
);
936 else if( msg
->type
== MSG_REMOVEALL
)
938 t_CKUINT xid
= m_shred_id
;
939 EM_error3( "[chuck](VM): removing all (%i) shreds...", m_num_shreds
);
940 Chuck_VM_Shred
* shred
= NULL
;
942 while( m_num_shreds
&& xid
> 0 )
944 if( m_shreduler
->remove( shred
= m_shreduler
->lookup( xid
) ) )
945 this->free( shred
, TRUE
);
952 else if( msg
->type
== MSG_ADD
)
955 Chuck_VM_Shred
* shred
= NULL
;
956 if( msg
->shred
) shred
= this->spork( msg
->shred
);
957 else shred
= this->spork( msg
->code
, NULL
);
959 if( msg
->args
) shred
->args
= *(msg
->args
);
961 const char * s
= ( msg
->shred
? msg
->shred
->name
.c_str() : msg
->code
->name
.c_str() );
962 EM_error3( "[chuck](VM): sporking incoming shred: %i (%s)...", xid
, mini(s
) );
966 else if( msg
->type
== MSG_KILL
)
968 EM_error3( "[chuck](VM): KILL received...." );
969 // close file handles and clean up
971 // TODO: free more memory?
974 EM_log( CK_LOG_INFO
, "(VM): exiting..." );
978 else if( msg
->type
== MSG_STATUS
)
981 if( msg
->user
&& msg
->reply
)
984 Chuck_VM_Status
* status
= (Chuck_VM_Status
*)msg
->user
;
986 m_shreduler
->status( status
);
990 m_shreduler
->status();
993 else if( msg
->type
== MSG_TIME
)
995 float srate
= (float)Digitalio::sampling_rate();
996 fprintf( stderr
, "[chuck](VM): the values of now:\n" );
997 fprintf( stderr
, " now = %.6f (samp)\n", m_shreduler
->now_system
);
998 fprintf( stderr
, " = %.6f (second)\n", m_shreduler
->now_system
/ srate
);
999 fprintf( stderr
, " = %.6f (minute)\n", m_shreduler
->now_system
/ srate
/ 60.0f
);
1000 fprintf( stderr
, " = %.6f (hour)\n", m_shreduler
->now_system
/ srate
/ 60.0f
/ 60.0f
);
1001 fprintf( stderr
, " = %.6f (day)\n", m_shreduler
->now_system
/ srate
/ 60.0f
/ 60.0f
/ 24.0f
);
1002 fprintf( stderr
, " = %.6f (week)\n", m_shreduler
->now_system
/ srate
/ 60.0f
/ 60.0f
/ 24.0f
/ 7.0f
);
1004 else if( msg
->type
== MSG_RESET_ID
)
1006 t_CKUINT n
= m_shreduler
->highest();
1008 fprintf( stderr
, "[chuck](VM): reseting shred id to %d...\n", m_shred_id
+ 1 );
1015 msg
->replyA
= retval
;
1016 m_reply_buffer
->put( &msg
, 1 );
1027 //-----------------------------------------------------------------------------
1030 //-----------------------------------------------------------------------------
1031 t_CKUINT
Chuck_VM::next_id( )
1033 return ++m_shred_id
;
1039 //-----------------------------------------------------------------------------
1040 // name: shreduler()
1042 //-----------------------------------------------------------------------------
1043 Chuck_VM_Shreduler
* Chuck_VM::shreduler( ) const
1051 //-----------------------------------------------------------------------------
1054 //-----------------------------------------------------------------------------
1055 BBQ
* Chuck_VM::bbq( ) const
1063 //-----------------------------------------------------------------------------
1066 //-----------------------------------------------------------------------------
1067 t_CKUINT
Chuck_VM::srate() const
1069 return (t_CKUINT
)Digitalio::sampling_rate();
1075 //-----------------------------------------------------------------------------
1078 //-----------------------------------------------------------------------------
1079 Chuck_VM_Shred
* Chuck_VM::fork( Chuck_VM_Code
* code
)
1087 //-----------------------------------------------------------------------------
1090 //-----------------------------------------------------------------------------
1091 Chuck_VM_Shred
* Chuck_VM::spork( Chuck_VM_Code
* code
, Chuck_VM_Shred
* parent
)
1093 // allocate a new shred
1094 Chuck_VM_Shred
* shred
= new Chuck_VM_Shred
;
1095 // initialize the shred (default stack size)
1096 shred
->initialize( code
);
1098 shred
->name
= code
->name
;
1100 shred
->parent
= parent
;
1101 // set the base ref for global
1102 if( parent
) shred
->base_ref
= shred
->parent
->base_ref
;
1103 else shred
->base_ref
= shred
->mem
;
1105 this->spork( shred
);
1108 CK_TRACK( Chuck_Stats::instance()->add_shred( shred
) );
1116 //-----------------------------------------------------------------------------
1119 //-----------------------------------------------------------------------------
1120 Chuck_VM_Shred
* Chuck_VM::spork( Chuck_VM_Shred
* shred
)
1122 // set the current time
1123 shred
->start
= m_shreduler
->now_system
;
1125 shred
->now
= shred
->wake_time
= m_shreduler
->now_system
;
1127 shred
->xid
= next_id();
1129 shred
->vm_ref
= this;
1132 // add it to the parent
1134 shred
->parent
->children
[shred
->xid
] = shred
;
1136 m_shreduler
->shredule( shred
);
1146 //-----------------------------------------------------------------------------
1149 //-----------------------------------------------------------------------------
1150 t_CKBOOL
Chuck_VM::free( Chuck_VM_Shred
* shred
, t_CKBOOL cascade
, t_CKBOOL dec
)
1155 EM_log( CK_LOG_FINER
, "freeing shred (id==%d | ptr==%p)", shred
->xid
,
1158 // abort on the double free
1159 // TODO: can a shred be dumped, then resporked? from code?
1160 if( shred
->is_dumped
) return FALSE
;
1163 shred
->is_done
= TRUE
;
1165 // free the children
1166 t_CKINT size
= shred
->children
.size();
1169 vector
<Chuck_VM_Shred
*> list
; list
.resize( size
);
1170 map
<t_CKUINT
, Chuck_VM_Shred
*>::iterator iter
; t_CKINT i
= 0;
1171 for( iter
= shred
->children
.begin(); iter
!= shred
->children
.end(); iter
++ )
1172 list
[i
++] = (*iter
).second
;
1173 for( i
= 0; i
< size
; i
++ )
1174 this->free( list
[i
], cascade
);
1177 // make sure it's done
1178 assert( shred
->children
.size() == 0 );
1182 shred
->parent
->children
.erase( shred
->xid
);
1184 // track remove shred
1185 CK_TRACK( Chuck_Stats::instance()->remove_shred( shred
) );
1188 m_shreduler
->remove( shred
);
1189 // TODO: remove shred from event, with synchronization (still necessary with dump?)
1190 // if( shred->event ) shred->event->remove( shred );
1191 // OLD: shred->release();
1192 this->dump( shred
);
1194 if( dec
) m_num_shreds
--;
1195 if( !m_num_shreds
) m_shred_id
= 0;
1203 //-----------------------------------------------------------------------------
1204 // name: abort_current_shred()
1206 //-----------------------------------------------------------------------------
1207 t_CKBOOL
Chuck_VM::abort_current_shred( )
1210 Chuck_VM_Shred
* shred
= m_shreduler
->m_current_shred
;
1216 EM_log( CK_LOG_SEVERE
, "trying to abort current shred (id: %d)", shred
->xid
);
1218 shred
->is_abort
= TRUE
;
1223 EM_log( CK_LOG_SEVERE
, "cannot abort shred: nothing currently running!" );
1226 return shred
!= NULL
;
1232 //-----------------------------------------------------------------------------
1235 //-----------------------------------------------------------------------------
1236 void Chuck_VM::dump( Chuck_VM_Shred
* shred
)
1239 EM_log( CK_LOG_FINER
, "dumping shred (id==%d | ptr==%p)", shred
->xid
,
1242 m_shred_dump
.push_back( shred
);
1244 shred
->is_running
= FALSE
;
1245 shred
->is_done
= TRUE
;
1246 shred
->is_dumped
= TRUE
;
1250 m_num_dumped_shreds
++;
1256 //-----------------------------------------------------------------------------
1257 // name: release_dump()
1259 //-----------------------------------------------------------------------------
1260 void Chuck_VM::release_dump( )
1263 EM_log( CK_LOG_FINER
, "releasing dumped shreds..." );
1265 // iterate through dump
1266 for( t_CKUINT i
= 0; i
< m_shred_dump
.size(); i
++ )
1267 SAFE_RELEASE( m_shred_dump
[i
] );
1270 m_shred_dump
.clear();
1272 m_num_dumped_shreds
= 0;
1278 //-----------------------------------------------------------------------------
1279 // name: Chuck_VM_Stack()
1281 //-----------------------------------------------------------------------------
1282 Chuck_VM_Stack::Chuck_VM_Stack()
1284 stack
= sp
= sp_max
= NULL
;
1292 //-----------------------------------------------------------------------------
1293 // name: ~Chuck_VM_Stack()
1295 //-----------------------------------------------------------------------------
1296 Chuck_VM_Stack::~Chuck_VM_Stack()
1304 //-----------------------------------------------------------------------------
1305 // name: Chuck_VM_Code()
1307 //-----------------------------------------------------------------------------
1308 Chuck_VM_Code::Chuck_VM_Code()
1315 native_func_type
= NATIVE_UNKNOWN
;
1321 //-----------------------------------------------------------------------------
1322 // name: ~Chuck_VM_Code()
1324 //-----------------------------------------------------------------------------
1325 Chuck_VM_Code::~Chuck_VM_Code()
1327 // free instructions
1331 for( t_CKUINT i
= 0; i
< num_instr
; i
++ )
1335 SAFE_DELETE_ARRAY( instr
);
1344 // offset in bytes at the beginning of a stack for initializing data
1345 #define VM_STACK_OFFSET 16
1346 // 1/factor of stack is left blank, to give room to detect overflow
1347 #define VM_STACK_PADDING_FACTOR 16
1348 //-----------------------------------------------------------------------------
1349 // name: initialize()
1351 //-----------------------------------------------------------------------------
1352 t_CKBOOL
Chuck_VM_Stack::initialize( t_CKUINT size
)
1357 // make room for header
1358 size
+= VM_STACK_OFFSET
;
1360 stack
= new t_CKBYTE
[size
];
1361 if( !stack
) goto out_of_memory
;
1364 memset( stack
, 0, size
);
1366 // advance stack after the header
1367 stack
+= VM_STACK_OFFSET
;
1370 // upper limit (padding factor)
1371 sp_max
= sp
+ size
- (size
/ VM_STACK_PADDING_FACTOR
);
1373 // set flag and return
1374 return m_is_init
= TRUE
;
1378 // we have a problem
1380 "[chuck](VM): OutOfMemory: while allocating stack '%s'\n" );
1389 //-----------------------------------------------------------------------------
1392 //-----------------------------------------------------------------------------
1393 t_CKBOOL
Chuck_VM_Stack::shutdown()
1399 stack
-= VM_STACK_OFFSET
;
1400 SAFE_DELETE_ARRAY( stack
);
1403 // set the flag to false
1412 //-----------------------------------------------------------------------------
1413 // name: Chuck_VM_Shred()
1415 //-----------------------------------------------------------------------------
1416 Chuck_VM_Shred::Chuck_VM_Shred()
1418 mem
= new Chuck_VM_Stack
;
1419 reg
= new Chuck_VM_Stack
;
1424 // obj_array = NULL;
1425 // obj_array_size = 0;
1432 CK_TRACK( stat
= NULL
);
1438 //-----------------------------------------------------------------------------
1439 // name: ~Chuck_VM_Shred()
1441 //-----------------------------------------------------------------------------
1442 Chuck_VM_Shred::~Chuck_VM_Shred()
1450 //-----------------------------------------------------------------------------
1451 // name: initialize()
1453 //-----------------------------------------------------------------------------
1454 t_CKBOOL
Chuck_VM_Shred::initialize( Chuck_VM_Code
* c
,
1455 t_CKUINT mem_stack_size
,
1456 t_CKUINT reg_stack_size
)
1458 // allocate mem and reg
1459 if( !mem
->initialize( mem_stack_size
) ) return FALSE
;
1460 if( !reg
->initialize( reg_stack_size
) ) return FALSE
;
1466 code_orig
= code
= c
;
1468 code_orig
->add_ref();
1469 // shred in dump (all done)
1483 initialize_object( this, &t_shred
);
1491 //-----------------------------------------------------------------------------
1494 //-----------------------------------------------------------------------------
1495 t_CKBOOL
Chuck_VM_Shred::shutdown()
1497 // get iterator to our map
1498 map
<Chuck_UGen
*, Chuck_UGen
*>::iterator iter
= m_ugen_map
.begin();
1499 while( iter
!= m_ugen_map
.end() )
1501 (*iter
).first
->disconnect( TRUE
);
1510 // delete temp pointer space
1511 // SAFE_DELETE_ARRAY( obj_array );
1512 // obj_array_size = 0;
1514 // TODO: is this right?
1515 code_orig
->release();
1516 code_orig
= code
= NULL
;
1517 // what to do with next and prev?
1525 //-----------------------------------------------------------------------------
1528 //-----------------------------------------------------------------------------
1529 t_CKBOOL
Chuck_VM_Shred::add( Chuck_UGen
* ugen
)
1531 if( m_ugen_map
[ugen
] )
1534 m_ugen_map
[ugen
] = ugen
;
1541 //-----------------------------------------------------------------------------
1544 //-----------------------------------------------------------------------------
1545 t_CKBOOL
Chuck_VM_Shred::remove( Chuck_UGen
* ugen
)
1547 if( !m_ugen_map
[ugen
] )
1551 m_ugen_map
.erase( ugen
);
1558 //-----------------------------------------------------------------------------
1561 //-----------------------------------------------------------------------------
1562 t_CKBOOL
Chuck_VM_Shred::run( Chuck_VM
* vm
)
1565 instr
= code
->instr
;
1567 t_CKBOOL
* vm_running
= &vm
->m_running
;
1570 while( is_running
&& *vm_running
&& !is_abort
)
1572 // execute the instruction
1573 instr
[pc
]->execute( vm
, this );
1579 // track number of cycles
1580 CK_TRACK( this->stat
->cycles
++ );
1587 EM_log( CK_LOG_SYSTEM
, "aborting shred (id: %d)", this->xid
);
1592 // is the shred finished
1599 //-----------------------------------------------------------------------------
1600 // name: Chuck_VM_Shreduler()
1602 //-----------------------------------------------------------------------------
1603 Chuck_VM_Shreduler::Chuck_VM_Shreduler()
1609 m_current_shred
= NULL
;
1613 m_num_dac_channels
= 0;
1614 m_num_adc_channels
= 0;
1620 //-----------------------------------------------------------------------------
1621 // name: ~Chuck_VM_Shreduler()
1623 //-----------------------------------------------------------------------------
1624 Chuck_VM_Shreduler::~Chuck_VM_Shreduler()
1632 //-----------------------------------------------------------------------------
1633 // name: initialize()
1635 //-----------------------------------------------------------------------------
1636 t_CKBOOL
Chuck_VM_Shreduler::initialize()
1644 //-----------------------------------------------------------------------------
1647 //-----------------------------------------------------------------------------
1648 t_CKBOOL
Chuck_VM_Shreduler::shutdown()
1656 //-----------------------------------------------------------------------------
1657 // name: add_blocked()
1658 // desc: add shred to the shreduler's blocked list
1659 //-----------------------------------------------------------------------------
1660 t_CKBOOL
Chuck_VM_Shreduler::add_blocked( Chuck_VM_Shred
* shred
)
1662 // add shred to map, using pointer
1663 blocked
[shred
] = shred
;
1671 //-----------------------------------------------------------------------------
1672 // name: remove_blocked()
1673 // desc: remove shred from the shreduler's blocked list
1674 //-----------------------------------------------------------------------------
1675 t_CKBOOL
Chuck_VM_Shreduler::remove_blocked( Chuck_VM_Shred
* shred
)
1678 std::map
<Chuck_VM_Shred
*, Chuck_VM_Shred
*>::iterator iter
;
1679 iter
= blocked
.find( shred
);
1680 blocked
.erase( iter
);
1682 // remove from event
1683 if( shred
->event
!= NULL
) shred
->event
->remove( shred
);
1691 //-----------------------------------------------------------------------------
1694 //-----------------------------------------------------------------------------
1695 t_CKBOOL
Chuck_VM_Shreduler::shredule( Chuck_VM_Shred
* shred
)
1697 return this->shredule( shred
, now_system
);
1703 //-----------------------------------------------------------------------------
1706 //-----------------------------------------------------------------------------
1707 t_CKBOOL
Chuck_VM_Shreduler::shredule( Chuck_VM_Shred
* shred
,
1708 t_CKTIME wake_time
)
1711 if( shred
->prev
|| shred
->next
)
1713 // something is really wrong here - no shred can be
1714 // shreduled more than once
1715 EM_error3( "[chuck](VM): internal sanity check failed in shredule()" );
1716 EM_error3( "[chuck](VM): (shred shreduled while shreduled)" );
1722 if( wake_time
< (this->now_system
- .5) )
1724 // trying to enqueue on a time that is less than now
1725 EM_error3( "[chuck](VM): internal sanity check failed in shredule()" );
1726 EM_error3( "[chuck](VM): (wake time is past) - %f : %f", wake_time
, this->now_system
);
1731 shred
->wake_time
= wake_time
;
1738 // pointers to the shred queue
1739 Chuck_VM_Shred
* curr
= shred_list
;
1740 Chuck_VM_Shred
* prev
= NULL
;
1744 // found the place to insert
1745 if( curr
->wake_time
> wake_time
)
1754 shred
->next
= shred_list
;
1755 if( shred_list
) shred_list
->prev
= shred
;
1760 // insert the shred in sorted order
1761 shred
->next
= prev
->next
;
1763 if( prev
->next
) prev
->next
->prev
= shred
;
1773 //-----------------------------------------------------------------------------
1776 //-----------------------------------------------------------------------------
1777 void Chuck_VM_Shreduler::advance2( )
1779 // advance system 'now'
1780 this->now_system
+= 1;
1784 BBQ
* audio
= this->bbq
;
1789 audio
->digi_in()->tick_in( &l
, &r
);
1790 m_adc
->m_multi_chan
[0]->m_current
= l
* m_adc
->m_multi_chan
[0]->m_gain
;
1791 m_adc
->m_multi_chan
[1]->m_current
= r
* m_adc
->m_multi_chan
[1]->m_gain
;
1792 m_adc
->m_current
= .5f
* ( l
+ r
);
1794 m_adc
->m_multi_chan
[0]->m_time
= this->now_system
;
1795 m_adc
->m_multi_chan
[1]->m_time
= this->now_system
;
1796 m_adc
->m_time
= this->now_system
;
1800 m_dac
->system_tick( this->now_system
);
1801 l
= m_dac
->m_multi_chan
[0]->m_current
;
1802 r
= m_dac
->m_multi_chan
[1]->m_current
;
1804 // l *= .5f; r *= .5f;
1807 m_bunghole
->system_tick( this->now_system
);
1810 audio
->digi_out()->tick_out( l
, r
);
1816 //-----------------------------------------------------------------------------
1819 //-----------------------------------------------------------------------------
1820 void Chuck_VM_Shreduler::advance( )
1822 // advance system 'now'
1823 this->now_system
+= 1;
1828 BBQ
* audio
= this->bbq
;
1834 audio
->digi_in()->tick_in( frame
, m_num_adc_channels
);
1836 // loop over channels
1837 for( i
= 0; i
< m_num_adc_channels
; i
++ )
1839 m_adc
->m_multi_chan
[i
]->m_current
= frame
[i
] * m_adc
->m_multi_chan
[i
]->m_gain
* m_adc
->m_gain
;
1840 m_adc
->m_multi_chan
[i
]->m_time
= this->now_system
;
1841 sum
+= m_adc
->m_multi_chan
[i
]->m_current
;
1843 m_adc
->m_last
= m_adc
->m_current
= sum
/ m_num_adc_channels
;
1844 m_adc
->m_time
= this->now_system
;
1848 m_dac
->system_tick( this->now_system
);
1849 for( i
= 0; i
< m_num_dac_channels
; i
++ )
1850 frame
[i
] = m_dac
->m_multi_chan
[i
]->m_current
* .5f
;
1853 m_bunghole
->system_tick( this->now_system
);
1856 audio
->digi_out()->tick_out( frame
, m_num_dac_channels
);
1861 //-----------------------------------------------------------------------------
1864 //-----------------------------------------------------------------------------
1865 Chuck_VM_Shred
* Chuck_VM_Shreduler::get( )
1867 Chuck_VM_Shred
* shred
= shred_list
;
1873 // TODO: should this be <=?
1874 if( shred
->wake_time
<= ( this->now_system
+ .5 ) )
1876 // if( shred->wake_time < this->now_system )
1879 shred_list
= shred
->next
;
1884 shred_list
->prev
= NULL
;
1895 //-----------------------------------------------------------------------------
1898 //-----------------------------------------------------------------------------
1899 t_CKUINT
Chuck_VM_Shreduler::highest( )
1901 Chuck_VM_Shred
* shred
= shred_list
;
1906 if( shred
->xid
> n
) n
= shred
->xid
;
1907 shred
= shred
->next
;
1910 std::map
<Chuck_VM_Shred
*, Chuck_VM_Shred
*>::iterator iter
;
1911 for( iter
= blocked
.begin(); iter
!= blocked
.end(); iter
++ )
1913 shred
= (*iter
).second
;
1914 if( shred
->xid
> n
) n
= shred
->xid
;
1923 //-----------------------------------------------------------------------------
1926 //-----------------------------------------------------------------------------
1927 t_CKBOOL
Chuck_VM_Shreduler::replace( Chuck_VM_Shred
* out
, Chuck_VM_Shred
* in
)
1938 out
->prev
->next
= in
;
1941 out
->next
->prev
= in
;
1943 in
->next
= out
->next
;
1944 in
->prev
= out
->prev
;
1946 out
->next
= out
->prev
= NULL
;
1948 in
->wake_time
= out
->wake_time
;
1949 in
->start
= in
->wake_time
;
1957 //-----------------------------------------------------------------------------
1960 //-----------------------------------------------------------------------------
1961 t_CKBOOL
Chuck_VM_Shreduler::remove( Chuck_VM_Shred
* out
)
1963 if( !out
) return FALSE
;
1966 if( out
->event
!= NULL
)
1968 return remove_blocked( out
);
1972 if( !out
->prev
&& !out
->next
&& out
!= shred_list
)
1976 shred_list
= out
->next
;
1978 out
->prev
->next
= out
->next
;
1981 out
->next
->prev
= out
->prev
;
1983 out
->next
= out
->prev
= NULL
;
1991 //-----------------------------------------------------------------------------
1994 //-----------------------------------------------------------------------------
1995 Chuck_VM_Shred
* Chuck_VM_Shreduler::lookup( t_CKUINT xid
)
1997 Chuck_VM_Shred
* shred
= shred_list
;
2000 if( m_current_shred
!= NULL
&& m_current_shred
->xid
== xid
)
2001 return m_current_shred
;
2003 // look for in shreduled list
2006 if( shred
->xid
== xid
)
2009 shred
= shred
->next
;
2013 std::map
<Chuck_VM_Shred
*, Chuck_VM_Shred
*>::iterator iter
;
2014 for( iter
= blocked
.begin(); iter
!= blocked
.end(); iter
++ )
2016 shred
= (*iter
).second
;
2017 if( shred
->xid
== xid
)
2027 //-----------------------------------------------------------------------------
2030 //-----------------------------------------------------------------------------
2033 bool operator() ( const Chuck_VM_Shred
* lhs
, const Chuck_VM_Shred
* rhs
)
2034 { return lhs
->xid
< rhs
->xid
; }
2040 //-----------------------------------------------------------------------------
2043 //-----------------------------------------------------------------------------
2044 void Chuck_VM_Shreduler::status( Chuck_VM_Status
* status
)
2046 Chuck_VM_Shred
* shred
= shred_list
;
2047 Chuck_VM_Shred
* temp
= NULL
;
2049 t_CKUINT srate
= Digitalio::sampling_rate();
2050 t_CKUINT s
= (t_CKUINT
)now_system
;
2051 t_CKUINT h
= s
/(srate
*3600);
2052 s
= s
- (h
*(srate
*3600));
2053 t_CKUINT m
= s
/ (srate
*60);
2054 s
= s
- (m
*(srate
*60));
2055 t_CKUINT sec
= s
/ srate
;
2056 s
= s
- (sec
*(srate
));
2057 // float millisecond = s / (float)(srate) * 1000.0f;
2059 status
->srate
= srate
;
2060 status
->now_system
= now_system
;
2061 status
->t_second
= sec
;
2062 status
->t_minute
= m
;
2065 // get list of shreds
2066 vector
<Chuck_VM_Shred
*> list
;
2069 list
.push_back( shred
);
2070 shred
= shred
->next
;
2074 std::map
<Chuck_VM_Shred
*, Chuck_VM_Shred
*>::iterator iter
;
2075 for( iter
= blocked
.begin(); iter
!= blocked
.end(); iter
++ )
2077 shred
= (*iter
).second
;
2078 list
.push_back( shred
);
2081 // get current shred
2082 if( temp
= m_current_shred
)
2083 list
.push_back( temp
);
2087 std::sort( list
.begin(), list
.end(), byid
);
2091 for( t_CKUINT i
= 0; i
< list
.size(); i
++ )
2094 status
->list
.push_back( new Chuck_VM_Shred_Status(
2095 shred
->xid
, shred
->name
, shred
->start
, shred
->event
!= NULL
) );
2102 //-----------------------------------------------------------------------------
2105 //-----------------------------------------------------------------------------
2106 void Chuck_VM_Shreduler::status( )
2108 Chuck_VM_Shred_Status
* shred
= NULL
;
2110 this->status( &m_status
);
2111 t_CKUINT h
= m_status
.t_hour
;
2112 t_CKUINT m
= m_status
.t_minute
;
2113 t_CKUINT sec
= m_status
.t_second
;
2114 fprintf( stdout
, "[chuck](VM): status (now == %ldh%ldm%lds, %.1f samps) ...\n",
2115 h
, m
, sec
, m_status
.now_system
);
2118 for( t_CKUINT i
= 0; i
< m_status
.list
.size(); i
++ )
2120 shred
= m_status
.list
[i
];
2122 " [shred id]: %ld [source]: %s [spork time]: %.2fs ago%s\n",
2123 shred
->xid
, mini( shred
->name
.c_str() ),
2124 (m_status
.now_system
- shred
->start
) / m_status
.srate
,
2125 shred
->has_event
? " (blocked)" : "" );
2132 //-----------------------------------------------------------------------------
2133 // name: Chuck_VM_Status()
2135 //-----------------------------------------------------------------------------
2136 Chuck_VM_Status::Chuck_VM_Status()
2140 t_second
= t_minute
= t_hour
= 0;
2146 //-----------------------------------------------------------------------------
2147 // name: ~Chuck_VM_Status()
2149 //-----------------------------------------------------------------------------
2150 Chuck_VM_Status::~Chuck_VM_Status()
2158 //-----------------------------------------------------------------------------
2161 //-----------------------------------------------------------------------------
2162 void Chuck_VM_Status::clear()
2164 for( t_CKUINT i
= 0; i
< list
.size(); i
++ )
2166 SAFE_DELETE( list
[i
] );