--- liblscp/trunk/src/client.c 2004/06/24 18:25:11 144 +++ liblscp/trunk/src/client.c 2004/07/06 14:06:17 177 @@ -40,60 +40,83 @@ 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; - lscp_event_t event = LSCP_EVENT_NONE; + char * pszToken; + char * pch; + int cchToken; + lscp_event_t event; #ifdef DEBUG fprintf(stderr, "_lscp_client_evt_proc: Client waiting for events.\n"); #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)); - if (strcasecmp(pszToken, "CHANNELS") == 0) - event = LSCP_EVENT_CHANNELS; - else if (strcasecmp(pszToken, "VOICE_COUNT") == 0) - event = LSCP_EVENT_VOICE_COUNT; - else if (strcasecmp(pszToken, "STREAM_COUNT") == 0) - event = LSCP_EVENT_STREAM_COUNT; - else if (strcasecmp(pszToken, "BUFFER_FILL") == 0) - event = LSCP_EVENT_BUFFER_FILL; - else if (strcasecmp(pszToken, "CHANNEL_INFO") == 0) - event = LSCP_EVENT_CHANNEL_INFO; - else if (strcasecmp(pszToken, "MISCELLANEOUS") == 0) - event = LSCP_EVENT_MISCELLANEOUS; - // 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_call: select"); pClient->evt.iState = 0; } + + // Finally, always signal the event. + lscp_cond_signal(pClient->cond); } #ifdef DEBUG @@ -153,7 +176,7 @@ // Subscribe to a single event. static lscp_status_t _lscp_client_evt_request ( lscp_client_t *pClient, int iSubscribe, lscp_event_t event ) { - char *pszEvent; + const char *pszEvent; char szQuery[LSCP_BUFSIZ]; int cchQuery; @@ -161,29 +184,10 @@ return LSCP_FAILED; // Which (single) event? - switch (event) { - case LSCP_EVENT_CHANNELS: - pszEvent = "CHANNELS"; - break; - case LSCP_EVENT_VOICE_COUNT: - pszEvent = "VOICE_COUNT"; - break; - case LSCP_EVENT_STREAM_COUNT: - pszEvent = "STREAM_COUNT"; - break; - case LSCP_EVENT_BUFFER_FILL: - pszEvent = "BUFFER_FILL"; - break; - case LSCP_EVENT_CHANNEL_INFO: - pszEvent = "CHANNEL_INFO"; - break; - case LSCP_EVENT_MISCELLANEOUS: - pszEvent = "CHANNEL_INFO"; - break; - default: + pszEvent = lscp_event_to_text(event); + if (pszEvent == NULL) return LSCP_FAILED; - } - + // Build the query string... cchQuery = sprintf(szQuery, "%sSUBSCRIBE %s\n\n", (iSubscribe == 0 ? "UN" : ""), pszEvent); // Just send data, forget result... @@ -192,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; @@ -320,8 +327,16 @@ pClient->midi_devices = NULL; pClient->engines = NULL; pClient->channels = NULL; - lscp_driver_info_init(&(pClient->audio_info)); - lscp_driver_info_init(&(pClient->midi_info)); + lscp_driver_info_init(&(pClient->audio_driver_info)); + lscp_driver_info_init(&(pClient->midi_driver_info)); + lscp_device_info_init(&(pClient->audio_device_info)); + lscp_device_info_init(&(pClient->midi_device_info)); + lscp_param_info_init(&(pClient->audio_param_info)); + lscp_param_info_init(&(pClient->midi_param_info)); + lscp_device_port_info_init(&(pClient->audio_channel_info)); + lscp_device_port_info_init(&(pClient->midi_port_info)); + lscp_param_info_init(&(pClient->audio_channel_param_info)); + lscp_param_info_init(&(pClient->midi_port_param_info)); lscp_engine_info_init(&(pClient->engine_info)); lscp_channel_info_init(&(pClient->channel_info)); // Initialize error stuff. @@ -335,6 +350,7 @@ // Initialize the transaction mutex. lscp_mutex_init(pClient->mutex); + lscp_cond_init(pClient->cond); // Finally we've some success... return pClient; @@ -384,8 +400,16 @@ // Free up all cached members. lscp_channel_info_reset(&(pClient->channel_info)); lscp_engine_info_reset(&(pClient->engine_info)); - lscp_driver_info_reset(&(pClient->midi_info)); - lscp_driver_info_reset(&(pClient->audio_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)); // Free available engine table. lscp_szsplit_destroy(pClient->audio_drivers); lscp_szsplit_destroy(pClient->midi_drivers); @@ -399,7 +423,7 @@ pClient->engines = NULL; // Free result error stuff. lscp_client_set_result(pClient, NULL, 0); - // Frre stream usage stuff. + // Free stream usage stuff. if (pClient->buffer_fill) free(pClient->buffer_fill); pClient->buffer_fill = NULL; @@ -413,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); @@ -1017,6 +1042,9 @@ * Current number of active disk streams: * GET CHANNEL STREAM_COUNT * + * @param pClient Pointer to client instance structure. + * @param iSamplerChannel Sampler channel number. + * * @returns The number of active disk streams on success, -1 otherwise. */ int lscp_get_channel_stream_count ( lscp_client_t *pClient, int iSamplerChannel ) @@ -1041,6 +1069,60 @@ } +/** + * Current least usage of active disk streams. + * + * @param pClient Pointer to client instance structure. + * @param iSamplerChannel Sampler channel number. + * + * @returns The usage percentage of the least filled active disk stream + * on success, -1 otherwise. + */ +int lscp_get_channel_stream_usage ( lscp_client_t *pClient, int iSamplerChannel ) +{ + char szQuery[LSCP_BUFSIZ]; + int iStreamUsage = -1; + const char *pszResult; + const char *pszSeps = "[]%,"; + char *pszToken; + char *pch; + int iStream; + int iPercent; + + if (iSamplerChannel < 0) + return iStreamUsage; + + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + iStream = 0; + sprintf(szQuery, "GET CHANNEL BUFFER_FILL PERCENTAGE %d\r\n", iSamplerChannel); + if (lscp_client_call(pClient, szQuery) == LSCP_OK) { + pszResult = lscp_client_get_result(pClient); + pszToken = lscp_strtok((char *) pszResult, pszSeps, &(pch)); + while (pszToken) { + if (*pszToken) { + // Skip stream id. + pszToken = lscp_strtok(NULL, pszSeps, &(pch)); + if (pszToken == NULL) + break; + // Get least buffer fill percentage. + iPercent = atol(pszToken); + if (iStreamUsage > iPercent || iStream == 0) + iStreamUsage = iPercent; + iStream++; + } + pszToken = lscp_strtok(NULL, pszSeps, &(pch)); + } + } + + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); + + return iStreamUsage; +} + + /** * Current fill state of disk stream buffers: * GET CHANNEL BUFFER_FILL {BYTES|PERCENTAGE}