/* Copyright (c) 2004-2005, Ethan Funk. All rights reserved. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "arplayer.h" #include "OutputDistrib.h" #include "InputPlayer.h" #include "FilePlayer.h" #include "Recorder.h" #include "Manager.h" #include "aeTask.h" #include "AudioRingBuffer3.h" #define maxthread 20 #define listen_port 9550 // Global Variables string versionStr="1.4b2"; unsigned char Debug = false; unsigned char msg = false; int ctrl_c_count; int s; // socket for accepting connections pthread_t listen_thread; pthread_t sThread[maxthread]; pthread_t manager_thread; struct threadPass tp[maxthread]; AudioUnit mixer; meta_list metaList; // list of all items metaData by UID number ARPlayer *pList[inBusNum]; sourceRec sourceList[inBusNum]; vector PlayList; vector TaskList; vector RecList; unsigned long plRev = 0; // incremented each time the play list changes // simple mutex locks and semaphores pthread_mutex_t iMutex = PTHREAD_MUTEX_INITIALIZER; // for the inputDef list pthread_mutex_t oMutex = PTHREAD_MUTEX_INITIALIZER; // for the outputDistribution list pthread_mutex_t sMutex = PTHREAD_MUTEX_INITIALIZER; // session thread mutex pthread_mutex_t mMutex = PTHREAD_MUTEX_INITIALIZER; // mutual exclusion control for the master output rendering pthread_mutex_t mgrMutex = PTHREAD_MUTEX_INITIALIZER; // mutual exclusion control for the list manager semiphore pthread_cond_t mgrSemaphore; pthread_mutex_t pushMutex; // mutual exclusion control for the push feed outputs semiphore pthread_cond_t pushSemaphore; // advanced read/write differentiated locks Lock *lMutex; // for the play-list list Lock *pMutex; // for the player list Lock *dMutex; // for the metaData list Lock *tMutex; // for the task list Lock *rMutex; // for the recorder list float avrMeterPost[inChNum*inBusNum]; float pkMeterPost[inChNum*inBusNum]; float avrMeterPre[inChNum*inBusNum]; float pkMeterPre[inChNum*inBusNum]; float avrOutputMeter[outChNum*outBusNum]; float pkOutputMeter[outChNum*outBusNum]; unsigned long tcpPort; float latency_factor; string startup_path; string dbi_path; output_map outputMap; // stores the output bus distribution settings input_map inputMap; // stores the line input definitions Distributor *Master = NULL; AudioRingBuffer *BusBuffer; double MasterOutputTime = 0; unsigned char plRunning = false; float coreRate; ServerLog *ServerLoger = NULL; ProgramLog *ProgramLoger = NULL; NotifyQue *Notifier = NULL; MidiPort *MidiObj = NULL; unsigned char run; // functions int main(int argc, const char *argv[]) { struct sockaddr_in server; /* server address information */ pthread_mutexattr_t attr; int namelen; /* length of client name */ int i; char command[1024]; unsigned long lastUID = 0; sigblock(sigmask(SIGPIPE)); signal(SIGINT, INThandler); signal(SIGTERM, TERMhandler); signal(SIGHUP, HUPhandler); // init advanced locks lMutex = new Lock; pMutex = new Lock; dMutex = new Lock; tMutex = new Lock; rMutex = new Lock; for(i=0; i< maxthread; i++){ sThread[i] = 0; } pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); // clear player list for(i=0;iwriteLock(true); metaList[0] = new metaDataRec; metaList[0]->users = 0; metaList[0]->rev = 0; // new record, revision zero metaList[0]->metaData["Version"] = versionStr; dMutex->writeUnlock(); if (argc ==2) { // command line options list fprintf(stdout,"Usage-- command line options:\n\t-c [startup configuration file path]\n\t-r [core mixer sample rate]\n\t-p [client connection tcp port number]\n\t uses default control tcp port (9550), 96000 sps core sample rate and no configuartion.\n"); exit(0); } i = 1; startup_path = "startup.conf"; dbi_path = ""; // yeild NULL c string so libdbi will use default driver location tcpPort = listen_port; coreRate = 96000.0; latency_factor = 1.0; // use default devive buffer sizes if(startup_path.length() > 0) // load configuration file loadPreConfig(startup_path); while((argc - i) > 1){ // there is a user specified parameter if (strcmp(argv[i], "-p") == 0) { // tcp listening port being specified i = i + 1; tcpPort = atoi(argv[i]); i = i + 1; }else if (strcmp(argv[i], "-r") == 0) { // core sample rate being specified i = i + 1; coreRate = atof(argv[i]); i = i + 1; }else if (strcmp(argv[i], "-c") == 0) { // config file path being specified i = i + 1; startup_path = argv[i]; i = i + 1; }else if (strcmp(argv[i], "-d") == 0) { // dbi driver directory path being specified i = i + 1; dbi_path = argv[i]; i = i + 1; }else if (strcmp(argv[i], "-l") == 0) { // set latency factor (multiplies the default buffer sizes) i = i + 1; latency_factor = atof(argv[i]); // force latency_factpr to be a power of two... ie. 0.25, 0.5, 1, 2, 4, .. int exp; frexp(latency_factor, &exp); latency_factor = ldexp(1.0, exp-1); i = i + 1; } } // display version fprintf(stdout,"Audioengine, version %s\n", versionStr.c_str()); fprintf(stdout,"Copyright (C) 2004-2005 Ethan Funk\n\n"); fprintf(stdout,"Audioengine comes with ABSOLUTELY NO WARRANTY; for details\n"); fprintf(stdout,"type `info'. This is free software, and you are welcome\n"); fprintf(stdout,"to redistribute it under certain conditions; See the\n"); fprintf(stdout,"GNU General Public License included with this program for details.\n\n"); fprintf(stdout,"==================================================================\n"); if(dbi_path.length() == 0) i = dbi_initialize(NULL); else i = dbi_initialize(dbi_path.c_str()); if (i < 0) { fprintf(stderr, "Failed to load database drivers.\n", argv[0]); exit(1); }else{ fprintf(stdout, "%d database drivers loaded.\n", i); } Notifier = new NotifyQue; MidiObj = new MidiPort; //create audio mixer if (InitAudioSystem()) { fprintf(stderr, "%s: Basic audio setup failed\n", argv[0]); exit(1); }else{ fprintf(stdout, "Audio system initialized: core sample rate = %f\n", coreRate); } setMixerMasterVolume(0.707); setMixerOutputVolume(0, 1.0); if(startup_path.length() > 0) // load configuration file loadConfiguration(0, startup_path, &lastUID); // start TCP listening for control sessions s = socket(AF_INET, SOCK_STREAM, 0); /* create stream socket using TCP */ if( s == -1 ) { fprintf(stderr, "socket() Socket was not created.\n"); exit(2); } server.sin_family = AF_INET; /* set up the server name */ server.sin_port = tcpPort; server.sin_addr.s_addr = INADDR_ANY; if(bind(s, (struct sockaddr *) &server, sizeof( server )) < 0 ) { /* bind server address to socket */ fprintf(stderr,"bind() Error binding server to port %d.\n", tcpPort); exit(3); } /* find out what port was assigned */ namelen = sizeof(server); if(getsockname(s, (struct sockaddr *) &server, &namelen) < 0 ) { fprintf(stderr, "getsockname() failed to get port number\n"); exit(4); } fprintf(stdout,"listening on port %d, max connections = %d\n", ntohs(server.sin_port), maxthread); if(listen(s, 1) != 0 ) { /* listen for a connection */ fprintf(stderr, "listen() failed\n"); exit(5); } run = true; // create a listener thread to listen for TCP connection requests and spaun off connection sessions pthread_create(&listen_thread, NULL, &TCPListener, NULL); pthread_detach(listen_thread); // create a Play List Manager task thread pthread_create(&manager_thread, NULL, &ListManagerTask, NULL); pthread_detach(manager_thread); pthread_mutex_init(&mgrMutex, NULL); pthread_cond_init(&mgrSemaphore, NULL); // create a semaphore to waking push output threads (recorder/encoder) when there is new output data available. pthread_mutex_init(&pushMutex, NULL); pthread_cond_init(&pushSemaphore, NULL); ProgramLoger = new ProgramLog; ServerLoger = new ServerLog; ServerLoger->MakeEntry("Server has started"); while(run){ // main control loop fprintf(stdout, constPrompt); fflush(stdout); fgets(command, sizeof(command), stdin); //remove LF and CR at end of command strtok(command, "\r"); strtok(command, "\n"); if(processCommand(0, command, &lastUID, false)) break; } // cancel all running TCP realted threads for(i=0; i< maxthread; i++){ if(sThread[i]){ pthread_cancel(sThread[i]); } } // cancel the TCP connection listening thread pthread_cancel(listen_thread); // cancel the Play List Manager thread pthread_cancel(manager_thread); // close all recorders Recorder *rec; while(RecList.size()){ rec = RecList.at(0); RecList.erase(RecList.begin()); delete rec; } ShutDown(); dbi_shutdown(); close(s); /* close server socket connection */ // unlink(s); deleteMetaRecord(0); delete pMutex; delete lMutex; delete dMutex; delete tMutex; delete rMutex; pthread_mutex_destroy(&sMutex); pthread_mutex_destroy(&iMutex); pthread_mutex_destroy(&oMutex); pthread_mutex_destroy(&mMutex); pthread_mutex_destroy(&mgrMutex); pthread_cond_destroy(&mgrSemaphore); pthread_mutex_destroy(&pushMutex); pthread_cond_destroy(&pushSemaphore); ServerLoger->MakeEntry("Server has shutdown"); delete MidiObj; delete Notifier; delete ProgramLoger; delete ServerLoger; fprintf(stdout, "Good Bye!\n"); exit(0); } void INThandler(int sig) { // called when the controling console gets a control-c keyboard signal char c; signal(sig, SIG_IGN); printf("Force quit? [y/n] "); c = getchar(); if (c == 'y' || c == 'Y'){ kill(getpid(), SIGQUIT); }else{ signal(sig, INThandler); } } void TERMhandler(int sig) { signal(sig, SIG_IGN); run = false; } void HUPhandler(int sig) { // called to force log file to close and re-load prefs unsigned long lastUID = 0; signal(sig, SIG_IGN); if(startup_path.length() > 0){ // load configuration file loadConfiguration(0, startup_path, &lastUID); } if(ServerLoger){ // close log file ServerLoger->CloseLogFile(); } signal(sig, HUPhandler); } void* TCPListener(void *refCon) { int namelen; /* length of client name */ int ns; /* client socket */ int i; struct sockaddr_in client; /* client address information */ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); while(1){ namelen = sizeof(client); ns = accept(s, (struct sockaddr *) &client, &namelen); /* wait for connection request */ if(ns > 0) { pthread_mutex_lock( &sMutex ); for(i=0;i< maxthread;i++){ if(!sThread[i]){ if(Debug){ fprintf(stdout, " MESSAGE: connection # %d from %s\n", i+1, inet_ntoa(client.sin_addr)); fprintf(stdout, constPrompt); fflush(stdout); } tp[i].client_socket = ns; tp[i].client = client; tp[i].notify_socket = 0; tp[i].notify_port = 0; tp[i].notify_seq = 0; tp[i].list_pos = i; pthread_create(&sThread[i], NULL, (void*(*)(void*))&sessionThread, &tp[i]); pthread_detach(sThread[i]); break; } } pthread_mutex_unlock( &sMutex ); if(i == maxthread){ ServerLoger->MakeEntry("Connection Listener: requests exceed max number of allowed connections"); send( ns, "maximum number of connection exceeded. Try again later.\n", 57, 0); close(ns); } } } return NULL; } void* sessionThread(struct threadPass *pass) { char command[4096]; /* receive data buffer */ char block[1024]; /* receive data buffer */ char *fragment, *save_pointer; int rx_length, tx_length; int cs; /* client socket */ int n; /* thread position in thread list */ unsigned long lastUID = 0; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); // get local copies our position in the session thread list and the client TCP socket n = pass->list_pos; cs = pass->client_socket; // display version tx_length = snprintf(command, sizeof command, "Audioengine, version %s\n", versionStr.c_str()); if( send( cs, command, tx_length, 0) < 0) goto finish; tx_length = snprintf(command, sizeof command, "Copyright (C) 2004-2005 Ethan Funk\n\n"); if( send( cs, command, tx_length, 0) < 0) goto finish; tx_length = snprintf(command, sizeof command, "Audioengine comes with ABSOLUTELY NO WARRANTY; for details\n"); if( send( cs, command, tx_length, 0) < 0) goto finish; tx_length = snprintf(command, sizeof command, "type `info'. This is free software, and you are welcome\n"); if( send( cs, command, tx_length, 0) < 0) goto finish; tx_length = snprintf(command, sizeof command, "to redistribute it under certain conditions; See the\n"); if( send( cs, command, tx_length, 0) < 0) goto finish; tx_length = snprintf(command, sizeof command, "GNU General Public License included with this program for details.\n\n"); if( send( cs, command, tx_length, 0) < 0) goto finish; tx_length = snprintf(command, sizeof command, "==================================================================\n"); if( send( cs, command, tx_length, 0) < 0) goto finish; // send prompt tx_length = snprintf(command, sizeof command, constPrompt); if( send( cs, command, tx_length, 0) < 0) goto finish; command[0] = 0; // wait for a client command to arrive rx_length = recv(cs, block, sizeof block, 0); block[rx_length] = 0; // null at end of segment to make it a c-string while(rx_length > 0){ // "\n" is our command delimitor fragment = strtok_r(block, "\n", &save_pointer); while(fragment){ // delimitor found in the string strncat(command, fragment, (sizeof command) - (strlen(command) + 1)); if(processCommand(cs, command, &lastUID, false)) goto finish; // send prompt tx_length = snprintf(command, sizeof command, constPrompt); if( send( cs, command, tx_length, 0) < 0) goto finish; command[0] = 0; // check for another one fragment = strtok_r(NULL, "\n", &save_pointer); } // no delimitor left in the string... save whats left, the delimitor my show up in the next round if(save_pointer) strncat(command, save_pointer, (sizeof command) - (strlen(command) + 1)); // wait for a client command to arrive rx_length = recv(cs, block, sizeof block, 0); block[rx_length] = 0; // null at end of segment to make it a c-string } finish: if(Debug){ fprintf(stdout, " MESSAGE: connection #%d has closed\n", n+1); fprintf(stdout, constPrompt); fflush(stdout); } if(pass->notify_socket) close(pass->notify_socket); close(cs); /* close client socket connection */ pthread_mutex_lock( &sMutex ); sThread[n] = 0; pthread_mutex_unlock( &sMutex ); pthread_exit(0); } void loadPreConfig(string file_path) { FILE *fp; char *arg, *param, *result, line[4096]; if((fp = fopen(file_path.c_str(), "r")) == NULL){ fprintf(stderr, "Could not open configuartion file for reading: %s\n", file_path.c_str()); return; } result = fgets(line, sizeof line, fp); while(result != NULL){ if(line[0] == '-'){ // a preload configuartion switch ('-' first char in line) arg = strtok_r(line, " ", ¶m); if (strcmp(arg, "-p") == 0) { // tcp listening port being specified tcpPort = atoi(param); }else if (strcmp(arg, "-d") == 0) { // dbi driver directory path dbi_path = param; }else if (strcmp(arg, "-r") == 0) { // core sample rate being specified coreRate = atof(param); }else if (strcmp(arg, "-l") == 0) { // set latency factor (multiplies the default buffer sizes) latency_factor = atof(param); // force latency_factpr to be a power of two... ie. 0.25, 0.5, 1, 2, 4, .. int exp; frexp(latency_factor, &exp); latency_factor = ldexp(1.0, exp-1); } } result = fgets(line, sizeof line, fp); } fclose(fp); } unsigned char loadConfiguration(int cs, string file_path, unsigned long *lastUID) { FILE *fp; char *result, line[4096]; if((fp = fopen(file_path.c_str(), "r")) == NULL) return false; result = fgets(line, sizeof line, fp); while(result != NULL){ if((line[0] != ';') && (line[0] != '-')) // not a commented or pre-config line (';' or '-' as first char in line) processCommand(cs, line, lastUID, true); result = fgets(line, sizeof line, fp); } fclose(fp); return true; } void my_send(int cs, const char *buf, int tx_length, unsigned char silent) { if(silent) return; if(cs == 0){ fprintf(stdout, "%s", buf); fflush(stdout); } else send(cs, buf, tx_length, 0); } unsigned char processCommand(int cs, char *command, unsigned long *lastUID, unsigned char silent) { ARPlayer *player; char buf[4096]; /* send data buffer */ char *arg; char *param; float aFloat; unsigned long aLong; long chArray[inChNum]; char *save_pointer; int i = 0; int n = maxthread; unsigned int aInt; int bInt; int tx_length; //remove LF and CR at end of command strtok_r(command, "\r", &save_pointer); strtok_r(command, "\n", &save_pointer); if(Debug){ for(n=0;n< maxthread;n++){ if(cs == tp[n].client_socket) break; } if((n == maxthread) || (cs == 0)) fprintf(stdout, "command '%s' received from console\n", command); else fprintf(stdout, "command '%s' received from connection #%d\n", command, n+1); } arg = strtok_r(command, " ", &save_pointer); if(arg == NULL){ tx_length = snprintf(buf, sizeof buf, "huh?\n"); my_send( cs, buf, tx_length, silent); return false; } // Check the arguments if (cs == 0){ // local commands only for security reasons if (strcmp(arg, "close") == 0){ // local command only for security reasons // first parameter, connection number is in save_pointer i = atoi(save_pointer) - 1; // cancel the specified TCP thread if((i >= 0) && (i < maxthread)){ pthread_mutex_lock( &sMutex ); if(sThread[i]){ shutdown(tp[i].client_socket, SHUT_RDWR); } pthread_mutex_unlock( &sMutex ); goto OK; }else{ tx_length = snprintf(buf, sizeof buf, "bad client number.\n"); my_send( cs, buf, tx_length, silent); } return false; } if (strcmp(arg, "msg-on") == 0){ // local command only for security reasons msg = true; goto OK; } if (strcmp(arg, "msg-off") == 0){ // local command only for security reasons msg = false; goto OK; } if (strcmp(arg, "debug-on") == 0){ // local command only for security reasons Debug = true; goto OK; } if (strcmp(arg, "debug-off") == 0){ // local command only for security reasons Debug = false; goto OK; } } if (strcmp(arg, "clients") == 0){ // list the current connected clients fprintf(stdout, "Connected clients\n"); pthread_mutex_lock( &sMutex ); for(int cn=0; cngetState()); fprintf(stdout, "lMutex - play list: %c\n", lMutex->getState()); fprintf(stdout, "dMutex - metaData list: %c\n", dMutex->getState()); fprintf(stdout, "tMutex - task list: %c\n", tMutex->getState()); fprintf(stdout, "rMutex - recorder list: %c\n", rMutex->getState()); if(pthread_mutex_trylock(&sMutex) == 0){ pthread_mutex_unlock(&sMutex); fprintf(stdout, "sMutex - session: -\n"); }else{ fprintf(stdout, "sMutex - session: x\n"); } if(pthread_mutex_trylock(&iMutex) == 0){ pthread_mutex_unlock(&iMutex); fprintf(stdout, "iMutex - input device: -\n"); }else{ fprintf(stdout, "iMutex - input device: x\n"); } if(pthread_mutex_trylock(&oMutex) == 0){ pthread_mutex_unlock(&oMutex); fprintf(stdout, "oMutex - output device: -\n"); }else{ fprintf(stdout, "oMutex - output device: x\n"); } if(pthread_mutex_trylock(&mMutex) == 0){ pthread_mutex_unlock(&mMutex); fprintf(stdout, "mMutex - master output: -\n"); }else{ fprintf(stdout, "mMutex - master output: x\n"); } if(pthread_mutex_trylock(&mgrMutex) == 0){ pthread_mutex_unlock(&mgrMutex); fprintf(stdout, "mgrMutex - list manager: -\n"); }else{ fprintf(stdout, "mgrMutex - list manager: x\n"); } return false; } if (strcmp(arg, "echo") == 0){ // first parameter, message to echo is in save_pointer tx_length = snprintf(buf, sizeof buf, "%s\n", save_pointer); my_send(cs, buf, tx_length, silent); return false; } if (strcmp(arg, "config") == 0){ // first parameter, file path is in save_pointer if(save_pointer != NULL){ if(!loadConfiguration(cs, save_pointer, lastUID)){ tx_length = snprintf(buf, sizeof buf, "Could not open configuartion file for reading: %s\n", save_pointer); my_send(cs, buf, tx_length, silent); }else{ goto OK; } } } if (strcmp(arg, "devlist") == 0) { if(!silent){ tx_length = snprintf(buf, sizeof buf, "Dev#\tName\tUID\tOutputs\tInputs\n"); my_send( cs, buf, tx_length, silent); ReturnDeviceList(cs, buf, sizeof buf); } return false; } if (strcmp(arg, "dblist") == 0) { if(!silent){ tx_length = snprintf(buf, sizeof buf, "Name\tRev\n"); my_send( cs, buf, tx_length, silent); DumpDBDriverList(cs, buf, sizeof buf); } return false; } if (strcmp(arg, "dbinit") == 0) { if(db_initialize()) goto OK; else return false; } if (strcmp(arg, "innames") == 0) { // first param is device UID if(save_pointer != NULL){ tx_length = snprintf(buf, sizeof buf, "Chan#\tName\n"); my_send(cs, buf, tx_length, silent); if(!silent) ReturnDevChNames(cs, buf, sizeof buf, save_pointer, true); return false; } } if (strcmp(arg, "outnames") == 0) { // first param is device UID if(save_pointer != NULL){ tx_length = snprintf(buf, sizeof buf, "Chan#\tName\n"); my_send( cs, buf, tx_length, silent); if(!silent) ReturnDevChNames(cs, buf, sizeof buf, save_pointer, false); return false; } } if (strcmp(arg, "getdm") == 0) { // first parameter, device input channel number param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aInt = atoi(param); // second param is device UID if(save_pointer != NULL){ if(!silent) ReturnDevDMDest(cs, buf, sizeof buf, save_pointer, aInt); return false; } } } if (strcmp(arg, "setdm") == 0) { // first parameter, device input channel number /* param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aInt = atoi(param); // second param is destination ID to route this channel to param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aLong = atoi(param); // third param is device UID if(save_pointer != NULL){ if(SetDevDMDest(save_pointer, aInt, aLong)) return false; } } } */ } if (strcmp(arg, "listdm") == 0) { // first parameter, device input channel number param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aInt = atoi(param); // second param is device UID if(save_pointer != NULL){ tx_length = snprintf(buf, sizeof buf, "Dest#\tDest Name\n"); my_send( cs, buf, tx_length, silent); if(!silent) ReturnDevDMDestList(cs, buf, sizeof buf, save_pointer, aInt); return false; } } } if ((strcmp(arg, "exit") == 0) || (strcmp(arg, "q") == 0)) return true; if (!strcmp(arg, "info")){ tx_length = snprintf(buf, sizeof buf, "Audioengine, version %s\n", versionStr.c_str()); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "Copyright (C) 2004-2005 Ethan Funk\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "============================================================================\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "This program is free software; you can redistribute it and/or\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "modify it under the terms of the GNU General Public License\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "as published by the Free Software Foundation; either version 2\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "of the License, or (at your option) any later version.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "This program is distributed in the hope that it will be useful,\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "GNU General Public License for more details.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "You should have received a copy of the GNU General Public License\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "along with this program; if not, write to the Free Software\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "============================================================================\n"); my_send( cs, buf, tx_length, silent); } if (!strcmp(arg, "notify") && (cs != 0)) { struct notifyData data; // first parameter, UDP port to send notifications on param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aInt = atoi(param); //Find our session in the list for(i=0; i< maxthread; i++){ if(tp[i].client_socket == cs){ // close existing socket, if any if(tp[i].notify_socket) close(tp[i].notify_socket); tp[i].notify_port = 0; if(aInt != 0){ // new UDP socket if((tp[i].notify_socket = socket(AF_INET, SOCK_DGRAM, 0)) >= 0){ tp[i].notify_port = aInt; // set socket send timeout to 1 seconds struct timeval timeout; timeout.tv_sec = 1; /* seconds */ timeout.tv_usec = 0; /* and microseconds */ setsockopt(tp[i].notify_socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); // send a notification to the new client for current status of all players pMutex->readLock(true); for(i=0;iMakeEntry(data); data.type = nType_bal; data.iVal = 0; data.fVal = player->balance; Notifier->MakeEntry(data); data.type = nType_bus; data.iVal = player->bus_assignment; data.fVal = 0.0; Notifier->MakeEntry(data); data.type = nType_stat; data.iVal = player->status; data.fVal = 0.0; Notifier->MakeEntry(data); }else{ // empty data.type = nType_vol; data.iVal = 0; data.fVal = 0.0; Notifier->MakeEntry(data); data.type = nType_bal; Notifier->MakeEntry(data); data.type = nType_bus; Notifier->MakeEntry(data); data.type = nType_stat; Notifier->MakeEntry(data); } } pMutex->readUnlock(); } } break; } } if(i < maxthread) goto OK; } } if (strcmp(arg, "urlmeta") == 0){ // first parameter, url is in save_pointer if(!silent && save_pointer){ unsigned long localUID; // create a meta data record to hold results localUID = createMetaRecord(save_pointer); registerMetaUser(localUID); // fill the metadata record GetURLMetaData(localUID, save_pointer); // dump the results dMutex->readLock(true); if (metaList.find(localUID) != metaList.end()){ meta_map_ptr dp; string dataStr; metaDataPtr Data; Data = metaList[localUID]; // dump the metaData from pairs map for (dp = Data->metaData.begin(); dp!=Data->metaData.end(); dp++){ dataStr = (*dp).first + "=" + (*dp).second; tx_length = snprintf(buf, sizeof buf, "%s\n", dataStr.c_str()); my_send( cs, buf, tx_length, silent); } dMutex->readUnlock(); *lastUID = aLong; return false; } dMutex->readUnlock(); // done with the metadata record... delete deleteMetaRecord(localUID); } return false; } if (strcmp(arg, "settings") == 0) { dMutex->readLock(true); if (metaList.find(0) != metaList.end()){ meta_map_ptr dp; string dataStr; metaDataPtr Data; Data = metaList[0]; // dump the metaData from pairs map for (dp = Data->metaData.begin(); dp!=Data->metaData.end(); dp++){ dataStr = (*dp).first + "=" + (*dp).second; tx_length = snprintf(buf, sizeof buf, "%s\n", dataStr.c_str()); my_send( cs, buf, tx_length, silent); } dMutex->readUnlock(); return false; } dMutex->readUnlock(); } if (strcmp(arg, "get") == 0) { // first parameter, property key param = strtok_r(nil, " ", &save_pointer); if(param != NULL){ string key(param); dMutex->readLock(true); if (metaList.find(0) != metaList.end()){ meta_map_ptr dp; string value; metaDataPtr Data; Data = metaList[0]; dp = Data->metaData.find(key); if (dp != Data->metaData.end()){ value = (*dp).second; tx_length = snprintf(buf, sizeof buf, "%s\n", value.c_str()); my_send( cs, buf, tx_length, silent); } dMutex->readUnlock(); return false; } dMutex->readUnlock(); } } if (strcmp(arg, "set") == 0) { // first parameter, property key param = strtok_r(nil, " ", &save_pointer); if(param != NULL){ // second parameter, value to set (in save_pointer) if(save_pointer != NULL){ // value specified... set key to new value string key(param); if(key != "Version"){ string value(save_pointer); dMutex->readLock(true); if (metaList.find(0) != metaList.end()){ metaDataPtr Data; Data = metaList[0]; // check if the key already exists if (Data->metaData.find(key) != Data->metaData.end()){ // key already in map... delete it dMutex->writeLock(true); Data->metaData.erase(key); dMutex->writeUnlock(); } Data->metaData[key] = value; // change rev so clients know a changed has been made Data->rev = Data->rev + 1; dMutex->readUnlock(); goto OK; } dMutex->readUnlock(); } } } } if (strcmp(arg, "saveset") == 0) { string dataStr; // save all current settings from pairs map if(GetMetaData(0, "file_prefs").length() > 0){ FILE *fp; if((fp=fopen(GetMetaData(0, "file_prefs").c_str(), "w")) != NULL){ fprintf(fp, "; settings definitions\necho setting up last saved settings\n"); dMutex->readLock(true); if (metaList.find(0) != metaList.end()){ meta_map_ptr dp; string dataStr; metaDataPtr Data; Data = metaList[0]; // dump the metaData from pairs map for (dp = Data->metaData.begin(); dp!=Data->metaData.end(); dp++){ if((*dp).first != "Version"){ dataStr = (*dp).first + " " + (*dp).second; fprintf(fp, "set %s\n", dataStr.c_str()); } } } dMutex->readUnlock(); fclose(fp); goto OK; } return false; } } if (strcmp(arg, "dumpmeta") == 0) { // first parameter, meta data item UID in hex format param = strtok_r(nil, " ", &save_pointer); if(param != nil){ if(strlen(param) > 2){ // hex number (UID) char *end; aLong = strtoul(param, &end, 16); }else{ // decimal number (pNum) bInt = atoi(param); if(bInt < 0){ aLong = *lastUID; }else{ aInt = (unsigned long)bInt; if(!getPlayerUID(&aInt)){ tx_length = snprintf(buf, sizeof buf, "bad player number.\n"); my_send( cs, buf, tx_length, silent); *lastUID = 0; return false; } aLong = aInt; } } if(aLong != 0){ dMutex->readLock(true); if (metaList.find(aLong) != metaList.end()){ meta_map_ptr dp; string dataStr; metaDataPtr Data; Data = metaList[aLong]; // dump the metaData from pairs map for (dp = Data->metaData.begin(); dp!=Data->metaData.end(); dp++){ dataStr = (*dp).first + "=" + (*dp).second; tx_length = snprintf(buf, sizeof buf, "%s\n", dataStr.c_str()); my_send( cs, buf, tx_length, silent); } dMutex->readUnlock(); *lastUID = aLong; return false; } dMutex->readUnlock(); } } *lastUID = 0; } if (strcmp(arg, "getmeta") == 0) { // first parameter, meta data item UID in hex format param = strtok_r(nil, " ", &save_pointer); if(param != nil){ if(strlen(param) > 2){ // hex number (UID) char *end; aLong = strtoul(param, &end, 16); }else{ // decimal number (pNum) bInt = atoi(param); if(bInt < 0){ aLong = *lastUID; }else{ aInt = (unsigned long)bInt; if(!getPlayerUID(&aInt)){ tx_length = snprintf(buf, sizeof buf, "bad player number.\n"); my_send( cs, buf, tx_length, silent); *lastUID = 0; return false; } aLong = aInt; } } if(aLong != 0){ // second parameter, key to set value for (in save_pointer) if(save_pointer != NULL){ string key(save_pointer); tx_length = snprintf(buf, sizeof buf, "%s\n", GetMetaData(aLong, key).c_str()); my_send( cs, buf, tx_length, silent); *lastUID = aLong; return false; } } } *lastUID = 0; } if (strcmp(arg, "setmeta") == 0) { // first parameter, meta data item UID in hex format or player number in decimal format param = strtok_r(nil, " ", &save_pointer); if(param != nil){ if(strlen(param) > 2){ // hex number (UID) char *end; aLong = strtoul(param, &end, 16); }else{ // decimal number (pNum) bInt = atoi(param); if(bInt < 0){ aLong = *lastUID; }else{ aInt = (unsigned long)bInt; if(!getPlayerUID(&aInt)){ tx_length = snprintf(buf, sizeof buf, "bad player number.\n"); my_send( cs, buf, tx_length, silent); *lastUID = 0; return false; } aLong = aInt; } } if(aLong != 0){ // second parameter, key to set value for param = strtok_r(nil, " ", &save_pointer); if(param != NULL){ string key(param); if(save_pointer != NULL){ string value(save_pointer); // third parameter, value to set (in save_pointer) SetMetaData(aLong, key, value); *lastUID = aLong; goto OK; } } } } *lastUID = 0; } if (strcmp(arg, "delmeta") == 0) { // first parameter, meta data item UID in hex format param = strtok_r(nil, " ", &save_pointer); if(param != nil){ if(strlen(param) > 2){ // hex number (UID) char *end; aLong = strtoul(param, &end, 16); }else{ // decimal number (pNum) bInt = atoi(param); if(bInt < 0){ aLong = *lastUID; }else{ aInt = (unsigned long)bInt; if(!getPlayerUID(&aInt)){ tx_length = snprintf(buf, sizeof buf, "bad player number.\n"); my_send( cs, buf, tx_length, silent); *lastUID = 0; goto OK; } aLong = aInt; } } if(aLong != 0){ // second parameter, key to set value for (in save_pointer) if(save_pointer != NULL){ dMutex->readLock(true); if (metaList.find(aLong) != metaList.end()){ string key(save_pointer); // delete the pair given the key metaDataPtr Data; Data = metaList[aLong]; dMutex->writeLock(true); Data->metaData.erase(key); dMutex->writeUnlock(); // change rev so clients know a changed has been made Data->rev = Data->rev + 1; dMutex->readUnlock(); *lastUID = 0; goto OK; } dMutex->readUnlock(); } } } *lastUID = 0; } if (strcmp(arg, "savein") == 0) { input_map_ptr dp; string dataStr; // save all line-input definitions from pairs map if(GetMetaData(0, "file_inputs").length() > 0){ FILE *fp; if((fp=fopen(GetMetaData(0, "file_inputs").c_str(), "w")) != NULL){ pthread_mutex_lock( &iMutex ); fprintf(fp, "; line-input definitions\necho setting up line input definitions\n"); for (dp=inputMap.begin(); dp!=inputMap.end(); dp++){ dataStr = ""; for(i=0; i 0){ FILE *fp; if((fp=fopen(GetMetaData(0, "file_outputs").c_str(), "w")) != NULL){ pthread_mutex_lock( &oMutex ); fprintf(fp, "; audio output device definitions\necho setting up audio output devices\n"); for (dp=outputMap.begin(); dp!=outputMap.end(); dp++){ // master or slave if (&((*dp).second) == Master) dataStr = "master"; else dataStr = "slave"; // bus count tx_length = snprintf(buf, sizeof buf, " %08lx %d", (*dp).second.muteGain, (*dp).second.channelMapSize); dataStr = dataStr + buf; //bus list for(int idx=0; idx<(*dp).second.channelMapSize; idx++){ tx_length = snprintf(buf, sizeof buf, " %d", (*dp).second.channelMap[idx]); dataStr = dataStr + buf; } fprintf(fp, "setout %s %s %s\n", (*dp).first.c_str(), dataStr.c_str(), (*dp).second.deviceUID.c_str()); } pthread_mutex_unlock( &oMutex ); fclose(fp); goto OK; } return false; } } if (strcmp(arg, "dumpout") == 0) { output_map_ptr dp; string dataStr; // dump all line-input definitions from pairs map tx_length = snprintf(buf, sizeof buf, "Name\tDevice UID\t\tType\tMute\tMix Bus to Output channel mapping\n"); my_send(cs, buf, tx_length, silent); pthread_mutex_lock( &oMutex ); for (dp=outputMap.begin(); dp!=outputMap.end(); dp++){ if (&((*dp).second) == Master) dataStr = "master"; else dataStr = "slave"; tx_length = snprintf(buf, sizeof buf, "\t%08lx", (*dp).second.muteGain); dataStr = dataStr + buf; for(int idx=0; idx<(*dp).second.channelMapSize; idx++){ tx_length = snprintf(buf, sizeof buf, "\t%d", (*dp).second.channelMap[idx]); dataStr = dataStr + buf; } tx_length = snprintf(buf, sizeof buf, "%s\t%s\t%s\n", (*dp).first.c_str(), (*dp).second.deviceUID.c_str(), dataStr.c_str()); my_send( cs, buf, tx_length, silent); } pthread_mutex_unlock( &oMutex ); return false; } if (strcmp(arg, "outvol") == 0) { // first parameter, output device UID param = strtok_r(nil, " ", &save_pointer); if(param != nil){ pthread_mutex_lock( &oMutex ); output_map_ptr dp; string key(param); string dataStr; // find the pair given the key dp = outputMap.find(key); if (dp != outputMap.end()){ if(save_pointer != nil){ aFloat = atof(save_pointer); (*dp).second.SetVolume(aFloat); pthread_mutex_unlock( &oMutex ); goto OK; } } pthread_mutex_unlock( &oMutex ); } } if (strcmp(arg, "setdly") == 0) { // first parameter, output device Name or recorder UID param = strtok_r(nil, " ", &save_pointer); if(param != nil){ // first try to match to an output pthread_mutex_lock( &oMutex ); output_map_ptr dp; string key(param); string dataStr; // find the pair given the key dp = outputMap.find(key); if (dp != outputMap.end()){ if(save_pointer != nil){ aFloat = atof(save_pointer); if((*dp).second.SetDelay(aFloat)){ pthread_mutex_unlock( &oMutex ); goto OK; } } pthread_mutex_unlock( &oMutex ); return false; }else{ pthread_mutex_unlock( &oMutex ); // no match in the outputs, try recorder UIDs if(strlen(param) > 2){ // hex number (UID) char *end; aLong = strtoul(param, &end, 16); }else{ // assume we want the last UID used in this session aLong = *lastUID; } if(aLong != 0){ Recorder *rec; rMutex->readLock(true); for(unsigned int idx=0; idxUID == aLong){ if(save_pointer != nil){ aFloat = atof(save_pointer); if(rec->SetDelay(aFloat)){ rMutex->readUnlock(); *lastUID = aLong; goto OK; } } } } rMutex->readUnlock(); *lastUID = aLong; return false; } } } } if (strcmp(arg, "getdly") == 0) { tx_length = snprintf(buf, sizeof buf, "Dest ID\t\tName\t\tDelay\n"); my_send( cs, buf, tx_length, silent); // output list output_map_ptr dp; pthread_mutex_lock( &oMutex ); for (dp = outputMap.begin(); dp!=outputMap.end(); dp++){ aFloat = (*dp).second.GetDelay(); tx_length = snprintf(buf, sizeof buf, "%s\t%s\t%.1f\n", (*dp).first.c_str(), (*dp).first.c_str(), aFloat); my_send( cs, buf, tx_length, silent); } pthread_mutex_unlock( &oMutex ); // recorder list Recorder *rec; rMutex->readLock(true); for(unsigned int idx=0; idxGetDelay(); tx_length = snprintf(buf, sizeof buf, "%08lx\t%s\t%.1f\n", rec->UID, GetMetaData(rec->UID, string("Name")).c_str(), aFloat); my_send( cs, buf, tx_length, silent); } rMutex->readUnlock(); return false; } if (strcmp(arg, "dump") == 0) { // output list output_map_ptr dp; pthread_mutex_lock( &oMutex ); for (dp = outputMap.begin(); dp!=outputMap.end(); dp++){ (*dp).second.SetDelay(0.0); } pthread_mutex_unlock( &oMutex ); // recorder list Recorder *rec; rMutex->readLock(true); for(unsigned int idx=0; idxSetDelay(0.0); } rMutex->readUnlock(); goto OK; } if (strcmp(arg, "getout") == 0) { // first parameter, output device UID param = strtok_r(nil, " ", &save_pointer); if(param != nil){ pthread_mutex_lock( &oMutex ); output_map_ptr dp; string key(param); string dataStr; // find the pair given the key dp = outputMap.find(key); if (dp != outputMap.end()){ tx_length = snprintf(buf, sizeof buf, "Device UID\t\tType\tMute\tMix Bus to Output channel mapping\n"); my_send( cs, buf, tx_length, silent); if (&((*dp).second) == Master) dataStr = "master"; else dataStr = "slave"; tx_length = snprintf(buf, sizeof buf, "\t%08lx", (*dp).second.muteGain); dataStr = dataStr + buf; for(int idx=0; idx<(*dp).second.channelMapSize; idx++){ tx_length = snprintf(buf, sizeof buf, "\t%d", (*dp).second.channelMap[idx]); dataStr = dataStr + buf; } tx_length = snprintf(buf, sizeof buf, "%s\t%s\n", (*dp).second.deviceUID.c_str(), dataStr.c_str()); my_send( cs, buf, tx_length, silent); pthread_mutex_unlock( &oMutex ); return false; } pthread_mutex_unlock( &oMutex ); } } if (strcmp(arg, "setout") == 0) { // first parameter, name string param = strtok_r(nil, " ", &save_pointer); if(param != NULL){ string key(param); // second parameter, type string: master or slave param = strtok_r(nil, " ", &save_pointer); if(param != NULL){ unsigned char isMaster; if (strcmp(param, "master") == 0) isMaster = true; else isMaster = false; // third parameter, mute gains (hex format) param = strtok_r(nil, " ", &save_pointer); if(param != nil){ char *end; unsigned int mg; mg = strtoul(param, &end, 16); // fourth parameter, channel assignment count param = strtok_r(nil, " ", &save_pointer); if(param != NULL){ int count; count = atoi(param); // fith through nth parameter, channel assignment int outChMap[256]; for(i=0; i < count; i++){ param = strtok_r(nil, " ", &save_pointer); if(param == NULL) break; outChMap[i] = atoi(param); } if(i == count){ // last parameter, output device UID string if(save_pointer != NULL){ string UID(save_pointer); pthread_mutex_lock( &oMutex ); // check if the key already exists output_map_ptr dp; dp = outputMap.find(key); if (dp != outputMap.end()){ // key already in map... if(strcmp((*dp).second.deviceUID.c_str(), UID.c_str()) == 0){ // same dev UID... update other settings if((*dp).second.SetChannelMap(outChMap, i)){ (*dp).second.muteGain = mg; if(isMaster) (*dp).second.SetMaster(true); else (*dp).second.SetMaster(false); pthread_mutex_unlock( &oMutex ); goto OK; }else{ // if there is an error seting up the device, delete the record outputMap.erase(key); tx_length = snprintf(buf, sizeof buf, "could not update the device settings.\n"); my_send( cs, buf, tx_length, silent); pthread_mutex_unlock( &oMutex ); return false; } }else{ // new output device... delete the entire record outputMap.erase(key); } } if(!outputMap[key].Initialize(UID, outChMap, i, isMaster, mg)){ // if there is an error seting up the device, delete the record outputMap.erase(key); tx_length = snprintf(buf, sizeof buf, "could not initialize the device.\n"); my_send( cs, buf, tx_length, silent); pthread_mutex_unlock( &oMutex ); return false; }else{ pthread_mutex_unlock( &oMutex ); goto OK; } } } } } } } } if (strcmp(arg, "delout") == 0) { // first parameter, output name param = strtok_r(nil, " ", &save_pointer); if(param != nil){ pthread_mutex_lock( &oMutex ); string key(param); // delete the pair given the key outputMap.erase(key); pthread_mutex_unlock( &oMutex ); goto OK; } } if (strcmp(arg, "deltask") == 0) { // first parameter, task number param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aInt = atoi(param); tMutex->readLock(true); if(aIntthread); tMutex->readUnlock(); goto OK; }else{ tMutex->readUnlock(); tx_length = snprintf(buf, sizeof buf, "bad task number.\n"); my_send( cs, buf, tx_length, silent); return false; } } } if (strcmp(arg, "tasks") == 0) { // dump the task list tx_length = snprintf(buf, sizeof buf, "Index\tUID\t\tThreadID\tName\n"); my_send(cs, buf, tx_length, silent); tMutex->readLock(true); for(unsigned int x=0; xUID, TaskList.at(x)->thread->sig, TaskList.at(x)->name.c_str()); my_send( cs, buf, tx_length, silent); } tMutex->readUnlock(); return false; } if (strcmp(arg, "load") == 0) { // first parameter, player number param = strtok_r(nil, " ", &save_pointer); if(param != nil){ bInt = atoi(param); // second parameter, url (file, input name, etc) is in save_pointer if(save_pointer){ if(LoadPlayer(&bInt, save_pointer, 0)){ aLong = 0; pMutex->readLock(true); player = pList[bInt]; if(player != nil){ aLong = player->UID; if(aLong){ *lastUID = aLong; tx_length = snprintf(buf, sizeof buf, "UID=%08lx\n", aLong); my_send( cs, buf, tx_length, silent); pMutex->readUnlock(); return false; } } pMutex->readUnlock(); }else{ tx_length = snprintf(buf, sizeof buf, "failed to load player.\n"); my_send( cs, buf, tx_length, silent); return false; } } } } if (!strcmp(arg, "play")) { // first parameter, player number param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aInt = atoi(param); if(!checkPnumber(aInt)){ tx_length = snprintf(buf, sizeof buf, "bad player number.\n"); my_send( cs, buf, tx_length, silent); return false; } pMutex->readLock(true); player = pList[aInt]; if(player != nil){ player->start(); pMutex->readUnlock(); goto OK; } pMutex->readUnlock(); } } if (!strcmp(arg, "stop")) { // first parameter, player number param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aInt = atoi(param); if(!checkPnumber(aInt)){ tx_length = snprintf(buf, sizeof buf, "bad player number.\n"); my_send( cs, buf, tx_length, silent); return false; } pMutex->readLock(true); player = pList[aInt]; if(player != nil){ player->stop(); pMutex->readUnlock(); goto OK; } pMutex->readUnlock(); } } if (!strcmp(arg, "unload")) { // first parameter, player number param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aInt = atoi(param); if(!checkPnumber(aInt)){ tx_length = snprintf(buf, sizeof buf, "bad player number.\n"); my_send( cs, buf, tx_length, silent); return false; } pMutex->readLock(true); player = pList[aInt]; if(player != NULL){ delete player; pMutex->readUnlock(); goto OK; } pMutex->readUnlock(); } } if (!strcmp(arg, "status")) { // player status tx_length = snprintf(buf, sizeof buf, "pNum\tstatus\tmeta-UID\tRev\ttype\tvol\tbal\tbus\tpos\tdur\tbuff\tnext\tseg\tfade\n"); my_send( cs, buf, tx_length, silent); pMutex->readLock(true); for(i=0;ireadLock(true); tx_length = snprintf(buf, sizeof buf, "%u\t%ld\t%08lx\t%ld\t%s\t%.4f\t%.2f\t%06lx\t%.1f\t%s\t%.1f\t%d\t%.1f\t%.1f\n", i, player->status, player->UID, metaList[player->UID]->rev, player->type, getMixerInputVolume(i), player->balance, player->bus_assignment, player->position, GetMetaData(player->UID, "Duration").c_str(), player->bufftime, player->next, player->segout, player->fade); my_send( cs, buf, tx_length, silent); dMutex->readUnlock(); }else{ // empty tx_length = snprintf(buf, sizeof buf, "%u\t%d\n", i, 0); my_send( cs, buf, tx_length, silent); } } pMutex->readUnlock(); // recorders status tx_length = snprintf(buf, sizeof buf, "\nencoder\t\tname\t\ttype\tstatus\t\tsource\tbus\ttime\tlimit\tdelay\n"); my_send( cs, buf, tx_length, silent); Recorder *rec; rMutex->readLock(true); for(unsigned int idx=0; idxconverter == NULL){ statStr = "uninitialized"; }else if(rec->runThread){ if(rec->errCode == 6){ tx_length = snprintf(errStr, sizeof errStr, "reconnecting"); statStr = errStr; }else if(rec->errCode == 7){ statStr = "starting"; }else{ statStr = "running"; } }else{ if(rec->errCode){ if(rec->errCode == 1){ statStr = "time-out"; }else if(rec->errCode == 2){ statStr = "connect failed"; }else if(rec->errCode == 3){ statStr = "can't keep up"; }else if(rec->errCode == 4){ statStr = "write error"; }else if(rec->errCode == 5){ statStr = "compressor error"; }else{ tx_length = snprintf(errStr, sizeof errStr, "fault:%d", rec->errCode); statStr = errStr; } }else{ statStr = "stopped"; } } tx_length = snprintf(buf, sizeof buf, "%08lx\t%s\t%s\t%s\t%s\t%d\t%.1f\t%.1f\n", rec->UID, GetMetaData(rec->UID, string("Name")).c_str(), GetMetaData(rec->UID, string("Type")).c_str(), statStr, rec->source.c_str(), rec->Bus[0]/2, rec->RecordTime, rec->t_limit); my_send( cs, buf, tx_length, silent); } rMutex->readUnlock(); // play list status if(plRunning) tx_length = snprintf(buf, sizeof buf, "\nListRev=%ld %s\n", plRev, "Running"); else tx_length = snprintf(buf, sizeof buf, "\nListRev=%ld %s\n", plRev, "Stopped"); my_send( cs, buf, tx_length, silent); // settings rev tx_length = snprintf(buf, sizeof buf, "SettingsRev=%ld\n", metaList[0]->rev); my_send( cs, buf, tx_length, silent); // last log entry time tx_length = snprintf(buf, sizeof buf, "LogTime=%ld\n", ProgramLoger->logChangeTime); my_send( cs, buf, tx_length, silent); return false; } if (!strcmp(arg, "meters")) { // update the meter array values getMixerMeters(); tx_length = snprintf(buf, sizeof buf, "chan\tavr\tpeak\n"); my_send( cs, buf, tx_length, silent); for(i=0;i<(outBusNum*outChNum);i++){ tx_length = snprintf(buf, sizeof buf, "%u\t%.1f\t%.1f\n", i, avrOutputMeter[i], pkOutputMeter[i]); my_send( cs, buf, tx_length, silent); } tx_length = snprintf(buf, sizeof buf, "\ninputs (L or R peak)\n"); my_send( cs, buf, tx_length, silent); string result; for(i=0;i<(inBusNum);i++){ float channel, peak; peak = -99.9; for(int j=0;j peak) peak = channel; } tx_length = snprintf(buf, sizeof buf, "%.1f\t", peak); result = result + string(buf); } result = result + "\n"; my_send( cs, result.c_str(), result.size(), silent); return false; } if (!strcmp(arg, "pos")) { // first parameter, input bus number param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aInt = atoi(param); if(!checkPnumber(aInt)){ tx_length = snprintf(buf, sizeof buf, "bad player number.\n"); my_send( cs, buf, tx_length, silent); return false; } // second parameter, time param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aFloat = atof(param); pMutex->readLock(true); player = pList[aInt]; if(player != nil){ player->setPosition(aFloat); pMutex->readUnlock(); goto OK; } pMutex->readUnlock(); } } } if (!strcmp(arg, "next")) { // first parameter, player number to set next player for param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aInt = atoi(param); if(!checkPnumber(aInt)){ tx_length = snprintf(buf, sizeof buf, "bad first player number.\n"); my_send( cs, buf, tx_length, silent); return false; } // second parameter, player number to start at segue time param = strtok_r(nil, " ", &save_pointer); if(param != nil){ bInt = atoi(param); if(!checkPnumber(bInt)){ if(bInt != -1){ tx_length = snprintf(buf, sizeof buf, "bad second player number.\n"); my_send( cs, buf, tx_length, silent); return false; } } // third parameter, segue time param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aFloat = atof(param); pMutex->readLock(true); player = pList[aInt]; if(player != nil){ player->next = bInt; player->segout = aFloat; pMutex->readUnlock(); goto OK; } pMutex->readUnlock(); } } } } if (!strcmp(arg, "fade")) { // first parameter, player number param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aInt = atoi(param); if(!checkPnumber(aInt)){ tx_length = snprintf(buf, sizeof buf, "bad player number.\n"); my_send( cs, buf, tx_length, silent); return false; } // second parameter, time to start fade param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aFloat = atof(param); pMutex->readLock(true); player = pList[aInt]; if(player != nil){ player->fade = aFloat; pMutex->readUnlock(); goto OK; } pMutex->readUnlock(); } } } if (!strcmp(arg, "vol")) { // first parameter, input bus number param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aInt = atoi(param); if(!checkPnumber(aInt)){ tx_length = snprintf(buf, sizeof buf, "bad player number.\n"); my_send( cs, buf, tx_length, silent); return false; } // second parameter, volume param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aFloat = atof(param); setMixerInputVolume(aInt, aFloat); goto OK; } } } if (!strcmp(arg, "setstat")) { // first parameter, player number param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aInt = atoi(param); if(!checkPnumber(aInt)){ tx_length = snprintf(buf, sizeof buf, "bad player number.\n"); my_send( cs, buf, tx_length, silent); return false; } // second parameter, status word param = strtok_r(nil, " ", &save_pointer); if(param != nil){ bInt = atoi(param); pMutex->readLock(true); player = pList[aInt]; if(player != nil){ player->setStatus(bInt); pMutex->readUnlock(); goto OK; } pMutex->readUnlock(); } } } if (!strcmp(arg, "bus")) { // first parameter, input bus (player) number param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aInt = atoi(param); if(!checkPnumber(aInt)) return false; // second parameter, output bus enable word (0 = disable, 1 = enable on bus corrisponding to bit number in word) param = strtok_r(nil, " ", &save_pointer); if(param != nil){ char *end; bInt = strtoul(param, &end, 16); pMutex->readLock(true); player = pList[aInt]; if(player != nil){ player->bus_assignment = bInt; UpdateMatrix(aInt); if(player->status & status_playing){ // it's currently playing if(!(bInt & 2L)){ // not in cue player->status = player->status | status_hasPlayed; } } RefreshMutesGroups(0L); pMutex->readUnlock(); goto OK; } pMutex->readUnlock(); } } } if (!strcmp(arg, "bal")) { // first parameter, input bus number param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aInt = atoi(param); if(!checkPnumber(aInt)) return false; // second parameter, balance (-1 = left only, +1 = right only, 0 = middle) note: constant power panning, +3dB on Left/right only param = strtok_r(nil, " ", &save_pointer); if(param != nil){ aFloat = atof(param); if (aFloat > 1.0) aFloat = 1.0; if (aFloat < -1.0) aFloat = -1.0; pMutex->readLock(true); player = pList[aInt]; if(player != nil){ player->balance = aFloat; UpdateMatrix(aInt); pMutex->readUnlock(); goto OK; } pMutex->readUnlock(); } } } if (!strcmp(arg, "delete")) { // first parameter, position (-1 for end of list) or UID param = strtok_r(nil, " ", &save_pointer); if(param != NULL){ lMutex->readLock(true); if(strlen(param) > 4){ // hex number (UID) char *end; aInt = strtoul(param, &end, 16); if(!getListPos(&aInt)){ lMutex->readUnlock(); tx_length = snprintf(buf, sizeof buf, "UID not in play list.\n"); my_send( cs, buf, tx_length, silent); return false; } }else{ // decimal number (list index) aInt = atoi(param); } DeleteItem(aInt); lMutex->readUnlock(); goto OK; } } if (!strcmp(arg, "move")) { // first parameter, old position param = strtok_r(nil, " ", &save_pointer); if(param != NULL){ lMutex->readLock(true); if(strlen(param) > 4){ // hex number (UID) char *end; aInt = strtoul(param, &end, 16); if(!getListPos(&aInt)){ lMutex->readUnlock(); tx_length = snprintf(buf, sizeof buf, "move from UID not in play list.\n"); my_send( cs, buf, tx_length, silent); return false; } }else{ // decimal number (pNum) aInt = atoi(param); } // second parameter, new position (-1 for end of list) param = strtok_r(nil, " ", &save_pointer); if(param != NULL){ if(strlen(param) > 4){ // hex number (UID) char *end; unsigned int temp; temp = strtoul(param, &end, 16); if(!getListPos(&temp)){ lMutex->readUnlock(); tx_length = snprintf(buf, sizeof buf, "move to UID not in play list.\n"); my_send( cs, buf, tx_length, silent); return false; } bInt = temp; }else{ // decimal number (pNum) bInt = atoi(param); } MoveItem(aInt, bInt); lMutex->readUnlock(); goto OK; } lMutex->readUnlock(); } } if (!strcmp(arg, "add")) { // first parameter, position (-1 for end of list) or uid param = strtok_r(nil, " ", &save_pointer); if(param != NULL){ lMutex->readLock(true); if(strlen(param) > 4){ // hex number (UID) char *end; aInt = strtoul(param, &end, 16); if(!getListPos(&aInt)){ lMutex->readUnlock(); return false; } }else{ // decimal number (position) aInt = atoi(param); } // second parameter, URL or preloaded player number to add if(save_pointer != NULL){ if(isdigit(*save_pointer)){ // second param: player number aFloat = atof(save_pointer); if(!checkPnumber((int)aFloat)){ tx_length = snprintf(buf, sizeof buf, "bad player number.\n"); my_send( cs, buf, tx_length, silent); lMutex->readUnlock(); return false; } AddPlayer(aInt, (int)aFloat); lMutex->readUnlock(); return false; }else{ // Second Param: URL aLong = AddItem(aInt, save_pointer); if(aLong){ *lastUID = aLong; tx_length = snprintf(buf, sizeof buf, "UID=%08lx\n", aLong); my_send( cs, buf, tx_length, silent); lMutex->readUnlock(); return false; } } } lMutex->readUnlock(); } } if (!strcmp(arg, "list")) { // dump the entire play list tx_length = snprintf(buf, sizeof buf, "index\tstatus\tpNum\tmeta-UID\tRev\ttype\tdur\tsegin\tsegout\ttotal\tname\n"); my_send(cs, buf, tx_length, silent); lMutex->readLock(true); ARPlayer *instance; double totalTime = 0.0; for(unsigned int idx=0; idxreadLock(true); Data = metaList[instance->UID]; // check if the Name key already exists string Name; if (Data->metaData.find("Name") != Data->metaData.end()) Name = Data->metaData["Name"]; // check if the Segin key already exists string segInStr; if (Data->metaData.find("SegIn") != Data->metaData.end()) segInStr = Data->metaData["SegIn"]; // check if the Segout key already exists string segOutStr; if (Data->metaData.find("SegOut") != Data->metaData.end()) segOutStr = Data->metaData["SegOut"]; float segOutT; segOutT = atof(segOutStr.c_str()); if(segOutT == 0.0){ segOutStr = Data->metaData["Duration"]; segOutT = atof(segOutStr.c_str()); } if(segOutT > instance->position) segOutT = segOutT - instance->position; if((instance->status & status_playing) != 0){ totalTime = segOutT; }else{ totalTime = totalTime + segOutT; } float segInT; segInT = atof(segInStr.c_str()); if((instance->status & status_hasPlayed) == 0) if(i > 0) totalTime = totalTime - segInT; tx_length = snprintf(buf, sizeof buf, "%d\t%lu\t%d\t%08lx\t%lu\t%s\t%s\t%.1f\t%.1f\t%.1f\t%s\n", idx, instance->status, instance->pNum, instance->UID, metaList[instance->UID]->rev, instance->type, GetMetaData(instance->UID, "Duration").c_str(), segInT, segOutT, totalTime, Name.c_str()); dMutex->readUnlock(); my_send(cs, buf, tx_length, silent); } lMutex->readUnlock(); return false; } if (!strcmp(arg, "run")) { plRunning = true; wakeManager(); // force the playist manager to process the playlist - get things going goto OK; } if (!strcmp(arg, "halt")) { plRunning = false; wakeManager(); // force the playist manager to process the playlist - clean up goto OK; } if (!strcmp(arg, "newrec")) { Recorder *rec; rec = new Recorder; rec->UID = createMetaRecord(""); registerMetaUser(rec->UID); *lastUID = rec->UID; // add recorder instance to end of recorder list vector::iterator lp; rMutex->writeLock(true); lp = RecList.end(); RecList.insert(lp, rec); rMutex->writeUnlock(); *lastUID = rec->UID; tx_length = snprintf(buf, sizeof buf, "UID=%08lx\n", rec->UID); my_send( cs, buf, tx_length, silent); return false; } if (!strcmp(arg, "initrec")) { // first parameter, meta data item UID in hex format param = strtok_r(nil, " ", &save_pointer); if(param != nil){ if(strlen(param) > 2){ // hex number (UID) char *end; aLong = strtoul(param, &end, 16); }else{ // assume we want the last UID used in this session aLong = *lastUID; } Recorder *rec; rMutex->readLock(true); for(unsigned int idx=0; idxUID == aLong){ // file name string matched the first parameter if(rec->init()){ rMutex->readUnlock(); *lastUID = aLong; goto OK; } } } rMutex->readUnlock(); } *lastUID = 0; } if (!strcmp(arg, "startrec")) { // first parameter, meta data item UID in hex format param = strtok_r(nil, " ", &save_pointer); if(param != nil){ if(strlen(param) > 2){ // hex number (UID) char *end; aLong = strtoul(param, &end, 16); }else{ // assume we want the last UID used in this session aLong = *lastUID; } if(aLong != 0){ Recorder *rec; rMutex->readLock(true); for(unsigned int idx=0; idxUID == aLong){ // file name string matched the first parameter rec->start(); rMutex->readUnlock(); *lastUID = aLong; goto OK; } } rMutex->readUnlock(); *lastUID = aLong; return false; } } *lastUID = 0; } if (!strcmp(arg, "stoprec")) { // first parameter, meta data item UID in hex format param = strtok_r(nil, " ", &save_pointer); if(param != nil){ if(strlen(param) > 2){ // hex number (UID) char *end; aLong = strtoul(param, &end, 16); }else{ // assume we want the last UID used in this session aLong = *lastUID; } if(aLong != 0){ Recorder *rec; rMutex->readLock(true); for(unsigned int idx=0; idxUID == aLong){ rec->stop(); rMutex->readUnlock(); *lastUID = aLong; goto OK; } } rMutex->readUnlock(); *lastUID = aLong; return false; } } *lastUID = 0; } if (!strcmp(arg, "closerec")) { // first parameter, meta data item UID in hex format param = strtok_r(nil, " ", &save_pointer); if(param != nil){ if(strlen(param) > 2){ // hex number (UID) char *end; aLong = strtoul(param, &end, 16); }else{ // assume we want the last UID used in this session aLong = *lastUID; } if(aLong != 0){ Recorder *rec; rMutex->readLock(true); for(unsigned int idx=0; idxUID == aLong){ rMutex->writeLock(true); RecList.erase(RecList.begin() + idx); rMutex->writeUnlock(); delete rec; } } rMutex->readUnlock(); *lastUID = aLong; goto OK; } } *lastUID = 0; } if (!strcmp(arg, "help")) { // update the meter array values getMixerMeters(); tx_length = snprintf(buf, sizeof buf, "AUDIO ENGINE CONTROL COMMAND LIST\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "----System/Configuartion Commands----\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "info\n\tshows version information, etc.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "settings [key string (optional)] [value string(optional)]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tReturns all the current setting (automation, database, etc.) keys and values.\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "get [key string]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tReturns the setting value (automation, database, etc.) for the specified key, the value for that key will be\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "set [key string] [value string]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tSets the specified setting key (automation, database, etc.) to the specified value.\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "setpath [path string]\n\tsets the save path to the settings configuration file (local only command)\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "saveset\n\tsaves all the current settings to the settings configuaration file, if the savesettings path has been set.\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "config [path string]\n\tloads a configuration file.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "clients\n\tshows a list of the currently connected client ip addresses (local only command)\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "echo [message string]\n\tprints a message to stdout.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "dblist \n\tprints a list of the (libdbi) database drivers installed on this machine.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "dbinit \n\tsets up a the database structure of a new database for use.\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tSettings for db_name, db_type and (server) db_user, db_pw, db_server or (SQLite) db_dir must point to the new database.\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "inpath [path string]\n\tsets the save path to the line input definition configuration file (local only command)\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "outpath [path string]\n\tsets the save path to the audio output setup configuration file (local only command)\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "tasks\n\treturns an indexed list of all the running tasks.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "deltask\n\tdeletes/stops the specified (by index) task from running.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "notify [UDP-port integer]\n\tregisters the specified UDP port at the client address who issues this command,\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tto receive player state change notification.\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tnotification packet is of the form:\n\tunsigned short - sequence number\n\tunsigned short - player number\n\tbyte - notice type\n\tfloat - floating point value\n\tlong integer - integer value\n\t\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\ttype:\n\t0x01 - volume (float)\n\t0x02 - balance (float)\n\t0x03 -bus assignment (integer)\n\t0x04 - status bits (integer)\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "exit\n\tclose this control session (remote connection) or shut down this program (local command)\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "----Audio Device Control----\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "meters\n\tretruns the current peak and avarage VU meter readings for each output channel\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "devlist\n\tretruns information on all the audio input and output devices in the system\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "setin [input-name string] [bus hex] [left integer] [right integer] [midiStart hex] [midiStop hex] [device string (UID)]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tcreates or updates a line input definition mapping the left and right channels to the given device\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tinput numbers and enabling the selected output buses. Bus number bits 0 through 15 are stereo mixer output bus enables,\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tbits 17 through 19 are mute group A, B and C enables. Mute group is enables/disabled at play/stop.\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tMidi start and stop hex codes are sent (left to right, three bytes max) when this input is started or stoped.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "delin [name string]\n\tdeletes specified line input definition.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "getin [name string]\n\treturns specified line input definition.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "dumpin\n\treturns a list of all the line input settings.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "savein\n\tsaves the current line input definitions to the line input definition configuration file if\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tthe path to the file has been set and the file permissions allow writing.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "setout [name string] [type string] [muteGain integer] [bus-count integer] [bus-mapping output 0 integer]...[bus-mapping output n integer] [deviceUID string]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tcreates or updates the named output device definition mapping specified mixer output bus to the given\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tdevice output numbers and setting the device type to either 'master' or 'slave'. There can only be one\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tmaster device By setting a devive to master, the current master device, if any will become a slave.\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tThe master device has the lowest latency. The muteGain integer (hex format) sets the output device gains\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\twhen cue (LSB), mute-A, mute-B, or mute-C (MSB) mute groups are enables. 00=off, ff=not attenuation.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "delout [name string]\n\tdeletes specified output device definition.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "getout [name string]\n\treturns specified output device definition.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "outvol [name string]\n\tsets the specified output device volume.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "delay [name string]\n\tsets the specified output device delay in seconds (0 to -10).\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "dumpout\n\treturns a list of all the output device definitions.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "saveout\n"); tx_length = snprintf(buf, sizeof buf, "\tsaves the current output device definitions to the output device definition configuration file if the\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tpath to the file has been set and the file permissions allow writing.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "innames\n\treturns a list (if any) of a device's input channel names.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "outnames\n\treturns a list (if any) of a device's output channel names.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "----Player Commands----\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "status\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tretruns the current status of each player and any recorders. Status integer: bit flags b0-loading, b1-standby, \n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tb2-playing, b3-hasPlayed, b4-finished reading file, b5-logged, b6-waiting, b7-position has changes\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "load [pNum integer] [url string]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tloads the specified player number with the specified. URL is a refernce to the resource to load:\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\ti.e. file:///unix_path_to_audio_file or input:///input_name (see setin and dumpin) or\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\thttp://[user:passwd@]server[:port]/path for streams and web server audio files.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "unload [pNum integer]\n\tunloads the specified player number\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "stop [pNum integer]\n\tstops the specified player number from playing\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "play [pNum integer]\n\tstarts playing the specified player number\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "pos [pNum integer] [position float]\n\tsets the play position (seconds) of the specified player number\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "vol [pNum integer] [volume float]\n\tsets the specified player number volume (1.0 = unity gain)\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "bal [pNum integer] [balance float]\n\tsets the specified player number balance (-1.0 = L, 1.0 = R)\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "bus [pNum integer] [bus hex]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tsets the specified player number output bus (set bit enables the corresponding output)\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "fade [pNum integer] [time float]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tsets the time position to start fading out the specified player number\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "next [pNum integer] [next integer] [time float]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tsets the time position for the specified player (pNum) to start playing the next player (next)\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "----Playlist Commands----\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "list\n\tDumps the current play list\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "add [pos integer] [url string]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "add [pos integer] [pNum integer]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tinserts the specified item or player into the play list at index (decimal) pos (-1 for end of list) or at the same\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tposition as the list item with (hex) pos UID. Note: url supports stop:/// for a playlist stop.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "delete [pos integer]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tdeletes the play list item at index (decimal) pos ot UID (hex) pos. If the item is in a player,\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tit will be unloaded too.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "move [from-pos integer] [to-pos integer]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tmoves the play list item at index from-pos to index to-pos if decimal or UID to UID in hex format.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "run\n\tstarts the automatic loading and playing of items in the play list.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "halt\n\tstops the automatic loading and playing of items in the play list. Items currently loaded into a\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tplayer unloaded unless thay are playing.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "----Player/Playlist Meta Data Commands----\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "urlmeta [url string]\n\treturns a list of metadata (if any) for the item referenced from the given url.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "setmeta [metaUID or pNum integer] [key string] [value string]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tset specified player number (decimal) or UID (hex) meta data key/value pair\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "delmeta [metaUID integer] [key string]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tdeletes specified player number (decimal) or UID (hex) meta data key/value pair\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "getmeta [metaUID integer] [key string]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\treturns specified player number (decimal) or UID (hex) meta string for the given property key.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "dumpmeta [metaUID integer]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\treturns a list of all the meta data key/value pairs for the specified UID\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "setstat [pNum integer] [status integer]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tsets the specified player number status integer: bit flags b0-loading, b1-standby, b2-playing,\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tb3-hasPlayed, b4-finished reading file, b5-logged, b6-waiting, b7-position has changes\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "----Recording Commands----\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "newrec\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tCreates a new file recorder/streamer instance with no specified properties. The UID of the new instance is returned\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tto reference it in the commands that follow\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "initrec [UID integer]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tInitializes the specified file recorder/streamer instance UID, in hex format or -1 for last used UID.\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tThe following MetaData, set prior to this call, is used to set up the initialized recorder/encoder properties.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tSource: name of input source or \"mixer\" to specify a mixer output bus.\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tBus: stereo bus number if the source is mixer. 0 = Monitor, 1 = Cue, 2 = Main, 3 = Alternate\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tLimit: recording time limit in seconds. Set to 0 or ommit for no time limit.\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tType: aiff or wave (uncompressed), or lame/mp3 or shoutcast (using lame encoder - must be in the encoder folder).\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tMode: 0=mono, 1=stereo.\n\tSampleSize: is 8 or 16 (bits) for uncompressed files.\n\tQuality: encoding quality 0=high, 9=low.\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tSampleRate: encoded sample rate in Hz\n\tBitRate: kb/s for compressed files.\n\tPath: File path to directory for file to be saved in.\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tName: name for encoder/stream\n\tServer: Address of streaming server.\n\tPort: Port number for streaming server\n\tPassword: Password for the streaming server.\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tArtist, Album, Genre, url, irc, icq, aim, etc: Other meta tags for streaming. Set Public to 1 for shoutcast listing.\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "startrec [UID integer]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tstarts or re-starts the recorder with the associated UID (hex format)\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "stoprec [UID integer]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tstops the recorder with the associated UID (hex format)\n\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "closerec [UID integer]\n"); my_send( cs, buf, tx_length, silent); tx_length = snprintf(buf, sizeof buf, "\tcloses out the recorder with the associated UID (hex format), deleting the recorder instance.\n\n"); my_send( cs, buf, tx_length, silent); return false; } if (strlen(arg) > 1){ tx_length = snprintf(buf, sizeof buf, "huh?\n"); my_send( cs, buf, tx_length, silent); } return false; OK: tx_length = snprintf(buf, sizeof buf, "OK\n"); my_send( cs, buf, tx_length, silent); return false; } void ShutDown(void) { unsigned int i; ARPlayer *playerPtr = nil; // stop and unload all player for (i=0; istop(); delete playerPtr; } } // shut down audio engine CloseAudioSystem(); for (i=0; iconnected; if(isConnected){ // render from the specified soure AU err= AudioUnitRender( playerPtr->outputSource, inActionFlags, inTimeStamp, 0, inNumberFrames, //# of frames requested ioData); pthread_mutex_lock(&sourceList[inBusNumber].countLock); if(playerPtr->disconnect & (sourceList[inBusNumber].cur_render_count <= 1)){ // we are done, if a disconnect is requested, now is the time to do it. playerPtr->connected = false; sourceList[inBusNumber].playerPtr = NULL; } pthread_mutex_unlock(&sourceList[inBusNumber].countLock); } if(err | !isConnected){ // no connection or render error-- clear the actual buffers for(i = 0 ; i < ioData->mNumberBuffers ; i++){ bzero(ioData->mBuffers[i].mData, ioData->mBuffers[i].mDataByteSize); } *inActionFlags = kAudioUnitRenderAction_OutputIsSilence; } pthread_mutex_lock(&sourceList[inBusNumber].countLock); sourceList[inBusNumber].cur_render_count--; pthread_mutex_unlock(&sourceList[inBusNumber].countLock); return noErr; } OSStatus InitAudioSystem(void) { // set up an 9x2 in by 4x2 out matrix mixer OSStatus result = 0; unsigned int i; unsigned long size; unsigned long data; unsigned long numbuses; ComponentDescription desc; Component comp; AudioStreamBasicDescription format; desc.componentType = kAudioUnitType_Mixer; desc.componentSubType = kAudioUnitSubType_MatrixMixer; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; if(comp = FindNextComponent(0, &desc)){ result = OpenAComponent(comp, &mixer); if (result) return -1; }else{ return -1; } // turn output metering ON data = 1; result = AudioUnitSetProperty( mixer, kAudioUnitProperty_MeteringMode, kAudioUnitScope_Output, 0, &data, sizeof(data) ); if (result) return result; size = sizeof(numbuses); // set output bus counts numbuses = outBusNum; // outBusNum = 1... 1 bus with 8 channels result = AudioUnitSetProperty( mixer, kAudioUnitProperty_BusCount, kAudioUnitScope_Output, 0, &numbuses, sizeof(numbuses) ); if (result) return result; // set output stream format size = sizeof(format); result = AudioUnitGetProperty( mixer, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, &size ); if (result) return result; format.mChannelsPerFrame = outChNum; format.mSampleRate = coreRate; result = AudioUnitSetProperty( mixer, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, sizeof(format) ); if (result) return result; enableMixerOutput(0, 1.0); //Alloc Master Output Bus ring buffer that will hold data between the mixer and all output audio devices // 16 second (bufDuration) buffer length BusBuffer = new AudioRingBuffer(); BusBuffer->Allocate(format.mChannelsPerFrame, format.mBytesPerFrame, (unsigned long)(coreRate * bufDuration)); // set input bus counts numbuses = inBusNum; result = AudioUnitSetProperty( mixer, kAudioUnitProperty_BusCount, kAudioUnitScope_Input, 0, &numbuses, sizeof(numbuses) ); if (result) return result; for (i=0; ireadLock(true); if (instance = pList[inputNum]){ instance->volChange(volume); } pMutex->readUnlock(); // send out notifications data.pNum = inputNum; data.type = nType_vol; data.iVal = 0; data.fVal = volume; Notifier->MakeEntry(data); } float getMixerInputVolume(unsigned long inputNum) { float result; AudioUnitGetParameter(mixer, kMatrixMixerParam_Volume, kAudioUnitScope_Global, ((inputNum*inChNum)<<16)+0xffff, &result); return result; } void setMixerMasterVolume(double volume) { AudioUnitSetParameter(mixer, kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xffffffff, volume, 0); } void setMixerOutputVolume(unsigned long outputNum, double volume) { unsigned int i; for(i = outputNum ; i < (outputNum+1)*outChNum; i++){ AudioUnitSetParameter(mixer, kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xffff0000+(i & 0xffff), volume, 0); } } void enableMixerInput(unsigned long inputNum, double volume) { AudioUnitSetParameter(mixer, kMatrixMixerParam_Enable, kAudioUnitScope_Input, inputNum, volume, 0); } void enableMixerOutput(unsigned long outputNum, double volume) { AudioUnitSetParameter(mixer, kMatrixMixerParam_Enable, kAudioUnitScope_Output, outputNum, volume, 0); } void getMixerMeters(void) { int i; OSStatus err; // inputs for (i=0; i<(inChNum * inBusNum); ++i) { err = AudioUnitGetParameter(mixer, kMatrixMixerParam_PostAveragePower, kAudioUnitScope_Input, i, &avrMeterPost[i]); err = AudioUnitGetParameter(mixer, kMatrixMixerParam_PostPeakHoldLevel, kAudioUnitScope_Input, i, &pkMeterPost[i]); err = AudioUnitGetParameter(mixer, kMatrixMixerParam_PreAveragePower, kAudioUnitScope_Input, i, &avrMeterPre[i]); err = AudioUnitGetParameter(mixer, kMatrixMixerParam_PrePeakHoldLevel, kAudioUnitScope_Input, i, &pkMeterPre[i]); } // outputs for (i=0; i<(outChNum * outBusNum); ++i) { err = AudioUnitGetParameter(mixer, kMatrixMixerParam_PostAveragePower, kAudioUnitScope_Output, i, &avrOutputMeter[i]); err = AudioUnitGetParameter(mixer, kMatrixMixerParam_PostPeakHoldLevel, kAudioUnitScope_Output, i, &pkOutputMeter[i]); } } void ReturnDeviceList(int cs, char *buf, unsigned long size) { CFStringRef devUID; AudioBufferList *bufferlistPtr; AudioDeviceID* theDeviceList; unsigned long nChan, theSize, theNumberDevices, i, j; char str[1024], name[1024]; int tx_length; OSStatus OSerr; OSerr = AudioHardwareGetPropertyInfo ( kAudioHardwarePropertyDevices, &theSize, NULL ); if (OSerr) return; theNumberDevices = theSize / sizeof(AudioDeviceID); theDeviceList = (AudioDeviceID*) malloc(theNumberDevices * sizeof(AudioDeviceID)); theSize = theNumberDevices * sizeof(AudioDeviceID); OSerr = AudioHardwareGetProperty ( kAudioHardwarePropertyDevices, &theSize, theDeviceList ); if (OSerr) return; for(i=0; imNumberBuffers; j++) nChan = nChan + bufferlistPtr->mBuffers[j].mNumberChannels; } free(bufferlistPtr); tx_length = snprintf(buf, size, "%lu\t", nChan); my_send(cs, buf, tx_length, false); // count inputs AudioDeviceGetPropertyInfo( theDeviceList[i], 0, true, kAudioDevicePropertyStreamConfiguration, &theSize, NULL); bufferlistPtr = (AudioBufferList*)malloc(theSize); nChan = 0; OSerr = AudioDeviceGetProperty( theDeviceList[i], 0, true, kAudioDevicePropertyStreamConfiguration, &theSize, bufferlistPtr); if (!OSerr){ for (j=0; j < bufferlistPtr->mNumberBuffers; j++) nChan = nChan + bufferlistPtr->mBuffers[j].mNumberChannels; } free(bufferlistPtr); tx_length = snprintf(buf, size, "%lu\n", nChan); my_send( cs, buf, tx_length, false); } if (theDeviceList) free(theDeviceList); } unsigned long GetDeviceID(const char *UID) { CFStringRef devUID; OSStatus OSerr; AudioDeviceID devID; unsigned long theSize; AudioValueTranslation passData; devUID = CFStringCreateWithCString(NULL,UID, CFStringGetSystemEncoding()); passData.mInputData = &devUID; passData.mInputDataSize = sizeof(CFStringRef); passData.mOutputData = &devID; passData.mOutputDataSize = sizeof(AudioDeviceID); theSize = sizeof passData; OSerr = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID, &theSize, &passData); //deallocate CFRelease(devUID); if (OSerr) return 0; if (devID == kAudioDeviceUnknown) return 0; return (unsigned long)devID; } void ReturnDevChNames(int cs, char *buf, unsigned long size, const char *UID, unsigned char input) { // channel names for given device input and output channels, if any AudioDeviceID theDevice; unsigned long i; unsigned long theSize; char name[256]; int tx_length; OSStatus OSerr; if(theDevice = GetDeviceID(UID)){ i=0; do{ theSize = sizeof name; OSerr = AudioDeviceGetProperty( theDevice, i, input, kAudioDevicePropertyChannelName, //kAudioDevicePropertyPlayThruDestination, &theSize, name); if(OSerr == noErr){ tx_length = snprintf(buf, size, "%lu\t%s\n", i, name); my_send( cs, buf, tx_length, false); i++; } }while(OSerr == noErr); } } void ReturnDevDMDest(int cs, char *buf, unsigned long size, const char *UID,long chan) { // Direct Monitoring destination assignment for the given device and input channel AudioDeviceID theDevice; unsigned long dest; unsigned long theSize; AudioValueTranslation valStruct; char name[256]; int tx_length; OSStatus OSerr; if(theDevice = GetDeviceID(UID)){ theSize = sizeof(dest); OSerr = AudioDeviceGetProperty(theDevice, chan, true, kAudioDevicePropertyPlayThruDestination, &theSize, &dest); if(OSerr == noErr){ valStruct.mInputData = &dest; valStruct.mInputDataSize = theSize; valStruct.mOutputData = name; valStruct.mOutputDataSize = sizeof(name); OSerr = AudioDeviceGetProperty(theDevice, chan, true, kAudioDevicePropertyPlayThruDestinationNameForID, &theSize, &valStruct); if(OSerr == noErr) tx_length = snprintf(buf, size, "%lu\t%s\n", dest, name); else tx_length = snprintf(buf, size, "%lu\t\n", dest); my_send( cs, buf, tx_length, false); } } } void ReturnDevDMDestList(int cs, char *buf, unsigned long size, const char *UID, long chan) { // direct monitoring destination list for the given devive and input channel AudioDeviceID theDevice; unsigned long i, count; unsigned long theSize; unsigned long *destList; AudioValueTranslation valStruct; char name[256]; int tx_length; OSStatus OSerr; if(theDevice = GetDeviceID(UID)){ OSerr = AudioDeviceGetPropertyInfo(theDevice, chan, true, kAudioDevicePropertyPlayThruDestinations, &theSize, NULL); if(!(destList = (unsigned long*)malloc(theSize))) return; count = theSize / sizeof(unsigned long); theSize = sizeof(unsigned long); for(i = 0; i < count; i++){ valStruct.mInputData = &destList[i]; valStruct.mInputDataSize = theSize; valStruct.mOutputData = name; valStruct.mOutputDataSize = sizeof(name); OSerr = AudioDeviceGetProperty(theDevice, chan, true, kAudioDevicePropertyPlayThruDestinationNameForID, &theSize, &valStruct); if(OSerr == noErr) tx_length = snprintf(buf, size, "%lu\t%s\n", destList[i], name); else tx_length = snprintf(buf, size, "%lu\t\n", destList[i]); my_send( cs, buf, tx_length, false); } if(destList) free(destList); } } void UpdateMatrix(unsigned long pNum) { int ic, oc, ob; float bal[4]; // {inLoutL, inRoutL, inLoutR, inRoutR} balance matrix float level; ARPlayer *player; unsigned long crosspoint, mIn, mOut; float balance, L, R; OSStatus problem; long buses; struct notifyData data; pMutex->readLock(true); if (player = pList[pNum]){ buses = player->bus_assignment; balance = player->balance; pMutex->readUnlock(); }else{ pMutex->readUnlock(); return; } R = sqrt(1.0 + balance); L = sqrt(1.0 - balance); bal[0] = L; bal[1] = 0.0; bal[2] = 0.0; bal[3] = R; if (buses & 2L){ //we are in cue mode! buses = 2L; // bus 1 (cue) only } for(ob=0; ob < outChNum/inChNum; ob++){ for(oc=0; oc < inChNum; oc++){ for(ic=0; ic < inChNum; ic++){ level = (float)(1L & (buses >> ob)) * bal[(oc * inChNum) + ic]; mIn = (inChNum * pNum) + ic; mOut = (inChNum * ob) + oc; crosspoint = (mIn << 16) | (mOut & 0x0000FFFF); problem = AudioUnitSetParameter(mixer, kMatrixMixerParam_Volume, kAudioUnitScope_Global, crosspoint, level, 0); } } } // send out notifications data.pNum = pNum; data.type = nType_bal; data.iVal = 0; data.fVal = balance; Notifier->MakeEntry(data); data.type = nType_bus; data.iVal = player->bus_assignment; data.fVal = 0.0; Notifier->MakeEntry(data); } unsigned long createMetaRecord(string url) { unsigned long theID; long seed; unsigned char unique; dMutex->readLock(true); do{ unique = true; seed = time(NULL) / 2; srand(seed); theID = rand(); // search through the metaList to make sure the ID is really unique meta_list_ptr dp; dp = metaList.find(theID); if (dp != metaList.end()){ unique = false; } }while((unique == false) && (theID != 0)); dMutex->writeLock(true); metaList[theID] = new metaDataRec; metaList[theID]->users = 0; metaList[theID]->rev = 0; // new record, revision zero metaList[theID]->metaData["URL"] = url; // URL value is minimum required value to be set dMutex->writeUnlock(); dMutex->readUnlock(); return theID; } void registerMetaUser(unsigned long UID) { metaDataPtr rec; if (UID == 0) return; dMutex->writeLock(true); rec = metaList[UID]; rec->users = rec->users + 1; dMutex->writeUnlock(); } void deleteMetaRecord(unsigned long UID) { metaDataPtr rec; if (UID == 0) return; dMutex->writeLock(true); rec = metaList[UID]; rec->users = rec->users - 1; if(rec->users <= 0){ // delete the pair given the key... no one is using this any more delete metaList[UID]; metaList.erase(UID); } dMutex->writeUnlock(); } unsigned char LoadPlayer(int *pNum, const char *url_str, unsigned long UID) { ARPlayer *player; CFURLRef url; CFStringRef type_str; char ctype[8]; int i; float vol; unsigned char result; pMutex->readLock(true); if(*pNum < 0){ // load next available player for(i = 0; i < inBusNum; i++){ if(pList[i] == NULL) break; } if(i == inBusNum) //no available players return false; else *pNum = i; } if(*pNum >= inBusNum) return false; if(!URLfromString(&url, url_str)) return false; type_str = CFURLCopyScheme(url); if(type_str == NULL){ CFRelease(url); return false; } // make lower case and c-string CFStringGetCString(type_str, ctype, sizeof(ctype), CFStringGetSystemEncoding()); for(unsigned char idx = 0; idx < strlen(ctype); idx = idx + 1) ctype[idx] = tolower(ctype[idx]); CFRelease(type_str); result = false; if(!strcmp(ctype, pType_audio_file)) result = LoadFilePlayer(*pNum, url); if(!strcmp(ctype, pType_input_device)) result = LoadInputPlayer(*pNum, url); if(!strcmp(ctype, pType_db_item)){ result = LoadItemPlayer(pNum, url_str, UID); CFRelease(url); pMutex->readUnlock(); return true; // always return true... let AR handle PLs and Tasks } CFRelease(url); if(result){ player = pList[*pNum]; if(UID == 0){ player->UID = createMetaRecord(url_str); GetURLMetaData(player->UID, url_str); }else player->UID = UID; registerMetaUser(player->UID); // set the balance and bus output settings player->balance = 0; if(player->bus_assignment == 0) player->bus_assignment = 5L; //bus 0 (monitor) and 2 (Main) by default vol = atof(GetMetaData(player->UID, "Volume").c_str()); if((vol == 0.0) || (vol > 10)) vol = 1.0; setMixerInputVolume(*pNum, vol); // set the channel input gain UpdateMatrix(*pNum); // update the mixer matrix to the set balance and buss settings // set fade, if any player->fade = atof(GetMetaData(player->UID, "Fade").c_str()); pMutex->readUnlock(); return true; } pMutex->readUnlock(); return false; } unsigned char LoadFilePlayer(int pNum, CFURLRef url) { FilePlayer *player; char path[4096]; if(CFURLGetFileSystemRepresentation(url, true, (unsigned char*)path, sizeof(path)) == false) return false; if (pList[pNum] == NULL){ player = new FilePlayer(pNum); pMutex->writeLock(true); pList[pNum] = player; pMutex->writeUnlock(); }else{ return false; } if(player->load(path)){ return true; }else{ // failed to load... delete delete player; return false; } } unsigned char LoadInputPlayer(int pNum, CFURLRef url) { InputPlayer *player; input_map_ptr dp; unsigned char isAbsolutePath; CFStringRef input_str; char input[256]; input_str = CFURLCopyStrictPath(url, &isAbsolutePath); if(input_str == NULL) return false; CFStringGetCString(input_str, input, sizeof(input), CFStringGetSystemEncoding()); CFRelease(input_str); // get input definition by name pthread_mutex_lock( &iMutex ); string key(input); // find the pair given the key dp = inputMap.find(key); if (dp == inputMap.end()){ // not found... exit pthread_mutex_unlock( &iMutex ); return false; } pthread_mutex_unlock( &iMutex ); if (pList[pNum] == NULL){ player = new InputPlayer(pNum); pMutex->writeLock(true); pList[pNum] = player; pMutex->writeUnlock(); }else{ return false; } if(player->load((*dp).second.deviceUID, (*dp).second.channelMap)){ player->bus_assignment = (*dp).second.bus; player->startMIDI = (*dp).second.startMIDI; player->stopMIDI = (*dp).second.stopMIDI; return true; }else{ // failed to load... delete delete player; return false; } } unsigned char LoadItemPlayer(int *pNum, const char *url_str, unsigned long UID) { unsigned long localUID; // get metadata for URL // create a meta data record to hold results localUID = createMetaRecord(url_str); registerMetaUser(localUID); // fill the metadata record GetURLMetaData(localUID, url_str); // make sure we have resolved the URL into something other than a database item again. if(GetMetaData(localUID, "Type") == string("item")){ // resolved back into a db item... done with the metadata record... delete deleteMetaRecord(localUID); return false; } // And make sure the URL has changed so we don't end up in a recursive loop! if(GetMetaData(localUID, "URL") == string(url_str)){ // resolved back into the same URL... done with the metadata record... delete deleteMetaRecord(localUID); return false; } // handle according to new type if(GetMetaData(localUID, "Type") == string("task")){ // try running the task dbTaskRunner(localUID, true); deleteMetaRecord(localUID); return false; } if(GetMetaData(localUID, "Type") == string("playlist")){ // ***** no playlist handler for the moment... do nothing deleteMetaRecord(localUID); return false; } if(atoi(GetMetaData(localUID, "Missing").c_str()) == 0){ // re-enter the load player cycle with the newly resoved URL if(LoadPlayer(pNum, GetMetaData(localUID, "URL").c_str(), localUID)){ // and we are done! return true; } } // failed... done with the metadata record... delete deleteMetaRecord(localUID); return false; } void GetURLMetaData(unsigned long UID, const char *url_str) { CFURLRef url; CFStringRef type_str; char ctype[8]; if(UID == 0) return; //make url if(!URLfromString(&url, url_str)) return; type_str = CFURLCopyScheme(url); if(type_str == NULL){ CFRelease(url); return; } // make lower case and c-string CFStringGetCString(type_str, ctype, sizeof(ctype), CFStringGetSystemEncoding()); for(unsigned char idx = 0; idx < strlen(ctype); idx = idx + 1) ctype[idx] = tolower(ctype[idx]); CFRelease(type_str); string Type(ctype); SetMetaData(UID, "Type", Type); if(Type == "file") GetFileMetaData(UID, url); else if(Type == "item") GetItemMetaData(UID, url); else if(Type == "stop") SetMetaData(UID, "Name", "--- Play List Stop ---"); CFRelease(url); } void GetFileMetaData(unsigned long UID, CFURLRef url) { OSType udType; short count, i; char nul = 0; char *type; Handle hData = NULL; Ptr p; FSSpec fsSpec; FSRef fsRef; short fileRefNum; short resID = 0; unsigned char wasChanged; Movie movie = NULL; Track track; Media media; double duration; OSErr err = noErr; UserData userData = 0; char path[4096]; char volume[4096]; unsigned char md5Ptr[16]; unsigned long iNode; string MD5_str; CFURLRef newURL; CFStringRef URLcfstr; if(CFURLGetFileSystemRepresentation(url, true, (unsigned char*)path, sizeof(path)) == false){ SetMetaData(UID, "Missing", "1"); return; } err = EnterMovies(); if (err != noErr) return; // Convert our file path to an FSRef err = FSPathMakeRef((const unsigned char *)path, &fsRef, NULL); if (err != noErr){ SetMetaData(UID, "Missing", "1"); return; } if(!GetFileVolume(path, volume)){ SetMetaData(UID, "Missing", "1"); return; } else if(!GetFileInode(path, &iNode)){ SetMetaData(UID, "Missing", "1"); return; } else if(!GetFileMD5(path, md5Ptr)){ SetMetaData(UID, "Missing", "1"); return; } // re-encode URL for complete formatting to local computer newURL = CFURLCreateFromFileSystemRepresentation(NULL, (UInt8*)path, strlen(path), false); URLcfstr = CFURLGetString(newURL); if(URLcfstr){ CFStringGetCString(URLcfstr, path, sizeof(path), CFStringGetSystemEncoding()); SetMetaData(UID, "URL", string(path)); } CFRelease(newURL); SetMetaData(UID, "Missing", "0"); snprintf(path, sizeof(path), "%lu", iNode); SetMetaData(UID, "FileID", string(path)); SetMetaData(UID, "Mount", string(volume)); MD5_str = ""; for(int i=0; i<16; i++){ snprintf(path, sizeof(path), "%02x", md5Ptr[i]); MD5_str = MD5_str + string(path); } SetMetaData(UID, "MD5", MD5_str); // Convert FSRef to an FSSpec if (FSGetCatalogInfo(&fsRef, kFSCatInfoNone, NULL, NULL, &fsSpec, NULL) != noErr){ SetMetaData(UID, "Missing", "1"); return; } // set name tag to the file name... updated later if there is a name meta tag in the file. SetMetaData(UID, "Name", string((const char*)(fsSpec.name))); // Open the movie file from FSSpec err = OpenMovieFile(&fsSpec, &fileRefNum, fsRdPerm); if (err != noErr){ SetMetaData(UID, "Missing", "1"); return; } // Instantiate the movie and close the file err = NewMovieFromFile(&movie, fileRefNum, &resID, NULL, newMovieActive, &wasChanged); CloseMovieFile(fileRefNum); if (err != noErr) goto cleanUp; userData = GetMovieUserData(movie); if (!userData) goto cleanUp; hData = NewHandle(0); udType = GetNextUserDataType(userData, 0); if(0 != udType) { do { count = CountUserDataType(userData, udType); for(i = 1; i <= count; i++) { if((udType>>24) == kUserDataIsText) { // if the first letter of udType is 0xA9 (the copyright symbol), // then use GetUserDataText instead of GetUserData // We will only read text type user data err = GetUserDataText(userData, hData, udType, i, langEnglish); if (err) goto cleanUp; // null-terminate the string in the handle PtrAndHand(&nul, hData, 1); // turn any CRs into spaces p = *hData; while(*p) { if (*p == kReturnCharCode) *p = ' '; p++; } HLock(hData); if(type = GetTypeLable(udType)){ SetMetaData(UID, string(type), string(*hData)); } HUnlock(hData); } } udType = GetNextUserDataType(userData, udType); } while(0 != udType); } // Get the first sound track track = GetMovieIndTrackType(movie, 1, SoundMediaType, movieTrackMediaType); if (!track) goto cleanUp; // Get the sound track's media media = GetTrackMedia(track); if (!media) goto cleanUp; //Get audio file duration in seconds if (GetMediaTimeScale(media) == 0){ duration = 0.0; snprintf(path, sizeof(path), "%.2f", duration); SetMetaData(UID, "Duration", string(path)); }else{ duration = (double)GetMediaDuration(media) / (double)GetMediaTimeScale(media); snprintf(path, sizeof(path), "%.2f", duration); SetMetaData(UID, "Duration", string(path)); } cleanUp: if (movie) DisposeMovie(movie); DisposeHandle(hData); } char *GetTypeLable(OSType udType) { switch(udType){ case kUserDataTextArtist: return "Artist"; case kUserDataTextAlbum: return "Album"; case kUserDataTextComment: return "Tag"; case kUserDataTextFullName: return "Name"; case kUserDataTextGenre: return "Category"; case kUserDataTextInformation: return "Info"; case kUserDataTextTrack: return "Track"; case kUserDataTextCopyright: return "Copyright"; case kUserDataTextComposer: return "Composer"; default: return NULL; } } unsigned long StrToFourChar(const char *cstr) { unsigned long fourByte = 0; int i, slen; slen = strlen(cstr); if(slen > 4) slen = 4; for(i=0; i < slen; i++){ fourByte = (fourByte << 8) + (unsigned long)(cstr[i]); } return fourByte; } unsigned char checkPnumber(int pNum) { if(pNum < 0) return false; if(pNum >= inBusNum) return false; return true; } unsigned char getPlayerUID(unsigned int *ref) { // the the meta-UID from the specified player if(!checkPnumber(*ref)) return false; ARPlayer *instance; pMutex->readLock(true); instance = pList[*ref]; if(instance == NULL) return false; *ref = instance->UID; pMutex->readUnlock(); return true; } unsigned char getListPos(unsigned int *ref) { // the the meta-UID from the specified player // assumes lMuxex is already locked ARPlayer *instance; for(unsigned int idx=0; idxUID){ *ref = idx; return true; } } return false; } void DeleteFromLists(ARPlayer *instance) { ARPlayer *player; int pNumber, i; unsigned int x; // check player list, delete if found pMutex->readLock(true); pNumber = instance->pNum; for(i=0; inext == pNumber) player->next = -1; } if(player == instance){ pMutex->writeLock(true); pList[i] = NULL; pMutex->writeUnlock(); } } pMutex->readUnlock(); // check play list, delete if found lMutex->readLock(true); vector::iterator lp = PlayList.begin(); for(x=0; xwriteLock(true); PlayList.erase(lp); plRev++; lMutex->writeUnlock(); } lp++; } lMutex->readUnlock(); // check task list, delete if found tMutex->readLock(true); TaskItem *taskIns; for(unsigned int x=0; xUID > instance->UID){ delete taskIns; } } tMutex->readUnlock(); } void SetMetaData(unsigned long uid, string key, string value) { dMutex->readLock(true); if (metaList.find(uid) != metaList.end()){ metaDataPtr Data; Data = metaList[uid]; dMutex->writeLock(true); // check if the key already exists if (Data->metaData.find(key) != Data->metaData.end()){ // key already in map... delete it Data->metaData.erase(key); } Data->metaData[key] = value; // change rev so clients know a changed has been made Data->rev = Data->rev + 1; dMutex->writeUnlock(); } dMutex->readUnlock(); } string GetMetaData(unsigned long uid, string key) { string value; dMutex->readLock(true); if (metaList.find(uid) != metaList.end()){ meta_map_ptr dp; metaDataPtr Data; Data = metaList[uid]; dp = Data->metaData.find(key); if (dp != Data->metaData.end()){ value = (*dp).second; } } dMutex->readUnlock(); return value; } unsigned char URLfromString(CFURLRef *url, const char *url_str) { *url = CFURLCreateWithBytes(NULL, (unsigned char*)url_str, strlen(url_str), kCFStringEncodingASCII, NULL); if(!CFURLCanBeDecomposed(*url)){ CFRelease(*url); return false; } return true; } string NthField(string source, string tolken, unsigned int field) { int pos; string result; result = ""; pos = 0; while(field > 0){ pos = source.find(tolken, pos); if(pos < 0) return result; field = field - 1; pos = pos + tolken.size(); } result = source.substr(pos); pos = result.find(tolken, 0); if(pos > 0) result = result.substr(0, pos); return result; } int CountFields(string source, string tolken) { int pos; int result; result = 0; pos = 0; pos = source.find(tolken, pos); while(pos > -1){ result++; pos = pos + tolken.size(); pos = source.find(tolken, pos); } return result; } void ReplaceAll(string *theStr, string find, string replace) { int pos; if(theStr){ pos = theStr->find(find, 0); while(pos > -1){ theStr->replace(pos, find.length(), replace); pos = theStr->find(find, pos); } } } void Execute(string command, unsigned long UID) { struct execRec{ char **argv; char *str; pid_t child; } *recPtr; char *save_pointer; int i; TaskItem *task; char pidStr[32]; i = command.size(); if(i > 0){ // allocate record for execution var's recPtr = (struct execRec *)malloc(sizeof(struct execRec)); // make s string from STL string recPtr->str = (char *)malloc(sizeof(char) * (i+1)); strcpy(recPtr->str, command.c_str()); // make array for holding arguments i = CountFields(command, " "); recPtr->argv = (char **)malloc(sizeof(char*) * (i+1)); //parse string for spaces, replace with nulls, and fill argv array with pointers to each parsed segment i = 0; recPtr->argv[i] = strtok_r(recPtr->str, " ", &save_pointer); while(recPtr->argv[i]){ i++; recPtr->argv[i] = strtok_r(NULL, " ", &save_pointer); } // execute it; if((recPtr->child = fork()) < 0) return; else if(recPtr->child == 0){ // if we are the forked child execvp(recPtr->str, recPtr->argv); // clean up }else{ snprintf(pidStr, sizeof(pidStr), "%i", recPtr->child); task = new TaskItem(string(recPtr->str)+" PID="+string(pidStr), WaitPID, (void *)recPtr, UID, 0L, true); // no time out (not cancelable) } } } ServerLog::ServerLog(void) { FileName = ""; FileObj = NULL; readPos = 0; writePos = 0; cueSize = 32; logCUE = (ServerLogStruct**)malloc(sizeof(ServerLogStruct*) * cueSize); for(int i = 0; i < cueSize; i++) logCUE[i] = new ServerLogStruct; pthread_mutex_init(&wlock, NULL); pthread_mutex_init(&semlock, NULL); pthread_cond_init(&cueSemaphore, NULL); run = true; pthread_create(&log_thread, NULL, &ServerLog::cueMonitor, this); } ServerLog::~ServerLog(void) { // wait until no one is writing to quit. pthread_mutex_lock( &wlock ); run = false; pthread_cond_signal(&cueSemaphore); pthread_join(log_thread, NULL); if(FileObj){ fclose(FileObj); FileObj = NULL; } pthread_mutex_destroy(&wlock); pthread_mutex_destroy(&semlock); pthread_cond_destroy(&cueSemaphore); for(int i = 0; i < cueSize; i++) delete logCUE[i]; free(logCUE); logCUE = NULL; cueSize = 0; } void ServerLog::MakeEntry(string message) { time_t now; short pos; if(pthread_mutex_lock(&wlock)) return; // lock error... do not make entry now = time(NULL); pos = writePos + 1; if(pos >= cueSize) pos = 0; if(pos != readPos){ localtime_r(&now, &logCUE[writePos]->when); logCUE[writePos]->message = message; writePos = pos; pthread_cond_signal(&cueSemaphore); } pthread_mutex_unlock(&wlock); } void ServerLog::CloseLogFile(void) { pthread_mutex_lock( &semlock ); if(FileObj){ fclose(FileObj); FileObj = NULL; } pthread_mutex_unlock( &semlock ); } void *ServerLog::cueMonitor(void* refCon){ char timeStr[32]; string fullMSG; string localName; short pos; ServerLog *parent = (ServerLog *)(refCon); do{ while(parent->readPos != parent->writePos){ pthread_mutex_lock( &parent->semlock ); // stuff in the cue to put in the log file localName = GetMetaData(0, "file_log"); strftime(timeStr, sizeof(timeStr), "%b %d, %Y %H:%M:%S", &parent->logCUE[parent->readPos]->when); fullMSG = string(timeStr) + " -- " + parent->logCUE[parent->readPos]->message; if(parent->FileObj){ // check that the file name has not changed if(localName != parent->FileName){ fclose(parent->FileObj); parent->FileObj = fopen(localName.c_str(), "a+"); if(parent->FileObj) parent->FileName = localName; } }else{ parent->FileObj = fopen(localName.c_str(), "a+"); if(parent->FileObj) parent->FileName = localName; } if(parent->FileObj){ // file open... write to it. fprintf(parent->FileObj, "%s\n", fullMSG.c_str()); fflush(parent->FileObj); } pthread_mutex_unlock(&parent->semlock); if(msg){ fprintf(stdout, "%s\n", fullMSG.c_str()); fprintf(stdout, constPrompt); fflush(stdout); } pos = parent->readPos + 1; if(pos >= parent->cueSize) pos = 0; parent->readPos = pos; } // Wait for someone to add something new to the cue pthread_mutex_lock( &parent->semlock ); pthread_cond_wait( &parent->cueSemaphore, &parent->semlock ); pthread_mutex_unlock(&parent->semlock); }while(parent->run); pthread_exit(0); } ProgramLog::ProgramLog(void) { readPos = 0; writePos = 0; cueSize = 32; logChangeTime = 0; logCUE = (ProgramLogStruct**)malloc(sizeof(ProgramLogStruct*) * cueSize); for(int i = 0; i < cueSize; i++) logCUE[i] = new ProgramLogStruct; pthread_mutex_init(&wlock, NULL); pthread_mutex_init(&semlock, NULL); pthread_cond_init(&cueSemaphore, NULL); run = true; pthread_create(&log_thread, NULL, &ProgramLog::cueMonitor, this); } ProgramLog::~ProgramLog(void) { run = false; pthread_cond_signal(&cueSemaphore); pthread_join(log_thread, NULL); pthread_mutex_destroy(&wlock); pthread_mutex_destroy(&semlock); pthread_cond_destroy(&cueSemaphore); for(int i = 0; i < cueSize; i++) delete logCUE[i]; free(logCUE); logCUE = NULL; cueSize = 0; } void ProgramLog::MakeEntry(ProgramLogStruct entry) { time_t now; short pos; now = time(NULL); if(pthread_mutex_lock(&wlock)) return; // lock error... do not make entry pos = writePos + 1; if(pos >= cueSize) pos = 0; if(pos != readPos){ *logCUE[writePos] = entry; logCUE[writePos]->when = now; writePos = pos; pthread_cond_signal(&cueSemaphore); } pthread_mutex_unlock(&wlock); } void ProgramLog::UIDEntry(unsigned long passUID, unsigned char Added, unsigned char Played) { ProgramLogStruct *entryRec; entryRec = new ProgramLogStruct; // set up strings entryRec->name = GetMetaData(passUID, "Name"); entryRec->artist = GetMetaData(passUID, "Artist"); entryRec->album = GetMetaData(passUID, "Album"); entryRec->source = GetMetaData(passUID, "URL"); entryRec->comment = GetMetaData(passUID, "Comment"); // set up Integers entryRec->artistID = atol(GetMetaData(passUID, "ArtistID").c_str()); entryRec->albumID = atol(GetMetaData(passUID, "AlbuID").c_str()); entryRec->ID = atol(GetMetaData(passUID, "ID").c_str()); entryRec->location = atol(GetMetaData(0, "db_loc").c_str()); // set up unsigned chars entryRec->added = Added; entryRec->played = Played; MakeEntry(*entryRec); delete entryRec; } void *ProgramLog::cueMonitor(void* refCon) { short pos; ProgramLog *parent = (ProgramLog *)(refCon); do{ while(parent->readPos != parent->writePos){ if(parent->logCUE[parent->readPos]->played){ // update all the recorder/streaming server now-playing meta data Recorder *rec; rMutex->readLock(true); for(unsigned int idx=0; idxpost((ProgramLogStruct*)(parent->logCUE[parent->readPos])); } rMutex->readUnlock(); } // update database program log if(MakeLogEntry((ProgramLogStruct*)(parent->logCUE[parent->readPos]))){ if(parent->logCUE[parent->readPos]->played){ // change the log changed time if the item was played AND the db was updated parent->logChangeTime = parent->logCUE[parent->readPos]->when; } } pos = parent->readPos + 1; if(pos >= parent->cueSize) pos = 0; parent->readPos = pos; } // Wait for someone to add something new to the cue pthread_mutex_lock( &parent->semlock ); pthread_cond_wait( &parent->cueSemaphore, &parent->semlock ); pthread_mutex_unlock(&parent->semlock); }while(parent->run); pthread_exit(0); } NotifyQue::NotifyQue(void) { readPos = 0; writePos = 0; queSize = 256; que = (struct notifyData*)malloc(sizeof(struct notifyData) * queSize); pthread_mutex_init(&wlock, NULL); pthread_mutex_init(&semlock, NULL); pthread_cond_init(&queSemaphore, NULL); run = true; pthread_create(&que_thread, NULL, &NotifyQue::queMonitor, this); } NotifyQue::~NotifyQue(void) { run = false; pthread_cond_signal(&queSemaphore); pthread_join(que_thread, NULL); pthread_mutex_destroy(&wlock); pthread_mutex_destroy(&semlock); pthread_cond_destroy(&queSemaphore); free(que); que = NULL; queSize = 0; } void NotifyQue::MakeEntry(struct notifyData entry) { int pos; if(pthread_mutex_lock(&wlock)) return; // lock error... do not make entry pos = writePos + 1; if(pos >= queSize) pos = 0; if(pos != readPos){ que[writePos] = entry; writePos = pos; pthread_cond_signal(&queSemaphore); } pthread_mutex_unlock(&wlock); } void *NotifyQue::queMonitor(void* refCon) { short pos; int i, sent; struct sockaddr_in adrRec; NotifyQue *parent = (NotifyQue *)(refCon); do{ while(parent->readPos != parent->writePos){ for(i=0; i< maxthread; i++){ if(tp[i].notify_port > 0){ /* Send the string to the client */ adrRec = tp[i].client; adrRec.sin_family = AF_INET; adrRec.sin_port = htons(tp[i].notify_port); tp[i].notify_seq++; parent->que[parent->readPos].seq = tp[i].notify_seq; sent = sendto(tp[i].notify_socket, &parent->que[parent->readPos], sizeof(struct notifyData), 0, (struct sockaddr *)&adrRec, sizeof(adrRec)); } } pos = parent->readPos + 1; if(pos >= parent->queSize) pos = 0; parent->readPos = pos; } // Wait for someone to add something new to the cue pthread_mutex_lock( &parent->semlock ); pthread_cond_wait( &parent->queSemaphore, &parent->semlock ); pthread_mutex_unlock(&parent->semlock); }while(parent->run); pthread_exit(0); } Lock::Lock(void) { pthread_mutexattr_t attr; pthread_mutex_init(&lock, NULL); pthread_mutex_init(&mutex, NULL); pthread_cond_init(&semaphore, NULL); pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&lock, &attr); } Lock::~Lock(void) { pthread_mutex_destroy(&lock); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&semaphore); } unsigned char Lock::readLock(unsigned char block) { unsigned long count; if(block){ pthread_mutex_lock(&lock); count = 0; if(threads.find(pthread_self()->sig) != threads.end()) count = threads[pthread_self()->sig]; threads[pthread_self()->sig] = count + 1; pthread_mutex_unlock(&lock); return true; }else{ if(!pthread_mutex_trylock(&lock)){ // not already locked... we got it count = 0; if(threads.find(pthread_self()->sig) != threads.end()) count = threads[pthread_self()->sig]; threads[pthread_self()->sig] = count + 1; pthread_mutex_unlock(&lock); return true; }else{ // already locked. return false; } } } unsigned char Lock::writeLock(unsigned char block) { // this must be called from a thread that already has a read lock in place, // otherwise deadlock will occur if block is true. if(block){ pthread_mutex_lock(&lock); while(1){ if(threads.size() == 0) return true; if(threads.size() == 1){ if(threads.find(pthread_self()->sig) != threads.end()) // it's just us... OK to take the lock return true; } // some one else is reading right now... can't write lock, wait for read lock change pthread_mutex_unlock(&lock); pthread_mutex_lock(&mutex); pthread_cond_wait(&semaphore, &mutex); pthread_mutex_unlock(&mutex); pthread_mutex_lock(&lock); } // it's all ours! return true; }else{ if(!pthread_mutex_trylock(&lock)){ // not already locked... we got it if(threads.size() == 0) return true; if(threads.size() == 1){ if(threads.find(pthread_self()->sig) != threads.end()) // it's just us... OK to take the lock return true; } // some one else is reading right now... can't write lock pthread_mutex_unlock(&lock); return false; }else{ return false; } } return false; } void Lock::readUnlock(void) { unsigned long count; pthread_mutex_lock(&lock); count = 0; if(threads.find(pthread_self()->sig) != threads.end()){ count = threads[pthread_self()->sig]; count--; if(count == 0) threads.erase(threads.find(pthread_self()->sig)); else threads[pthread_self()->sig] = count; } pthread_mutex_unlock(&lock); // signal any waiting write locks that we have released a read lock pthread_mutex_lock(&mutex); pthread_cond_signal(&semaphore); pthread_mutex_unlock(&mutex); } void Lock::writeUnlock(void) { pthread_mutex_unlock(&lock); } char Lock::getState(void) { if(pthread_mutex_trylock(&lock)){ // failed to lock... must already be locked return 'w'; } // it locked, so no one else had it. Release the lock, since we don't really want it. pthread_mutex_unlock(&lock); if(threads.size() > 0) return 'r'; return '-'; }