/*---------------------------------------------------------------------------- ChucK Concurrent, On-the-fly Audio Programming Language Compiler and Virtual Machine Copyright (c) 2003 Ge Wang and Perry R. Cook. All rights reserved. http://chuck.stanford.edu/ http://chuck.cs.princeton.edu/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 U.S.A. -----------------------------------------------------------------------------*/ //----------------------------------------------------------------------------- // file: chuck.cpp // desc: chuck engine chasis; VM + compiler + state; independent of audio I/O // REFACTOR-2017 // // author: Ge Wang (https://ccrma.stanford.edu/~ge/) // date: fall 2017 // // additional authors: // Jack Atherton (lja@ccrma.stanford.edu) // Spencer Salazar (spencer@ccrma.stanford.edu) //----------------------------------------------------------------------------- #include "chuck.h" #include "chuck_errmsg.h" #include "chuck_io.h" #include "chuck_globals.h" // added 1.4.1.0 #ifndef __DISABLE_OTF_SERVER__ #include "chuck_otf.h" #include "ulib_machine.h" #endif #ifndef __DISABLE_NETWORK__ #include "util_network.h" #endif #include "util_string.h" #include "ugen_stk.h" #ifndef __PLATFORM_WIN32__ #include #include #include #else #include // added 1.3.0.0 #endif // #ifndef __PLATFORM_WIN32__ // chuck param defaults #define CHUCK_PARAM_SAMPLE_RATE_DEFAULT "44100" #define CHUCK_PARAM_INPUT_CHANNELS_DEFAULT "2" #define CHUCK_PARAM_OUTPUT_CHANNELS_DEFAULT "2" #define CHUCK_PARAM_VM_ADAPTIVE_DEFAULT "0" #define CHUCK_PARAM_VM_HALT_DEFAULT "0" #define CHUCK_PARAM_OTF_ENABLE_DEFAULT "0" #define CHUCK_PARAM_OTF_PORT_DEFAULT "8888" #define CHUCK_PARAM_DUMP_INSTRUCTIONS_DEFAULT "0" #define CHUCK_PARAM_AUTO_DEPEND_DEFAULT "0" #define CHUCK_PARAM_DEPRECATE_LEVEL_DEFAULT "1" #define CHUCK_PARAM_WORKING_DIRECTORY_DEFAULT "" #define CHUCK_PARAM_CHUGIN_ENABLE_DEFAULT "1" #define CHUCK_PARAM_HINT_IS_REALTIME_AUDIO_DEFAULT "0" #ifndef __PLATFORM_WIN32__ // 1.4.1.0 (ge) changed to ""; was "/usr/local/lib/chuck" // redundant with g_default_chugin_path, which already contains #define CHUCK_PARAM_CHUGIN_DIRECTORY_DEFAULT "" #else // __PLATFORM_WIN32__ // 1.4.1.0 (ge) changed to ""; "C:\\Program Files\\ChucK\\chugins" // redundant with g_default_chugin_path, which already contains #define CHUCK_PARAM_CHUGIN_DIRECTORY_DEFAULT "" #endif // __PLATFORM_WIN32__ #define CHUCK_PARAM_USER_CHUGINS_DEFAULT std::list() #define CHUCK_PARAM_USER_CHUGIN_DIRECTORIES_DEFAULT std::list() // chuck statics const char ChucK::VERSION[] = CHUCK_VERSION_STRING; t_CKUINT ChucK::o_numVMs = 0; t_CKBOOL ChucK::o_isGlobalInit = FALSE; t_CKBOOL ChucK::enableSystemCall = FALSE; //----------------------------------------------------------------------------- // name: version() // desc: get chuck version //----------------------------------------------------------------------------- const char * ChucK::version() { return VERSION; } //----------------------------------------------------------------------------- // name: intSize() // desc: get chuck int size (in bits) //----------------------------------------------------------------------------- t_CKUINT ChucK::intSize() { return machine_intsize(); } //----------------------------------------------------------------------------- // name: ChucK() // desc: constructor //----------------------------------------------------------------------------- ChucK::ChucK() { // instantiate the carrier! m_carrier = new Chuck_Carrier(); // set reference back to this m_carrier->chuck = this; // increment the numChucKs o_numVMs++; // initialize default params initDefaultParams(); // did user init? m_init = FALSE; // did user start ChucK? m_started = FALSE; // zero out the hook m_hook = NULL; // m_hook = nullptr // global init, if needed if( !o_isGlobalInit ) globalInit(); } //----------------------------------------------------------------------------- // name: ~ChucK() // desc: destructor //----------------------------------------------------------------------------- ChucK::~ChucK() { // shutdown shutdown(); // decrement the numChucKs o_numVMs--; // clean up the carrier SAFE_DELETE( m_carrier ); } // REFACTOR-2017: TODO Ge: Implementation of this? //----------------------------------------------------------------------------- // name: ck_param_type // desc: enum for type of param (int, float, string) //----------------------------------------------------------------------------- enum ck_param_type { ck_param_int, ck_param_float, ck_param_string, ck_param_string_list }; //----------------------------------------------------------------------------- // name: ck_param_types // desc: storage for types of param (int, float, string) //----------------------------------------------------------------------------- std::map< std::string, ck_param_type> ck_param_types; //----------------------------------------------------------------------------- // name: initDefaultParams() // desc: initialize default params //----------------------------------------------------------------------------- void ChucK::initDefaultParams() { m_params[CHUCK_PARAM_SAMPLE_RATE] = CHUCK_PARAM_SAMPLE_RATE_DEFAULT; m_params[CHUCK_PARAM_INPUT_CHANNELS] = CHUCK_PARAM_INPUT_CHANNELS_DEFAULT; m_params[CHUCK_PARAM_OUTPUT_CHANNELS] = CHUCK_PARAM_OUTPUT_CHANNELS_DEFAULT; m_params[CHUCK_PARAM_VM_ADAPTIVE] = CHUCK_PARAM_VM_ADAPTIVE_DEFAULT; m_params[CHUCK_PARAM_VM_HALT] = CHUCK_PARAM_VM_HALT_DEFAULT; m_params[CHUCK_PARAM_OTF_ENABLE] = CHUCK_PARAM_OTF_ENABLE_DEFAULT; m_params[CHUCK_PARAM_OTF_PORT] = CHUCK_PARAM_OTF_PORT_DEFAULT; m_params[CHUCK_PARAM_DUMP_INSTRUCTIONS] = CHUCK_PARAM_DUMP_INSTRUCTIONS_DEFAULT; m_params[CHUCK_PARAM_AUTO_DEPEND] = CHUCK_PARAM_AUTO_DEPEND_DEFAULT; m_params[CHUCK_PARAM_DEPRECATE_LEVEL] = CHUCK_PARAM_DEPRECATE_LEVEL_DEFAULT; m_params[CHUCK_PARAM_WORKING_DIRECTORY] = CHUCK_PARAM_WORKING_DIRECTORY_DEFAULT; m_params[CHUCK_PARAM_CHUGIN_DIRECTORY] = CHUCK_PARAM_CHUGIN_DIRECTORY_DEFAULT; m_params[CHUCK_PARAM_CHUGIN_ENABLE] = CHUCK_PARAM_CHUGIN_ENABLE_DEFAULT; m_listParams[CHUCK_PARAM_USER_CHUGINS] = CHUCK_PARAM_USER_CHUGINS_DEFAULT; m_listParams[CHUCK_PARAM_USER_CHUGIN_DIRECTORIES] = CHUCK_PARAM_USER_CHUGIN_DIRECTORIES_DEFAULT; m_params[CHUCK_PARAM_HINT_IS_REALTIME_AUDIO] = CHUCK_PARAM_HINT_IS_REALTIME_AUDIO_DEFAULT; ck_param_types[CHUCK_PARAM_SAMPLE_RATE] = ck_param_int; ck_param_types[CHUCK_PARAM_INPUT_CHANNELS] = ck_param_int; ck_param_types[CHUCK_PARAM_OUTPUT_CHANNELS] = ck_param_int; ck_param_types[CHUCK_PARAM_VM_ADAPTIVE] = ck_param_int; ck_param_types[CHUCK_PARAM_VM_HALT] = ck_param_int; ck_param_types[CHUCK_PARAM_OTF_ENABLE] = ck_param_int; ck_param_types[CHUCK_PARAM_OTF_PORT] = ck_param_int; ck_param_types[CHUCK_PARAM_DUMP_INSTRUCTIONS] = ck_param_int; ck_param_types[CHUCK_PARAM_AUTO_DEPEND] = ck_param_int; ck_param_types[CHUCK_PARAM_DEPRECATE_LEVEL] = ck_param_int; ck_param_types[CHUCK_PARAM_WORKING_DIRECTORY] = ck_param_string; ck_param_types[CHUCK_PARAM_CHUGIN_DIRECTORY] = ck_param_string; ck_param_types[CHUCK_PARAM_CHUGIN_ENABLE] = ck_param_int; ck_param_types[CHUCK_PARAM_USER_CHUGINS] = ck_param_string_list; ck_param_types[CHUCK_PARAM_USER_CHUGIN_DIRECTORIES] = ck_param_string_list; ck_param_types[CHUCK_PARAM_HINT_IS_REALTIME_AUDIO] = ck_param_int; } //----------------------------------------------------------------------------- // name: setParam() // desc: set an int param by name //----------------------------------------------------------------------------- t_CKBOOL ChucK::setParam( const std::string & name, t_CKINT value ) { if( m_params.count( name ) > 0 && ck_param_types[name] == ck_param_int ) { std::ostringstream s; s << value; m_params[name] = s.str(); return TRUE; } else { return FALSE; } } //----------------------------------------------------------------------------- // name: setParamFloat() // desc: set a float param by name //----------------------------------------------------------------------------- t_CKBOOL ChucK::setParamFloat( const std::string & name, t_CKFLOAT value ) { if( m_params.count( name ) > 0 && ck_param_types[name] == ck_param_float ) { std::ostringstream s; s << value; m_params[name] = s.str(); return TRUE; } else { return FALSE; } } //----------------------------------------------------------------------------- // name: setParam() // desc: set a string param by name //----------------------------------------------------------------------------- t_CKBOOL ChucK::setParam( const std::string & name, const std::string & value ) { if( m_params.count( name ) > 0 && ck_param_types[name] == ck_param_string ) { m_params[name] = value; return TRUE; } else { return FALSE; } } //----------------------------------------------------------------------------- // name: setParam() // desc: set a string list param by name //----------------------------------------------------------------------------- t_CKBOOL ChucK::setParam( const std::string & name, const std::list< std::string > & value ) { if( m_listParams.count( name ) > 0 && ck_param_types[name] == ck_param_string_list ) { m_listParams[name] = value; return TRUE; } else { return FALSE; } } //----------------------------------------------------------------------------- // name: getParamInt() // desc: get an int param //----------------------------------------------------------------------------- t_CKINT ChucK::getParamInt( const std::string & key ) { t_CKINT result = 0; if( m_params.count( key ) > 0 && ck_param_types[key] == ck_param_int ) { std::istringstream s( m_params[key] ); s >> result; } return result; } //----------------------------------------------------------------------------- // name: getParamFloat() // desc: get a float param //----------------------------------------------------------------------------- t_CKFLOAT ChucK::getParamFloat( const std::string & key ) { t_CKFLOAT result = 0; if( m_params.count( key ) > 0 && ck_param_types[key] == ck_param_float ) { std::istringstream s( m_params[key] ); s >> result; } return result; } //----------------------------------------------------------------------------- // name: getParamString() // desc: get a string param //----------------------------------------------------------------------------- std::string ChucK::getParamString( const std::string & key ) { if( m_params.count( key ) > 0 && ck_param_types[key] == ck_param_string ) { return m_params[key]; } else { return ""; } } //----------------------------------------------------------------------------- // name: getParamStringList() // desc: get a string param //----------------------------------------------------------------------------- std::list< std::string > ChucK::getParamStringList( const std::string & key ) { if( m_listParams.count( key ) > 0 && ck_param_types[key] == ck_param_string_list ) { return m_listParams[key]; } else { return std::list< std::string >(); } } //----------------------------------------------------------------------------- // name: init() // desc: initialize ChucK (using params) //----------------------------------------------------------------------------- t_CKBOOL ChucK::init() { // sanity check if( m_init == TRUE ) { CK_FPRINTF_STDERR( "[chuck]: VM already initialized...\n" ); return false; } // initialize VM if( !initVM() ) goto cleanup; // initialize compiler if( !initCompiler() ) goto cleanup; // initialize chugin system if( !initChugins() ) goto cleanup; // initialize OTF programming system if( !initOTF() ) goto cleanup; // did user init? m_init = TRUE; return true; cleanup: // shutdown, dealloc shutdown(); return false; } //----------------------------------------------------------------------------- // name: initVM() // desc: initialize VM //----------------------------------------------------------------------------- t_CKBOOL ChucK::initVM() { // get VM params t_CKUINT srate = getParamInt( CHUCK_PARAM_SAMPLE_RATE ); t_CKUINT outs = getParamInt( CHUCK_PARAM_OUTPUT_CHANNELS ); t_CKUINT ins = getParamInt( CHUCK_PARAM_INPUT_CHANNELS ); t_CKUINT adaptiveSize = getParamInt( CHUCK_PARAM_VM_ADAPTIVE ); t_CKBOOL halt = getParamInt( CHUCK_PARAM_VM_HALT ) != 0; // instantiate VM m_carrier->vm = new Chuck_VM(); // reference back to carrier m_carrier->vm->setCarrier( m_carrier ); // initialize VM if( !m_carrier->vm->initialize( srate, outs, ins, adaptiveSize, halt ) ) { CK_FPRINTF_STDERR( "[chuck]: %s\n", m_carrier->vm->last_error() ); return false; } return true; } //----------------------------------------------------------------------------- // name: initCompiler() // desc: initialize compiler //----------------------------------------------------------------------------- t_CKBOOL ChucK::initCompiler() { // get compiler params t_CKBOOL dump = getParamInt( CHUCK_PARAM_DUMP_INSTRUCTIONS ) != 0; t_CKBOOL auto_depend = getParamInt( CHUCK_PARAM_AUTO_DEPEND ) != 0; t_CKUINT deprecate = getParamInt( CHUCK_PARAM_DEPRECATE_LEVEL ); std::string workingDir = getParamString( CHUCK_PARAM_WORKING_DIRECTORY ); // log EM_log( CK_LOG_SYSTEM, "initializing compiler..." ); // push indent level EM_pushlog(); // instantiate compiler m_carrier->compiler = new Chuck_Compiler(); // reference back to carrier m_carrier->compiler->setCarrier( m_carrier ); // initialize compiler if( !m_carrier->compiler->initialize() ) { CK_FPRINTF_STDERR( "[chuck]: compiler failed to initialize...\n" ); return false; } // set dump flag m_carrier->compiler->emitter->dump = dump; // set auto depend flag (for type checker) | currently must be FALSE m_carrier->compiler->set_auto_depend( auto_depend ); // set deprecation level m_carrier->env->deprecate_level = deprecate; // pop indent for compiler EM_poplog(); // log EM_log( CK_LOG_SYSTEM, "initializing synthesis engine..." ); // push indent for synthesis EM_pushlog(); // VM + type system integration (needs to be done after compiler) if( !m_carrier->vm->initialize_synthesis() ) { CK_FPRINTF_STDERR( "[chuck]: %s\n", m_carrier->vm->last_error() ); return false; } // pop indent EM_poplog(); std::string cwd; char * cstr_cwd = NULL; // 1.5.0.0 (barak) | was: char cstr_cwd[MAXPATHLEN]; // figure out current working directory (added 1.3.0.0) // is this needed for current path to work correctly?! // was: if( getcwd(cstr_cwd, MAXPATHLEN) == NULL ) // let getcwd allocate memory | 1.5.0.0 (barak) if( (cstr_cwd = getcwd(NULL, 0)) == NULL ) { // uh... EM_log( CK_LOG_SEVERE, "error: unable to determine current working directory!" ); } else { // make c++ string cwd = std::string(cstr_cwd); // reclaim memory from getcwd SAFE_FREE(cstr_cwd); // add trailing "/" cwd += '/'; // deferring this step until later, and only for Windows // cwd = normalize_directory_separator(cwd) + "/"; // 1.4.1.1 (ge) added -- expand for things like ~ workingDir = expand_filepath( workingDir, TRUE ); // 1.4.1.0 (ge) added -- test for absolute path // 1.4.1.1 (ge) deals with windows absolute paths: e.g., "C:\" // TODO: deals with windows network paths: e.g., "\\server\"??? if( is_absolute_path(workingDir) ) { // do nothing here } #ifdef __ANDROID__ else if( workingDir.rfind("jar:", 0) == 0 ) { // if workingDir is a JAR URL, make sure it ends on / if( *workingDir.rbegin() != '/' ) { workingDir = workingDir + "/"; } // log it // EM_log( CK_LOG_INFO, "setting current working directory: " ); } #endif else { // update workingDir = cwd + workingDir; } #ifdef __PLATFORM_WIN32__ // normalize path separators, only for windows; in case UNIX-style paths contains actual backslashes workingDir = normalize_directory_separator(workingDir); #endif // check if need to add / // (note if workingDir is empty string, then this leaves it alone) if( workingDir.length() > 0 && (workingDir[workingDir.length() - 1] != '/') ) { // append workingDir = workingDir + "/"; } // update to current working directory setParam( CHUCK_PARAM_WORKING_DIRECTORY, workingDir ); // log it EM_log( CK_LOG_INFO, "setting current working directory: " ); // push log EM_pushlog(); // print the working directory EM_log( CK_LOG_INFO, "%s", workingDir.c_str() ); // pop log EM_poplog(); } return true; } //----------------------------------------------------------------------------- // name: initChugin() // desc: initialize chugin system //----------------------------------------------------------------------------- t_CKBOOL ChucK::initChugins() { Chuck_VM_Code * code = NULL; Chuck_VM_Shred * shred = NULL; // print whether chugins enabled EM_log( CK_LOG_SYSTEM, "chugin system: %s", getParamInt( CHUCK_PARAM_CHUGIN_ENABLE ) ? "ON" : "OFF" ); // whether or not chug should be enabled (added 1.3.0.0) if( getParamInt( CHUCK_PARAM_CHUGIN_ENABLE ) != 0 ) { // chugin dur std::string chuginDir = getParamString( CHUCK_PARAM_CHUGIN_DIRECTORY ); // list of search pathes (added 1.3.0.0) std::list dl_search_path = getParamStringList( CHUCK_PARAM_USER_CHUGIN_DIRECTORIES ); if( chuginDir != std::string("") ) { dl_search_path.push_back( chuginDir ); } // list of individually named chug-ins (added 1.3.0.0) std::list named_dls = getParamStringList( CHUCK_PARAM_USER_CHUGINS ); EM_pushlog(); // print host version EM_log( CK_LOG_SEVERE, "host version: %d.%d", CK_DLL_VERSION_MAJOR, CK_DLL_VERSION_MINOR ); EM_poplog(); //--------------------------------------------------------------------- // set origin hint | 1.5.0.0 (ge) added m_carrier->compiler->m_originHint = te_originChugin; //--------------------------------------------------------------------- // log EM_log( CK_LOG_SYSTEM, "loading chugins..." ); // push indent level // EM_pushlog(); // load external libs | 1.5.0.4 (ge) enabled recursive search if( !compiler()->load_external_modules( ".chug", dl_search_path, named_dls, TRUE ) ) { // pop indent level EM_poplog(); // clean up goto error; } // pop log // EM_poplog(); //--------------------------------------------------------------------- // set origin hint | 1.5.0.0 (ge) added m_carrier->compiler->m_originHint = te_originImport; //--------------------------------------------------------------------- // log EM_log( CK_LOG_SYSTEM, "loading chuck extensions..." ); EM_pushlog(); // iterate over list of ck files that the compiler found for( std::list::iterator j = compiler()->m_cklibs_to_preload.begin(); j != compiler()->m_cklibs_to_preload.end(); j++) { // the filename std::string filename = *j; // log EM_log( CK_LOG_SEVERE, "preloading '%s'...", filename.c_str() ); // push indent EM_pushlog(); // SPENCERTODO: what to do for full path std::string full_path = filename; // parse, type-check, and emit if( compiler()->go( filename, full_path ) ) { // TODO: how to compilation handle? //return 1; // get the code code = compiler()->output(); // name it - TODO? // code->name += string(argv[i]); // spork it shred = vm()->spork( code, NULL, TRUE ); } // pop indent EM_poplog(); } // clear the list of chuck files to preload compiler()->m_cklibs_to_preload.clear(); // pop log EM_poplog(); return true; } else { // log | 1.4.1.0 (ge) commented out; printing earlier EM_log( CK_LOG_SYSTEM, "chugin system: OFF" ); } // load user namespace m_carrier->env->load_user_namespace(); // unset origin hint | 1.5.0.0 (ge) added m_carrier->compiler->m_originHint = te_originUnknown; return true; error: // 1.4.1.0 (ge) added // unset origin hint | 1.5.0.0 (ge) added m_carrier->compiler->m_originHint = te_originUnknown; return false; } //----------------------------------------------------------------------------- // name: probeChugins() // desc: probe chugin system and print output //----------------------------------------------------------------------------- void ChucK::probeChugins() { // ck files to pre load std::list ck_libs_to_preload; // print whether chugins enabled EM_log( CK_LOG_SYSTEM, "chugin system: %s", getParamInt( CHUCK_PARAM_CHUGIN_ENABLE ) ? "ON" : "OFF" ); // print host version EM_log( CK_LOG_SYSTEM, "chuck host version: %d.%d", CK_DLL_VERSION_MAJOR, CK_DLL_VERSION_MINOR ); // push EM_pushlog(); // print host version EM_log( CK_LOG_SYSTEM, "chugin major version must == host major version" ); EM_log( CK_LOG_SYSTEM, "chugin minor version must <= host major version" ); // pop EM_poplog(); // chugin dur std::string chuginDir = getParamString( CHUCK_PARAM_CHUGIN_DIRECTORY ); // list of search pathes (added 1.3.0.0) std::list dl_search_path = getParamStringList( CHUCK_PARAM_USER_CHUGIN_DIRECTORIES ); if( chuginDir != "" ) { // add to search path dl_search_path.push_back( chuginDir ); } // list of individually named chug-ins (added 1.3.0.0) std::list named_dls = getParamStringList( CHUCK_PARAM_USER_CHUGINS ); // log EM_log( CK_LOG_SYSTEM, "probing chugins (.chug)..." ); // push indent level // EM_pushlog(); // load external libs if( !Chuck_Compiler::probe_external_modules( ".chug", dl_search_path, named_dls, TRUE, ck_libs_to_preload ) ) { // warning EM_log( CK_LOG_SYSTEM, "error probing chugins..." ); } // pop log // EM_poplog(); // log EM_log( CK_LOG_SYSTEM, "probing auto-load chuck files (.ck)..." ); EM_pushlog(); // iterate over list of ck files that the compiler found for( std::list::iterator j = ck_libs_to_preload.begin(); j != ck_libs_to_preload.end(); j++ ) { // the filename std::string filename = *j; // log EM_log( CK_LOG_SYSTEM, "[SOURCE] '%s'...", filename.c_str() ); } // check if( ck_libs_to_preload.size() == 0 ) EM_log( CK_LOG_INFO, "(no auto-load chuck files found)" ); // pop log EM_poplog(); } //----------------------------------------------------------------------------- // name: initOTF() // desc: init OTF programming system //----------------------------------------------------------------------------- t_CKBOOL ChucK::initOTF() { #ifndef __DISABLE_OTF_SERVER__ // server if( getParamInt( CHUCK_PARAM_OTF_ENABLE ) != 0 ) { m_carrier->otf_port = getParamInt( CHUCK_PARAM_OTF_PORT ); // log EM_log( CK_LOG_SYSTEM, "starting listener on port: %d...", m_carrier->otf_port ); // start tcp server m_carrier->otf_socket = ck_tcp_create( 1 ); if( !m_carrier->otf_socket || !ck_bind( m_carrier->otf_socket, (int)m_carrier->otf_port ) || !ck_listen( m_carrier->otf_socket, 10 ) ) { CK_FPRINTF_STDERR( "[chuck]: cannot bind to tcp port %li...\n", m_carrier->otf_port ); ck_close( m_carrier->otf_socket ); m_carrier->otf_socket = NULL; } else { #if !defined(__PLATFORM_WIN32__) || defined(__WINDOWS_PTHREAD__) pthread_create( &m_carrier->otf_thread, NULL, otf_cb, m_carrier ); #else m_carrier->otf_thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)otf_cb, m_carrier, 0, 0 ); #endif } } else { // log EM_log( CK_LOG_SYSTEM, "OTF server/listener: OFF" ); } #endif return true; } //----------------------------------------------------------------------------- // name: shutdown() // desc: shutdown ChucK instance //----------------------------------------------------------------------------- t_CKBOOL ChucK::shutdown() { // log EM_log( CK_LOG_SYSTEM, "shutting down ChucK instance..." ); // push EM_pushlog(); // stop VM if( m_carrier != NULL && m_carrier->vm != NULL ) { m_carrier->vm->stop(); // wait for it to stop... while( m_carrier->vm->running() ) { usleep(1000); } } // free vm, compiler, friends // first, otf // REFACTOR-2017 TODO: le_cb? // STK-specific stk_detach( m_carrier ); #ifndef __DISABLE_OTF_SERVER__ // cancel otf thread if( m_carrier!= NULL ) { // stop otf thread if( m_carrier->otf_thread ) { #if !defined(__PLATFORM_WIN32__) || defined(__WINDOWS_PTHREAD__) pthread_cancel( m_carrier->otf_thread ); #else CloseHandle( m_carrier->otf_thread ); // doesn't cancel thread #endif m_carrier->otf_thread = 0; // signals thread shutdown } // close otf socket if( m_carrier->otf_socket ) { ck_close( m_carrier->otf_socket ); } // reset m_carrier->otf_socket = NULL; m_carrier->otf_port = 0; } #endif // __DISABLE_OTF_SERVER__ // TODO: a different way to unlock? // unlock all objects to delete chout, cherr Chuck_VM_Object::unlock_all(); // ensure we have a carrier if( m_carrier != NULL ) { SAFE_RELEASE( m_carrier->chout ); SAFE_RELEASE( m_carrier->cherr ); } // relock Chuck_VM_Object::lock_all(); // ensure we have a carrier if( m_carrier != NULL ) { // clean up vm, compiler SAFE_DELETE( m_carrier->vm ); SAFE_DELETE( m_carrier->compiler ); m_carrier->env = NULL; } // clear flag m_init = FALSE; // clear flag m_started = FALSE; // log EM_log( CK_LOG_SYSTEM, "ChucK instance shutdown complete." ); // pop EM_poplog(); // done return true; } //----------------------------------------------------------------------------- // name: compileFile() // desc: compile a file (can be called anytime) //----------------------------------------------------------------------------- t_CKBOOL ChucK::compileFile( const std::string & path, const std::string & argsTogether, t_CKINT count ) { // sanity check if( !m_carrier->compiler ) { // error CK_FPRINTF_STDERR( "[chuck]: compileFile() invoked before initialization ...\n" ); return false; } std::string filename; std::vector args; Chuck_VM_Code * code = NULL; Chuck_VM_Shred * shred = NULL; std::string thePath; std::string full_path; //------------------------------------------------------------------------- // set origin hint | 1.5.0.0 (ge) added m_carrier->compiler->m_originHint = te_originUserDefined; //------------------------------------------------------------------------- // log EM_log( CK_LOG_INFO, "compiling '%s'...", path.c_str() ); // push indent EM_pushlog(); // path expansion (e.g., ~ for unix systems) thePath = expand_filepath(path); // log if the path exansion is different if( path != thePath ) { EM_log( CK_LOG_FINE, "expanding path '%s'...", thePath.c_str() ); } // append std::string theThing = thePath + ":" + argsTogether; #ifdef __ANDROID__ if( theThing.rfind("jar:", 0) == 0 ) { const size_t jarPrefixLen = strlen("jar:"); const size_t secondColon = path.find(':', jarPrefixLen); if( secondColon == std::string::npos ) { theThing = std::string("jar\\:") + theThing.substr(jarPrefixLen); } else { theThing = std::string("jar\\:") + theThing.substr(jarPrefixLen, secondColon - jarPrefixLen) + std::string("\\:") + theThing.substr(secondColon + 1); } } #endif // __ANDROID__ // parse out command line arguments if( !extract_args( theThing, filename, args ) ) { // error CK_FPRINTF_STDERR( "[chuck]: malformed filename with argument list...\n" ); CK_FPRINTF_STDERR( " --> '%s'", theThing.c_str() ); goto error; } // construct full path to be associated with the file so me.sourceDir() works // (added 1.3.0.0) full_path = get_full_path(filename); // parse, type-check, and emit (full_path added 1.3.0.0) if( !m_carrier->compiler->go( filename, full_path ) ) goto error; // get the code code = m_carrier->compiler->output(); // name it code->name += path; // log EM_log( CK_LOG_FINE, "sporking %d %s...", count, count == 1 ? "instance" : "instances" ); // spork it while( count > 0 ) // 1.5.0.0 (ge) | added changed to check for > 0, in case of negative count { #ifndef __EMSCRIPTEN__ // spork (for now, spork_immediate arg is always false) shred = m_carrier->vm->spork( code, NULL, FALSE ); #else // spork (in emscripten, need to spork immediately so can get shred id) shred = m_carrier->vm->spork( code, NULL, TRUE ); #endif // add args shred->args = args; // decrement count count--; } // pop indent EM_poplog(); // reset the parser reset_parse(); // unset origin hint | 1.5.0.0 (ge) added m_carrier->compiler->m_originHint = te_originUnknown; return true; error: // 1.5.0.0 (ge) added // unset origin hint | 1.5.0.0 (ge) added m_carrier->compiler->m_originHint = te_originUnknown; return false; } //----------------------------------------------------------------------------- // name: compileCode() // desc: compile code directly //----------------------------------------------------------------------------- t_CKBOOL ChucK::compileCode( const std::string & code, const std::string & argsTogether, t_CKINT count ) { // sanity check if( !m_carrier->compiler ) { // error CK_FPRINTF_STDERR( "[chuck]: compileCode() invoked before initialization ...\n" ); return false; } std::vector args; Chuck_VM_Code * vm_code = NULL; Chuck_VM_Shred * shred = NULL; std::string workingDir; std::string full_path; //------------------------------------------------------------------------- // set origin hint | 1.5.0.0 (ge) added m_carrier->compiler->m_originHint = te_originUserDefined; //------------------------------------------------------------------------- // log EM_log( CK_LOG_FINE, "compiling code from string..." ); // push indent EM_pushlog(); // falsify filename / path for various logs std::string theThing = "compiled.code:" + argsTogether; std::string fakefakeFilename = ""; // parse out command line arguments if( !extract_args( theThing, fakefakeFilename, args ) ) { // error CK_FPRINTF_STDERR( "[chuck]: malformed filename with argument list...\n" ); CK_FPRINTF_STDERR( " --> '%s'", theThing.c_str() ); goto error; } // working directory workingDir = getParamString( CHUCK_PARAM_WORKING_DIRECTORY ); // construct full path to be associated with the file so me.sourceDir() works full_path = workingDir + "/compiled.code"; // log EM_log( CK_LOG_FINE, "full path: %s...", full_path.c_str() ); // parse, type-check, and emit (full_path added 1.3.0.0) if( !m_carrier->compiler->go( "", full_path, code ) ) goto error; // get the code vm_code = m_carrier->compiler->output(); // name it (no path to append) vm_code->name += "compiled.code"; // log EM_log( CK_LOG_FINE, "sporking %d %s...", count, count == 1 ? "instance" : "instances" ); // spork it while( count-- ) { #ifndef __EMSCRIPTEN__ // spork (for now, spork_immediate arg is always false) shred = m_carrier->vm->spork( vm_code, NULL, FALSE ); #else // spork (in emscripten, need to spork immediately so can get shred id) shred = m_carrier->vm->spork( vm_code, NULL, TRUE ); #endif // add args shred->args = args; } // pop indent EM_poplog(); // reset the parser reset_parse(); // unset origin hint | 1.5.0.0 (ge) added m_carrier->compiler->m_originHint = te_originUnknown; return true; error: // 1.5.0.0 (ge) added // unset origin hint | 1.5.0.0 (ge) added m_carrier->compiler->m_originHint = te_originUnknown; return false; } //----------------------------------------------------------------------------- // name: start() // desc: start chuck instance //----------------------------------------------------------------------------- t_CKBOOL ChucK::start() { // sanity check if( m_carrier->vm == NULL ) { CK_FPRINTF_STDERR( "[chuck]: cannot start, VM not initialized...\n" ); return false; } // if already started if( m_started ) { // return VM running state return m_carrier->vm->running(); } // start the VM! if( !m_carrier->vm->running() ) m_carrier->vm->start(); // set flag m_started = TRUE; // return state return m_carrier->vm->running(); } //----------------------------------------------------------------------------- // name: run() // desc: run engine (call from host callback) //----------------------------------------------------------------------------- void ChucK::run( SAMPLE * input, SAMPLE * output, t_CKINT numFrames ) { // make sure we started... if( !m_started ) this->start(); // call the callback m_carrier->vm->run( numFrames, input, output ); } //----------------------------------------------------------------------------- // name: globals() // desc: returns VM Globals Manager //----------------------------------------------------------------------------- Chuck_Globals_Manager * ChucK::globals() { // check pointer if( !m_carrier->vm ) return NULL; // check if running if( !m_carrier->vm->running() ) return NULL; // return the thing! return m_carrier->vm->globals_manager(); } //----------------------------------------------------------------------------- // name: setChoutCallback() // desc: provide a callback where Chout print statements are routed //----------------------------------------------------------------------------- t_CKBOOL ChucK::setChoutCallback( void (* callback)(const char *) ) { if( !m_init || m_carrier->chout == NULL ) return FALSE; m_carrier->chout->set_output_callback( callback ); return TRUE; } //----------------------------------------------------------------------------- // name: setCherrCallback() // desc: provide a callback where Cherr print statements are routed //----------------------------------------------------------------------------- t_CKBOOL ChucK::setCherrCallback( void (* callback)(const char *) ) { if( !m_init || m_carrier->cherr == NULL ) return FALSE; m_carrier->cherr->set_output_callback( callback ); return TRUE; } //----------------------------------------------------------------------------- // name: setStdoutCallback() // desc: provide a callback where stdout print statements are routed //----------------------------------------------------------------------------- t_CKBOOL ChucK::setStdoutCallback( void (* callback)(const char *) ) { ck_set_stdout_callback( callback ); return TRUE; } //----------------------------------------------------------------------------- // name: setStderrCallback() // desc: provide a callback where <<< >>> and stdout print statements are routed //----------------------------------------------------------------------------- t_CKBOOL ChucK::setStderrCallback( void (* callback)(const char *) ) { ck_set_stderr_callback( callback ); return TRUE; } //----------------------------------------------------------------------------- // name: globalInit() // desc: init all things once (for all ChucK instances) //----------------------------------------------------------------------------- t_CKBOOL ChucK::globalInit() { // sanity check if( o_isGlobalInit ) return FALSE; // nothing to do, for now // set flag o_isGlobalInit = TRUE; // done return TRUE; } //----------------------------------------------------------------------------- // name: globalCleanup() // desc: clean up all things //----------------------------------------------------------------------------- void ChucK::globalCleanup() { // sanity check if( !o_isGlobalInit ) return; // set flag o_isGlobalInit = FALSE; // log EM_log( CK_LOG_INFO, "detaching all resources..." ); // push EM_pushlog(); //#ifndef __DISABLE_MIDI__ // close midi file handles // midirw_detach(); //#endif // __DISABLE_MIDI__ #ifndef __ALTER_HID__ // shutdown HID HidInManager::cleanup(); #endif // __ALTER_HID__ #ifndef __DISABLE_SERIAL__ // shutdown serial Chuck_IO_Serial::shutdown(); #endif #ifndef __DISABLE_KBHIT__ // shutdown kb loop // KBHitManager::shutdown(); #endif // __DISABLE_KBHIT__ // pop EM_poplog(); // REFACTOR-2017 TODO Ge: // stop le_cb, ...? } //----------------------------------------------------------------------------- // set log level -- this should eventually be per-VM? //----------------------------------------------------------------------------- void ChucK::setLogLevel( t_CKINT level ) { EM_setlog( level ); } //----------------------------------------------------------------------------- // get log level -- also per-VM? //----------------------------------------------------------------------------- t_CKINT ChucK::getLogLevel() { return g_loglevel; } //----------------------------------------------------------------------------- // name: poop() // desc: --poop //----------------------------------------------------------------------------- void ChucK::poop() { #ifndef __DISABLE_OTF_SERVER__ ::uh(); #endif } //----------------------------------------------------------------------------- // name: setMainThreadHook() // desc: set a function pointer to call from the main thread loop // e.g., for graphics, MAUI, potentially from a chug-in //----------------------------------------------------------------------------- t_CKBOOL ChucK::setMainThreadHook(Chuck_DL_MainThreadHook * hook) { m_hook = hook; return true; } //----------------------------------------------------------------------------- // name: getMainThreadHook() // desc: get pointer to current function to be called from main loop //----------------------------------------------------------------------------- Chuck_DL_MainThreadHook * ChucK::getMainThreadHook() { return m_hook; }