--- liblscp/trunk/src/client.c 2004/07/05 16:26:44 171 +++ liblscp/trunk/src/client.c 2005/05/09 10:17:12 523 @@ -2,7 +2,7 @@ // /**************************************************************************** liblscp - LinuxSampler Control Protocol API - Copyright (C) 2004, rncbc aka Rui Nuno Capela. All rights reserved. + Copyright (C) 2004-2005, rncbc aka Rui Nuno Capela. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -40,12 +40,19 @@ static void _lscp_client_evt_proc ( void *pvClient ) { lscp_client_t *pClient = (lscp_client_t *) pvClient; - char achBuffer[LSCP_BUFSIZ]; - int cchBuffer; + + fd_set fds; // File descriptor list for select(). + int fd, fdmax; // Maximum file descriptor number. + struct timeval tv; // For specifying a timeout value. + int iSelect; // Holds select return status. + int iTimeout; + + char achBuffer[LSCP_BUFSIZ]; + int cchBuffer; const char *pszSeps = ":\r\n"; - char *pszToken; - char *pch; - int cchToken; + char * pszToken; + char * pch; + int cchToken; lscp_event_t event; #ifdef DEBUG @@ -53,36 +60,63 @@ #endif while (pClient->evt.iState) { + + // Prepare for waiting on select... + fd = (int) pClient->evt.sock; + FD_ZERO(&fds); + FD_SET((unsigned int) fd, &fds); + fdmax = fd; + + // Use the timeout (x10) select feature ... + iTimeout = 10 * pClient->iTimeout; + if (iTimeout > 1000) { + tv.tv_sec = iTimeout / 1000; + iTimeout -= tv.tv_sec * 1000; + } + else tv.tv_sec = 0; + tv.tv_usec = iTimeout * 1000; + // Wait for event... - cchBuffer = recv(pClient->evt.sock, achBuffer, sizeof(achBuffer), 0); - if (cchBuffer > 0) { - // Make sure received buffer it's null terminated. - achBuffer[cchBuffer] = (char) 0; - // Parse for the notification event message... - pszToken = lscp_strtok(achBuffer, pszSeps, &(pch)); // Have "NOTIFY". - if (strcasecmp(pszToken, "NOTIFY") == 0) { - pszToken = lscp_strtok(NULL, pszSeps, &(pch)); - event = lscp_event_from_text(pszToken); - // And pick the rest of data... - pszToken = lscp_strtok(NULL, pszSeps, &(pch)); - cchToken = (pszToken == NULL ? 0 : strlen(pszToken)); - // Double-check if we're really up to it... - if (pClient->events & event) { - // Invoke the client event callback... - if ((*pClient->pfnCallback)( - pClient, - event, - pszToken, - cchToken, - pClient->pvData) != LSCP_OK) { - pClient->evt.iState = 0; + iSelect = select(fdmax + 1, &fds, NULL, NULL, &tv); + if (iSelect > 0 && FD_ISSET(fd, &fds)) { + // May recv now... + cchBuffer = recv(pClient->evt.sock, achBuffer, sizeof(achBuffer), 0); + if (cchBuffer > 0) { + // Make sure received buffer it's null terminated. + achBuffer[cchBuffer] = (char) 0; + // Parse for the notification event message... + pszToken = lscp_strtok(achBuffer, pszSeps, &(pch)); // Have "NOTIFY". + if (strcasecmp(pszToken, "NOTIFY") == 0) { + pszToken = lscp_strtok(NULL, pszSeps, &(pch)); + event = lscp_event_from_text(pszToken); + // And pick the rest of data... + pszToken = lscp_strtok(NULL, pszSeps, &(pch)); + cchToken = (pszToken == NULL ? 0 : strlen(pszToken)); + // Double-check if we're really up to it... + if (pClient->events & event) { + // Invoke the client event callback... + if ((*pClient->pfnCallback)( + pClient, + event, + pszToken, + cchToken, + pClient->pvData) != LSCP_OK) { + pClient->evt.iState = 0; + } } } + } else { + lscp_socket_perror("_lscp_client_evt_proc: recv"); + pClient->evt.iState = 0; } - } else { - lscp_socket_perror("_lscp_client_evt_proc: recv"); + } // Check if select has in error. + else if (iSelect < 0) { + lscp_socket_perror("_lscp_client_evt_proc: select"); pClient->evt.iState = 0; } + + // Finally, always signal the event. + lscp_cond_signal(pClient->cond); } #ifdef DEBUG @@ -162,6 +196,9 @@ return LSCP_FAILED; } + // Wait on response. + lscp_cond_wait(pClient->cond, pClient->mutex); + // Update as naively as we can... if (iSubscribe) pClient->events |= event; @@ -313,6 +350,7 @@ // Initialize the transaction mutex. lscp_mutex_init(pClient->mutex); + lscp_cond_init(pClient->cond); // Finally we've some success... return pClient; @@ -360,18 +398,18 @@ lscp_mutex_lock(pClient->mutex); // Free up all cached members. - lscp_channel_info_reset(&(pClient->channel_info)); - lscp_engine_info_reset(&(pClient->engine_info)); - lscp_param_info_reset(&(pClient->midi_port_param_info)); - lscp_param_info_reset(&(pClient->audio_channel_param_info)); - lscp_device_port_info_reset(&(pClient->midi_port_info)); - lscp_device_port_info_reset(&(pClient->audio_channel_info)); - lscp_param_info_reset(&(pClient->midi_param_info)); - lscp_param_info_reset(&(pClient->audio_param_info)); - lscp_device_info_reset(&(pClient->midi_device_info)); - lscp_device_info_reset(&(pClient->audio_device_info)); - lscp_driver_info_reset(&(pClient->midi_driver_info)); - lscp_driver_info_reset(&(pClient->audio_driver_info)); + lscp_channel_info_free(&(pClient->channel_info)); + lscp_engine_info_free(&(pClient->engine_info)); + lscp_param_info_free(&(pClient->midi_port_param_info)); + lscp_param_info_free(&(pClient->audio_channel_param_info)); + lscp_device_port_info_free(&(pClient->midi_port_info)); + lscp_device_port_info_free(&(pClient->audio_channel_info)); + lscp_param_info_free(&(pClient->midi_param_info)); + lscp_param_info_free(&(pClient->audio_param_info)); + lscp_device_info_free(&(pClient->midi_device_info)); + lscp_device_info_free(&(pClient->audio_device_info)); + lscp_driver_info_free(&(pClient->midi_driver_info)); + lscp_driver_info_free(&(pClient->audio_driver_info)); // Free available engine table. lscp_szsplit_destroy(pClient->audio_drivers); lscp_szsplit_destroy(pClient->midi_drivers); @@ -399,6 +437,7 @@ // Last but not least, free good ol'transaction mutex. lscp_mutex_unlock(pClient->mutex); lscp_mutex_destroy(pClient->mutex); + lscp_cond_destroy(pClient->cond); free(pClient); @@ -602,6 +641,22 @@ } +/** + * Getting current subscribed events. + * + * @param pClient Pointer to client instance structure. + * + * @returns The current subscrived bit-wise OR'ed event flags. + */ +lscp_event_t lscp_client_get_events ( lscp_client_t *pClient ) +{ + if (pClient == NULL) + return LSCP_EVENT_NONE; + + return pClient->events; +} + + //------------------------------------------------------------------------- // Client command protocol functions. @@ -781,15 +836,41 @@ /** - * Getting all available engines: + * Getting all available engines count: * GET AVAILABLE_ENGINES * * @param pClient Pointer to client instance structure. * + * @returns The current total number of sampler engines on success, + * -1 otherwise. + */ +int lscp_get_available_engines ( lscp_client_t *pClient ) +{ + int iAvailableEngines = -1; + + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + if (lscp_client_call(pClient, "GET AVAILABLE_ENGINES\r\n") == LSCP_OK) + iAvailableEngines = atoi(lscp_client_get_result(pClient)); + + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); + + return iAvailableEngines; +} + + +/** + * Getting all available engines: + * LIST AVAILABLE_ENGINES + * + * @param pClient Pointer to client instance structure. + * * @returns A NULL terminated array of engine name strings, * or NULL in case of failure. */ -const char **lscp_get_available_engines ( lscp_client_t *pClient ) +const char **lscp_list_available_engines ( lscp_client_t *pClient ) { const char *pszSeps = ","; @@ -801,7 +882,7 @@ pClient->engines = NULL; } - if (lscp_client_call(pClient, "GET AVAILABLE_ENGINES\r\n") == LSCP_OK) + if (lscp_client_call(pClient, "LIST AVAILABLE_ENGINES\r\n") == LSCP_OK) pClient->engines = lscp_szsplit_create(lscp_client_get_result(pClient), pszSeps); // Unlock this section down. @@ -848,12 +929,12 @@ if (strcasecmp(pszToken, "DESCRIPTION") == 0) { pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); if (pszToken) - pEngineInfo->description = lscp_unquote(&pszToken, 1); + lscp_unquote_dup(&(pEngineInfo->description), &pszToken); } else if (strcasecmp(pszToken, "VERSION") == 0) { pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); if (pszToken) - pEngineInfo->version = lscp_unquote(&pszToken, 1); + lscp_unquote_dup(&(pEngineInfo->version), &pszToken); } pszToken = lscp_strtok(NULL, pszSeps, &(pch)); } @@ -904,7 +985,7 @@ if (strcasecmp(pszToken, "ENGINE_NAME") == 0) { pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); if (pszToken) - pChannelInfo->engine_name = lscp_unquote(&pszToken, 1); + lscp_unquote_dup(&(pChannelInfo->engine_name), &pszToken); } else if (strcasecmp(pszToken, "AUDIO_OUTPUT_DEVICE") == 0) { pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); @@ -918,19 +999,27 @@ } else if (strcasecmp(pszToken, "AUDIO_OUTPUT_ROUTING") == 0) { pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); - if (pszToken) + if (pszToken) { + if (pChannelInfo->audio_routing) + lscp_szsplit_destroy(pChannelInfo->audio_routing); pChannelInfo->audio_routing = lscp_szsplit_create(pszToken, ","); + } } else if (strcasecmp(pszToken, "INSTRUMENT_FILE") == 0) { pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); if (pszToken) - pChannelInfo->instrument_file = lscp_unquote(&pszToken, 1); + lscp_unquote_dup(&(pChannelInfo->instrument_file), &pszToken); } else if (strcasecmp(pszToken, "INSTRUMENT_NR") == 0) { pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); if (pszToken) pChannelInfo->instrument_nr = atoi(lscp_ltrim(pszToken)); } + else if (strcasecmp(pszToken, "INSTRUMENT_NAME") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + lscp_unquote_dup(&(pChannelInfo->instrument_name), &pszToken); + } else if (strcasecmp(pszToken, "INSTRUMENT_STATUS") == 0) { pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); if (pszToken) @@ -948,8 +1037,13 @@ } else if (strcasecmp(pszToken, "MIDI_INPUT_CHANNEL") == 0) { pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); - if (pszToken) - pChannelInfo->midi_channel = atoi(lscp_ltrim(pszToken)); + if (pszToken) { + pszToken = lscp_ltrim(pszToken); + if (strcasecmp(pszToken, "ALL") == 0) + pChannelInfo->midi_channel = LSCP_MIDI_CHANNEL_ALL; + else + pChannelInfo->midi_channel = atoi(pszToken); + } } else if (strcasecmp(pszToken, "VOLUME") == 0) { pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); @@ -1219,7 +1313,7 @@ if (iSamplerChannel < 0 || iAudioOut < 0 || iAudioIn < 0) return LSCP_FAILED; - sprintf(szQuery, "SET CHANNEL AUDIO_OUTPUT_CHANNELS %d %d %d\r\n", iSamplerChannel, iAudioOut, iAudioIn); + sprintf(szQuery, "SET CHANNEL AUDIO_OUTPUT_CHANNEL %d %d %d\r\n", iSamplerChannel, iAudioOut, iAudioIn); return lscp_client_query(pClient, szQuery); } @@ -1294,8 +1388,8 @@ * * @param pClient Pointer to client instance structure. * @param iSamplerChannel Sampler channel number. - * @param iMidiChannel MIDI channel number to listen (1-16) or - * zero (0) to listen on all channels. + * @param iMidiChannel MIDI channel address number to listen (0-15) or + * LSCP_MIDI_CHANNEL_ALL (16) to listen on all channels. * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ @@ -1306,10 +1400,10 @@ if (iSamplerChannel < 0 || iMidiChannel < 0 || iMidiChannel > 16) return LSCP_FAILED; - if (iMidiChannel > 0) - sprintf(szQuery, "SET CHANNEL MIDI_INPUT_CHANNEL %d %d\r\n", iSamplerChannel, iMidiChannel); - else + if (iMidiChannel == LSCP_MIDI_CHANNEL_ALL) sprintf(szQuery, "SET CHANNEL MIDI_INPUT_CHANNEL %d ALL\r\n", iSamplerChannel); + else + sprintf(szQuery, "SET CHANNEL MIDI_INPUT_CHANNEL %d %d\r\n", iSamplerChannel, iMidiChannel); return lscp_client_query(pClient, szQuery); } @@ -1359,4 +1453,18 @@ } +/** + * Resetting the sampler: + * RESET + * + * @param pClient Pointer to client instance structure. + * + * @returns LSCP_OK on success, LSCP_FAILED otherwise. + */ +lscp_status_t lscp_reset_sampler ( lscp_client_t *pClient ) +{ + return lscp_client_query(pClient, "RESET\r\n"); +} + + // end of client.c