--- liblscp/trunk/src/client.c 2004/06/04 21:06:59 107 +++ 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 @@ -28,92 +28,184 @@ // Local prototypes. -static void _lscp_client_set_result (lscp_client_t *pClient, char *pszResult, int iErrno); -static void _lscp_client_udp_proc (void *pvClient); +static void _lscp_client_evt_proc (void *pvClient); - -//------------------------------------------------------------------------- -// Helper functions. - -// Result buffer internal settler. -static void _lscp_client_set_result ( lscp_client_t *pClient, char *pszResult, int iErrno ) -{ - if (pClient->pszResult) - free(pClient->pszResult); - pClient->pszResult = NULL; - - pClient->iErrno = iErrno; - - if (pszResult) - pClient->pszResult = strdup(lscp_ltrim(pszResult)); -} +static lscp_status_t _lscp_client_evt_connect (lscp_client_t *pClient); +static lscp_status_t _lscp_client_evt_request (lscp_client_t *pClient, int iSubscribe, lscp_event_t event); //------------------------------------------------------------------------- -// UDP service (datagram oriented). +// Event service (datagram oriented). -static void _lscp_client_udp_proc ( void *pvClient ) +static void _lscp_client_evt_proc ( void *pvClient ) { lscp_client_t *pClient = (lscp_client_t *) pvClient; - struct sockaddr_in addr; - int cAddr; - char achBuffer[LSCP_BUFSIZ]; - int cchBuffer; - const char *pszSeps = " \r\n"; - char *pszToken; - char *pch; + + 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; #ifdef DEBUG - fprintf(stderr, "_lscp_client_udp_proc: Client waiting for events.\n"); + fprintf(stderr, "_lscp_client_evt_proc: Client waiting for events.\n"); #endif - while (pClient->udp.iState) { - cAddr = sizeof(struct sockaddr_in); - cchBuffer = recvfrom(pClient->udp.sock, achBuffer, sizeof(achBuffer), 0, (struct sockaddr *) &addr, &cAddr); - if (cchBuffer > 0) { -#ifdef DEBUG - lscp_socket_trace("_lscp_client_udp_proc: recvfrom", &addr, achBuffer, cchBuffer); -#endif - if (strncasecmp(achBuffer, "PING ", 5) == 0) { + 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... + 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; - lscp_strtok(achBuffer, pszSeps, &(pch)); // Skip "PING" - lscp_strtok(NULL, pszSeps, &(pch)); // Skip port (must be the same as in addr) - pszToken = lscp_strtok(NULL, pszSeps, &(pch)); // Have session-id. - if (pszToken) { - // Set now client's session-id, if not already - if (pClient->sessid == NULL) - pClient->sessid = strdup(pszToken); - if (pClient->sessid && strcmp(pszToken, pClient->sessid) == 0) { - sprintf(achBuffer, "PONG %s\r\n", pClient->sessid); - cchBuffer = strlen(achBuffer); - if (sendto(pClient->udp.sock, achBuffer, cchBuffer, 0, (struct sockaddr *) &addr, cAddr) < cchBuffer) - lscp_socket_perror("_lscp_client_udp_proc: sendto"); -#ifdef DEBUG - fprintf(stderr, "> %s", achBuffer); -#endif + // 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; + } } } - // Done with life proof. } else { - // - if ((*pClient->pfnCallback)( - pClient, - achBuffer, - cchBuffer, - pClient->pvData) != LSCP_OK) { - pClient->udp.iState = 0; - } + lscp_socket_perror("_lscp_client_evt_proc: recv"); + pClient->evt.iState = 0; } - } else { - lscp_socket_perror("_lscp_client_udp_proc: recvfrom"); - pClient->udp.iState = 0; + } // 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 + fprintf(stderr, "_lscp_client_evt_proc: Client closing.\n"); +#endif +} + + +//------------------------------------------------------------------------- +// Event subscription helpers. + +// Open the event service socket connection. +static lscp_status_t _lscp_client_evt_connect ( lscp_client_t *pClient ) +{ + lscp_socket_t sock; + struct sockaddr_in addr; + int cAddr; +#if defined(WIN32) + int iSockOpt = (-1); +#endif + + // Prepare the event connection socket... + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) { + lscp_socket_perror("_lscp_client_evt_connect: socket"); + return LSCP_FAILED; } +#if defined(WIN32) + if (setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) + lscp_socket_perror("lscp_client_evt_connect: setsockopt(SO_DONTLINGER)"); +#endif + #ifdef DEBUG - fprintf(stderr, "_lscp_client_udp_proc: Client closing.\n"); + lscp_socket_getopts("_lscp_client_evt_connect:", sock); #endif + + // Use same address of the command connection. + cAddr = sizeof(struct sockaddr_in); + memmove((char *) &addr, &(pClient->cmd.addr), cAddr); + + // Start the connection... + if (connect(sock, (struct sockaddr *) &addr, cAddr) == SOCKET_ERROR) { + lscp_socket_perror("_lscp_client_evt_connect: connect"); + closesocket(sock); + return LSCP_FAILED; + } + + // Set our socket agent struct... + lscp_socket_agent_init(&(pClient->evt), sock, &addr, cAddr); + + // And finally the service thread... + return lscp_socket_agent_start(&(pClient->evt), _lscp_client_evt_proc, pClient, 0); +} + + +// Subscribe to a single event. +static lscp_status_t _lscp_client_evt_request ( lscp_client_t *pClient, int iSubscribe, lscp_event_t event ) +{ + const char *pszEvent; + char szQuery[LSCP_BUFSIZ]; + int cchQuery; + + if (pClient == NULL) + return LSCP_FAILED; + + // Which (single) event? + 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... + if (send(pClient->evt.sock, szQuery, cchQuery, 0) < cchQuery) { + lscp_socket_perror("_lscp_client_evt_request: send"); + return LSCP_FAILED; + } + + // Wait on response. + lscp_cond_wait(pClient->cond, pClient->mutex); + + // Update as naively as we can... + if (iSubscribe) + pClient->events |= event; + else + pClient->events &= ~event; + + return LSCP_OK; } @@ -155,7 +247,9 @@ lscp_socket_t sock; struct sockaddr_in addr; int cAddr; +#if defined(WIN32) int iSockOpt = (-1); +#endif if (pfnCallback == NULL) { fprintf(stderr, "lscp_client_create: Invalid client callback function.\n"); @@ -164,7 +258,7 @@ pHost = gethostbyname(pszHost); if (pHost == NULL) { - lscp_socket_perror("lscp_client_create: gethostbyname"); + lscp_socket_herror("lscp_client_create: gethostbyname"); return NULL; } @@ -184,22 +278,22 @@ fprintf(stderr, "lscp_client_create: pClient=%p: pszHost=%s iPort=%d.\n", pClient, pszHost, iPort); #endif - // Prepare the TCP connection socket... + // Prepare the command connection socket... sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { - lscp_socket_perror("lscp_client_create: tcp: socket"); + lscp_socket_perror("lscp_client_create: cmd: socket"); free(pClient); return NULL; } #if defined(WIN32) if (setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) - lscp_socket_perror("lscp_client_create: tcp: setsockopt(SO_DONTLINGER)"); + lscp_socket_perror("lscp_client_create: cmd: setsockopt(SO_DONTLINGER)"); #endif #ifdef DEBUG - lscp_socket_getopts("lscp_client_create: tcp", sock); + lscp_socket_getopts("lscp_client_create: cmd", sock); #endif cAddr = sizeof(struct sockaddr_in); @@ -209,71 +303,40 @@ addr.sin_port = htons((short) iPort); if (connect(sock, (struct sockaddr *) &addr, cAddr) == SOCKET_ERROR) { - lscp_socket_perror("lscp_client_create: tcp: connect"); - closesocket(sock); - free(pClient); - return NULL; - } - - lscp_socket_agent_init(&(pClient->tcp), sock, &addr, cAddr); - -#ifdef DEBUG - fprintf(stderr, "lscp_client_create: tcp: pClient=%p: sock=%d addr=%s port=%d.\n", pClient, pClient->tcp.sock, inet_ntoa(pClient->tcp.addr.sin_addr), ntohs(pClient->tcp.addr.sin_port)); -#endif - - // Prepare the UDP datagram service socket... - - sock = socket(AF_INET, SOCK_DGRAM, 0); - if (sock == INVALID_SOCKET) { - lscp_socket_perror("lscp_client_create: udp: socket"); - lscp_socket_agent_free(&(pClient->tcp)); - free(pClient); - return NULL; - } - - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) - lscp_socket_perror("lscp_client_create: udp: setsockopt(SO_REUSEADDR)"); - -#ifdef DEBUG - lscp_socket_getopts("lscp_client_create: udp", sock); -#endif - - cAddr = sizeof(struct sockaddr_in); - memset((char *) &addr, 0, cAddr); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = htons(0); - - if (bind(sock, (const struct sockaddr *) &addr, cAddr) == SOCKET_ERROR) { - lscp_socket_perror("lscp_client_create: udp: bind"); - lscp_socket_agent_free(&(pClient->tcp)); + lscp_socket_perror("lscp_client_create: cmd: connect"); closesocket(sock); free(pClient); return NULL; } - if (getsockname(sock, (struct sockaddr *) &addr, &cAddr) == SOCKET_ERROR) { - lscp_socket_perror("lscp_client_create: udp: getsockname"); - lscp_socket_agent_free(&(pClient->tcp)); - closesocket(sock); - free(pClient); - return NULL; - } - - lscp_socket_agent_init(&(pClient->udp), sock, &addr, cAddr); + // Initialize the command socket agent struct... + lscp_socket_agent_init(&(pClient->cmd), sock, &addr, cAddr); #ifdef DEBUG - fprintf(stderr, "lscp_client_create: udp: pClient=%p: sock=%d addr=%s port=%d.\n", pClient, pClient->udp.sock, inet_ntoa(pClient->udp.addr.sin_addr), ntohs(pClient->udp.addr.sin_port)); + fprintf(stderr, "lscp_client_create: cmd: pClient=%p: sock=%d addr=%s port=%d.\n", pClient, pClient->cmd.sock, inet_ntoa(pClient->cmd.addr.sin_addr), ntohs(pClient->cmd.addr.sin_port)); #endif - // No session id, yet. - pClient->sessid = NULL; + // Initialize the event service socket struct... + lscp_socket_agent_init(&(pClient->evt), INVALID_SOCKET, NULL, 0); + // No events subscribed, yet. + pClient->events = LSCP_EVENT_NONE; // Initialize cached members. pClient->audio_drivers = NULL; pClient->midi_drivers = NULL; + pClient->audio_devices = NULL; + pClient->midi_devices = NULL; pClient->engines = NULL; - lscp_driver_info_init(&(pClient->audio_info)); - lscp_driver_info_init(&(pClient->midi_info)); + pClient->channels = NULL; + 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. @@ -287,16 +350,7 @@ // Initialize the transaction mutex. lscp_mutex_init(pClient->mutex); - - // Now's finally time to startup threads... - // UDP service thread... - if (lscp_socket_agent_start(&(pClient->udp), _lscp_client_udp_proc, pClient, 0) != LSCP_OK) { - lscp_socket_agent_free(&(pClient->tcp)); - lscp_socket_agent_free(&(pClient->udp)); - lscp_mutex_destroy(pClient->mutex); - free(pClient); - return NULL; - } + lscp_cond_init(pClient->cond); // Finally we've some success... return pClient; @@ -317,8 +371,8 @@ fprintf(stderr, "lscp_client_join: pClient=%p.\n", pClient); #endif -// lscp_socket_agent_join(&(pClient->udp)); - lscp_socket_agent_join(&(pClient->tcp)); +// lscp_socket_agent_join(&(pClient->evt)); + lscp_socket_agent_join(&(pClient->cmd)); return LSCP_OK; } @@ -340,26 +394,36 @@ fprintf(stderr, "lscp_client_destroy: pClient=%p.\n", pClient); #endif - // Free session-id, if any. - if (pClient->sessid) - free(pClient->sessid); - pClient->sessid = NULL; + // Lock this section up. + 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_driver_info_reset(&(pClient->midi_info)); - lscp_driver_info_reset(&(pClient->audio_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); + lscp_isplit_destroy(pClient->audio_devices); + lscp_isplit_destroy(pClient->midi_devices); lscp_szsplit_destroy(pClient->engines); + lscp_isplit_destroy(pClient->channels); // Make them null. pClient->audio_drivers = NULL; pClient->midi_drivers = NULL; pClient->engines = NULL; // Free result error stuff. - _lscp_client_set_result(pClient, NULL, 0); - // Frre stream usage stuff. + lscp_client_set_result(pClient, NULL, 0); + // Free stream usage stuff. if (pClient->buffer_fill) free(pClient->buffer_fill); pClient->buffer_fill = NULL; @@ -367,11 +431,13 @@ pClient->iTimeout = 0; // Free socket agents. - lscp_socket_agent_free(&(pClient->udp)); - lscp_socket_agent_free(&(pClient->tcp)); + lscp_socket_agent_free(&(pClient->evt)); + lscp_socket_agent_free(&(pClient->cmd)); // 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); @@ -418,7 +484,6 @@ //------------------------------------------------------------------------- // Client common protocol functions. - /** * Submit a command query line string to the server. The query string * must be cr/lf and null terminated. Besides the return code, the @@ -434,123 +499,20 @@ */ lscp_status_t lscp_client_query ( lscp_client_t *pClient, const char *pszQuery ) { - 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; - int cchQuery; - char achResult[LSCP_BUFSIZ]; - int cchResult; - const char *pszSeps = ":[]"; - char *pszResult; - char *pszToken; - char *pch; - int iErrno; - - lscp_status_t ret = LSCP_FAILED; - - if (pClient == NULL) - return ret; - + lscp_status_t ret; + // Lock this section up. lscp_mutex_lock(pClient->mutex); - pszResult = NULL; - iErrno = -1; - - // Send data, and then, wait for the result... - cchQuery = strlen(pszQuery); - if (send(pClient->tcp.sock, pszQuery, cchQuery, 0) < cchQuery) { - lscp_socket_perror("lscp_client_query: send"); - lscp_mutex_unlock(pClient->mutex); - return ret; - } - - // Prepare for waiting on select... - fd = (int) pClient->tcp.sock; - FD_ZERO(&fds); - FD_SET((unsigned int) fd, &fds); - fdmax = fd; - - // Use the timeout select feature... - iTimeout = 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... - iSelect = select(fdmax + 1, &fds, NULL, NULL, &tv); - if (iSelect > 0 && FD_ISSET(fd, &fds)) { - // May recv now... - cchResult = recv(pClient->tcp.sock, achResult, sizeof(achResult), 0); - if (cchResult > 0) { - // Assume early success. - ret = LSCP_OK; - // Always force the result to be null terminated (and trim trailing CRLFs)! - while (cchResult > 0 && (achResult[cchResult - 1] == '\n' || achResult[cchResult- 1] == '\r')) - cchResult--; - achResult[cchResult] = (char) 0; - // Check if the response it's an error or warning message. - if (strncasecmp(achResult, "WRN:", 4) == 0) - ret = LSCP_WARNING; - else if (strncasecmp(achResult, "ERR:", 4) == 0) - ret = LSCP_ERROR; - // So we got a result... - if (ret == LSCP_OK) { - // Reset errno in case of success. - iErrno = 0; - // Is it a special successful response? - if (strncasecmp(achResult, "OK[", 3) == 0) { - // Parse the OK message, get the return string under brackets... - pszToken = lscp_strtok(achResult, pszSeps, &(pch)); - if (pszToken) - pszResult = lscp_strtok(NULL, pszSeps, &(pch)); - } - else pszResult = achResult; - // The result string is now set to the command response, if any. - } else { - // Parse the error/warning message, skip first colon... - pszToken = lscp_strtok(achResult, pszSeps, &(pch)); - if (pszToken) { - // Get the error number... - pszToken = lscp_strtok(NULL, pszSeps, &(pch)); - if (pszToken) { - iErrno = atoi(pszToken); - // And make the message text our final result. - pszResult = lscp_strtok(NULL, pszSeps, &(pch)); - } - } - // The result string is set to the error/warning message text. - } - } - else if (cchResult == 0) { - // Fake a result message. - pszResult = "Server terminated the connection"; - ret = LSCP_QUIT; - } - else lscp_socket_perror("lscp_client_query: recv"); - } // Check if select has timed out. - else if (iSelect == 0) { - // Fake a result message. - pszResult = "Timeout during receive operation"; - ret = LSCP_TIMEOUT; - } - else lscp_socket_perror("lscp_client_query: select"); - - // Make the result official... - _lscp_client_set_result(pClient, pszResult, iErrno); - - // Can go on with it... + // Just make the now guarded call. + ret = lscp_client_call(pClient, pszQuery); + + // Unlock this section down. lscp_mutex_unlock(pClient->mutex); - + return ret; } - /** * Get the last received result string. In case of error or warning, * this is the text of the error or warning message issued. @@ -590,78 +552,111 @@ // Client registration protocol functions. /** - * Register frontend for receiving UDP event messages: - * SUBSCRIBE NOTIFICATION + * Register frontend for receiving event messages: + * SUBSCRIBE CHANNELS | VOICE_COUNT | STREAM_COUNT | BUFFER_FILL + * | CHANNEL_INFO | MISCELLANEOUS * * @param pClient Pointer to client instance structure. + * @param events Bit-wise OR'ed event flags to subscribe. * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_client_subscribe ( lscp_client_t *pClient ) +lscp_status_t lscp_client_subscribe ( lscp_client_t *pClient, lscp_event_t events ) { - lscp_status_t ret; - char szQuery[LSCP_BUFSIZ]; - const char *pszResult; - const char *pszSeps = "[]"; - char *pszToken; - char *pch; + lscp_status_t ret = LSCP_FAILED; - if (pClient == NULL || pClient->sessid) + if (pClient == NULL) return LSCP_FAILED; - sprintf(szQuery, "SUBSCRIBE NOTIFICATION %d\r\n", ntohs(pClient->udp.addr.sin_port)); - ret = lscp_client_query(pClient, szQuery); - if (ret == LSCP_OK) { - pszResult = lscp_client_get_result(pClient); -#ifdef DEBUG - fprintf(stderr, "lscp_client_subscribe: %s\n", pszResult); -#endif - // Check for the session-id on "OK[sessid]" response. - pszToken = lscp_strtok(pszResult, pszSeps, &(pch)); - if (pszToken && strcasecmp(pszToken, "OK") == 0) { - pszToken = lscp_strtok(NULL, pszSeps, &(pch)); - if (pszToken) - pClient->sessid = strdup(pszToken); - } - } + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + // If applicable, start the alternate connection... + if (pClient->events == LSCP_EVENT_NONE) + ret = _lscp_client_evt_connect(pClient); + + // Send the subscription commands. + if (ret == LSCP_OK && (events & LSCP_EVENT_CHANNELS)) + ret = _lscp_client_evt_request(pClient, 1, LSCP_EVENT_CHANNELS); + if (ret == LSCP_OK && (events & LSCP_EVENT_VOICE_COUNT)) + ret = _lscp_client_evt_request(pClient, 1, LSCP_EVENT_VOICE_COUNT); + if (ret == LSCP_OK && (events & LSCP_EVENT_STREAM_COUNT)) + ret = _lscp_client_evt_request(pClient, 1, LSCP_EVENT_STREAM_COUNT); + if (ret == LSCP_OK && (events & LSCP_EVENT_BUFFER_FILL)) + ret = _lscp_client_evt_request(pClient, 1, LSCP_EVENT_BUFFER_FILL); + if (ret == LSCP_OK && (events & LSCP_EVENT_CHANNEL_INFO)) + ret = _lscp_client_evt_request(pClient, 1, LSCP_EVENT_CHANNEL_INFO); + if (ret == LSCP_OK && (events & LSCP_EVENT_MISCELLANEOUS)) + ret = _lscp_client_evt_request(pClient, 1, LSCP_EVENT_MISCELLANEOUS); + + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); return ret; } /** - * Deregister frontend for not receiving UDP event messages anymore: - * UNSUBSCRIBE NOTIFICATION + * Deregister frontend from receiving UDP event messages anymore: + * SUBSCRIBE CHANNELS | VOICE_COUNT | STREAM_COUNT | BUFFER_FILL + * | CHANNEL_INFO | MISCELLANEOUS * * @param pClient Pointer to client instance structure. + * @param events Bit-wise OR'ed event flags to unsubscribe. * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_client_unsubscribe ( lscp_client_t *pClient ) +lscp_status_t lscp_client_unsubscribe ( lscp_client_t *pClient, lscp_event_t events ) { - lscp_status_t ret; - char szQuery[LSCP_BUFSIZ]; + lscp_status_t ret = LSCP_OK; if (pClient == NULL) return LSCP_FAILED; - if (pClient->sessid == NULL) - return LSCP_FAILED; - sprintf(szQuery, "UNSUBSCRIBE NOTIFICATION %s\n\n", pClient->sessid); - ret = lscp_client_query(pClient, szQuery); - if (ret == LSCP_OK) { -#ifdef DEBUG - fprintf(stderr, "lscp_client_unsubscribe: %s\n", lscp_client_get_result(pClient)); -#endif - // Bail out session-id string. - free(pClient->sessid); - pClient->sessid = NULL; - } + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + // Send the unsubscription commands. + if (ret == LSCP_OK && (events & LSCP_EVENT_CHANNELS)) + ret = _lscp_client_evt_request(pClient, 0, LSCP_EVENT_CHANNELS); + if (ret == LSCP_OK && (events & LSCP_EVENT_VOICE_COUNT)) + ret = _lscp_client_evt_request(pClient, 0, LSCP_EVENT_VOICE_COUNT); + if (ret == LSCP_OK && (events & LSCP_EVENT_STREAM_COUNT)) + ret = _lscp_client_evt_request(pClient, 0, LSCP_EVENT_STREAM_COUNT); + if (ret == LSCP_OK && (events & LSCP_EVENT_BUFFER_FILL)) + ret = _lscp_client_evt_request(pClient, 0, LSCP_EVENT_BUFFER_FILL); + if (ret == LSCP_OK && (events & LSCP_EVENT_CHANNEL_INFO)) + ret = _lscp_client_evt_request(pClient, 0, LSCP_EVENT_CHANNEL_INFO); + if (ret == LSCP_OK && (events & LSCP_EVENT_MISCELLANEOUS)) + ret = _lscp_client_evt_request(pClient, 0, LSCP_EVENT_MISCELLANEOUS); + + // If necessary, close the alternate connection... + if (pClient->events == LSCP_EVENT_NONE) + lscp_socket_agent_free(&(pClient->evt)); + + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); return ret; } +/** + * 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. @@ -683,7 +678,30 @@ if (pszFileName == NULL || iSamplerChannel < 0) return LSCP_FAILED; - sprintf(szQuery, "LOAD INSTRUMENT %s %d %d\r\n", pszFileName, iInstrIndex, iSamplerChannel); + sprintf(szQuery, "LOAD INSTRUMENT '%s' %d %d\r\n", pszFileName, iInstrIndex, iSamplerChannel); + return lscp_client_query(pClient, szQuery); +} + + +/** + * Loading an instrument in the background (non modal): + * LOAD INSTRUMENT NON_MODAL + * + * @param pClient Pointer to client instance structure. + * @param pszFileName Instrument file name. + * @param iInstrIndex Instrument index number. + * @param iSamplerChannel Sampler Channel. + * + * @returns LSCP_OK on success, LSCP_FAILED otherwise. + */ +lscp_status_t lscp_load_instrument_non_modal ( lscp_client_t *pClient, const char *pszFileName, int iInstrIndex, int iSamplerChannel ) +{ + char szQuery[LSCP_BUFSIZ]; + + if (pszFileName == NULL || iSamplerChannel < 0) + return LSCP_FAILED; + + sprintf(szQuery, "LOAD INSTRUMENT NON_MODAL '%s' %d %d\r\n", pszFileName, iInstrIndex, iSamplerChannel); return lscp_client_query(pClient, szQuery); } @@ -722,13 +740,55 @@ int lscp_get_channels ( lscp_client_t *pClient ) { int iChannels = -1; - if (lscp_client_query(pClient, "GET CHANNELS\r\n") == LSCP_OK) + + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + if (lscp_client_call(pClient, "GET CHANNELS\r\n") == LSCP_OK) iChannels = atoi(lscp_client_get_result(pClient)); + + // Unlock this section doen. + lscp_mutex_unlock(pClient->mutex); + return iChannels; } /** + * List current sampler channels number identifiers: + * LIST CHANNELS + * + * @param pClient Pointer to client instance structure. + * + * @returns An array of the sampler channels identifiers as positive integers, + * terminated with -1 on success, NULL otherwise. + */ +int *lscp_list_channels ( lscp_client_t *pClient ) +{ + const char *pszSeps = ","; + + if (pClient == NULL) + return NULL; + + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + if (pClient->channels) { + lscp_isplit_destroy(pClient->channels); + pClient->channels = NULL; + } + + if (lscp_client_call(pClient, "LIST CHANNELS\r\n") == LSCP_OK) + pClient->channels = lscp_isplit_create(lscp_client_get_result(pClient), pszSeps); + + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); + + return pClient->channels; +} + + +/** * Adding a new sampler channel: * ADD CHANNEL * @@ -740,8 +800,16 @@ int lscp_add_channel ( lscp_client_t *pClient ) { int iSamplerChannel = -1; - if (lscp_client_query(pClient, "ADD CHANNEL\r\n") == LSCP_OK) + + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + if (lscp_client_call(pClient, "ADD CHANNEL\r\n") == LSCP_OK) iSamplerChannel = atoi(lscp_client_get_result(pClient)); + + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); + return iSamplerChannel; } @@ -768,26 +836,58 @@ /** - * 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 = ","; + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + if (pClient->engines) { lscp_szsplit_destroy(pClient->engines); pClient->engines = NULL; } - if (lscp_client_query(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. + lscp_mutex_unlock(pClient->mutex); + return (const char **) pClient->engines; } @@ -815,28 +915,34 @@ if (pszEngineName == NULL) return NULL; + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + pEngineInfo = &(pClient->engine_info); lscp_engine_info_reset(pEngineInfo); sprintf(szQuery, "GET ENGINE INFO %s\r\n", pszEngineName); - if (lscp_client_query(pClient, szQuery) != LSCP_OK) - return NULL; - - pszResult = lscp_client_get_result(pClient); - pszToken = lscp_strtok(pszResult, pszSeps, &(pch)); - while (pszToken) { - if (strcasecmp(pszToken, "DESCRIPTION") == 0) { - pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); - if (pszToken) - pEngineInfo->description = lscp_unquote(&pszToken, 1); - } - else if (strcasecmp(pszToken, "VERSION") == 0) { - pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); - if (pszToken) - pEngineInfo->version = lscp_unquote(&pszToken, 1); + if (lscp_client_call(pClient, szQuery) == LSCP_OK) { + pszResult = lscp_client_get_result(pClient); + pszToken = lscp_strtok((char *) pszResult, pszSeps, &(pch)); + while (pszToken) { + if (strcasecmp(pszToken, "DESCRIPTION") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + lscp_unquote_dup(&(pEngineInfo->description), &pszToken); + } + else if (strcasecmp(pszToken, "VERSION") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + lscp_unquote_dup(&(pEngineInfo->version), &pszToken); + } + pszToken = lscp_strtok(NULL, pszSeps, &(pch)); } - pszToken = lscp_strtok(NULL, pszSeps, &(pch)); } + else pEngineInfo = NULL; + + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); return pEngineInfo; } @@ -865,68 +971,92 @@ if (iSamplerChannel < 0) return NULL; + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + pChannelInfo = &(pClient->channel_info); lscp_channel_info_reset(pChannelInfo); sprintf(szQuery, "GET CHANNEL INFO %d\r\n", iSamplerChannel); - if (lscp_client_query(pClient, szQuery) != LSCP_OK) - return NULL; - - pszResult = lscp_client_get_result(pClient); - pszToken = lscp_strtok(pszResult, pszSeps, &(pch)); - while (pszToken) { - if (strcasecmp(pszToken, "ENGINE_NAME") == 0) { - pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); - if (pszToken) - pChannelInfo->engine_name = lscp_unquote(&pszToken, 1); - } - else if (strcasecmp(pszToken, "AUDIO_OUTPUT_DEVICE") == 0) { - pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); - if (pszToken) - pChannelInfo->audio_device = atoi(lscp_ltrim(pszToken)); - } - else if (strcasecmp(pszToken, "AUDIO_OUTPUT_CHANNELS") == 0) { - pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); - if (pszToken) - pChannelInfo->audio_channels = atoi(lscp_ltrim(pszToken)); - } - else if (strcasecmp(pszToken, "AUDIO_OUTPUT_ROUTING") == 0) { - pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); - if (pszToken) - 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); - } - 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, "MIDI_INPUT_DEVICE") == 0) { - pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); - if (pszToken) - pChannelInfo->midi_device = atoi(lscp_ltrim(pszToken)); - } - else if (strcasecmp(pszToken, "MIDI_INPUT_PORT") == 0) { - pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); - if (pszToken) - pChannelInfo->midi_port = atoi(lscp_ltrim(pszToken)); - } - else if (strcasecmp(pszToken, "MIDI_INPUT_CHANNEL") == 0) { - pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); - if (pszToken) - pChannelInfo->midi_channel = atoi(lscp_ltrim(pszToken)); - } - else if (strcasecmp(pszToken, "VOLUME") == 0) { - pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); - if (pszToken) - pChannelInfo->volume = (float) atof(lscp_ltrim(pszToken)); + if (lscp_client_call(pClient, szQuery) == LSCP_OK) { + pszResult = lscp_client_get_result(pClient); + pszToken = lscp_strtok((char *) pszResult, pszSeps, &(pch)); + while (pszToken) { + if (strcasecmp(pszToken, "ENGINE_NAME") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + lscp_unquote_dup(&(pChannelInfo->engine_name), &pszToken); + } + else if (strcasecmp(pszToken, "AUDIO_OUTPUT_DEVICE") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + pChannelInfo->audio_device = atoi(lscp_ltrim(pszToken)); + } + else if (strcasecmp(pszToken, "AUDIO_OUTPUT_CHANNELS") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + pChannelInfo->audio_channels = atoi(lscp_ltrim(pszToken)); + } + else if (strcasecmp(pszToken, "AUDIO_OUTPUT_ROUTING") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + 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) + 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) + pChannelInfo->instrument_status = atoi(lscp_ltrim(pszToken)); + } + else if (strcasecmp(pszToken, "MIDI_INPUT_DEVICE") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + pChannelInfo->midi_device = atoi(lscp_ltrim(pszToken)); + } + else if (strcasecmp(pszToken, "MIDI_INPUT_PORT") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + pChannelInfo->midi_port = atoi(lscp_ltrim(pszToken)); + } + else if (strcasecmp(pszToken, "MIDI_INPUT_CHANNEL") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + 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)); + if (pszToken) + pChannelInfo->volume = (float) atof(lscp_ltrim(pszToken)); + } + pszToken = lscp_strtok(NULL, pszSeps, &(pch)); } - pszToken = lscp_strtok(NULL, pszSeps, &(pch)); } + else pChannelInfo = NULL; + + // Unlock this section up. + lscp_mutex_unlock(pClient->mutex); return pChannelInfo; } @@ -949,10 +1079,16 @@ if (iSamplerChannel < 0) return iVoiceCount; + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + sprintf(szQuery, "GET CHANNEL VOICE_COUNT %d\r\n", iSamplerChannel); - if (lscp_client_query(pClient, szQuery) == LSCP_OK) + if (lscp_client_call(pClient, szQuery) == LSCP_OK) iVoiceCount = atoi(lscp_client_get_result(pClient)); + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); + return iVoiceCount; } @@ -961,6 +1097,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 ) @@ -971,15 +1110,75 @@ if (iSamplerChannel < 0) return iStreamCount; + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + sprintf(szQuery, "GET CHANNEL STREAM_COUNT %d\r\n", iSamplerChannel); - if (lscp_client_query(pClient, szQuery) == LSCP_OK) + if (lscp_client_call(pClient, szQuery) == LSCP_OK) iStreamCount = atoi(lscp_client_get_result(pClient)); + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); + return iStreamCount; } /** + * 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} * @@ -1005,7 +1204,15 @@ char *pch; int iStream; + // Retrieve a channel stream estimation. iStreamCount = lscp_get_channel_stream_count(pClient, iSamplerChannel); + if (pClient->iStreamCount < 0) + return NULL; + + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + // Check if we need to reallocate the stream usage array. if (pClient->iStreamCount != iStreamCount) { if (pClient->buffer_fill) free(pClient->buffer_fill); @@ -1016,31 +1223,33 @@ pClient->iStreamCount = iStreamCount; } - if (pClient->iStreamCount < 1) - return NULL; - - iStream = 0; - pBufferFill = pClient->buffer_fill; - // Get buffer fill usage... - sprintf(szQuery, "GET CHANNEL BUFFER_FILL %s %d\r\n", pszUsageType, iSamplerChannel); - if (lscp_client_query(pClient, szQuery) == LSCP_OK) { - pszResult = lscp_client_get_result(pClient); - pszToken = lscp_strtok(pszResult, pszSeps, &(pch)); - while (pszToken && iStream < pClient->iStreamCount) { - if (*pszToken) { - pBufferFill[iStream].stream_id = atol(pszToken); + pBufferFill = pClient->buffer_fill; + if (pBufferFill && iStreamCount > 0) { + iStream = 0; + pBufferFill = pClient->buffer_fill; + sprintf(szQuery, "GET CHANNEL BUFFER_FILL %s %d\r\n", pszUsageType, iSamplerChannel); + if (lscp_client_call(pClient, szQuery) == LSCP_OK) { + pszResult = lscp_client_get_result(pClient); + pszToken = lscp_strtok((char *) pszResult, pszSeps, &(pch)); + while (pszToken && iStream < pClient->iStreamCount) { + if (*pszToken) { + pBufferFill[iStream].stream_id = atol(pszToken); + pszToken = lscp_strtok(NULL, pszSeps, &(pch)); + if (pszToken == NULL) + break; + pBufferFill[iStream].stream_usage = atol(pszToken); + iStream++; + } pszToken = lscp_strtok(NULL, pszSeps, &(pch)); - if (pszToken == NULL) - break; - pBufferFill[iStream].stream_usage = atol(pszToken); - iStream++; } - pszToken = lscp_strtok(NULL, pszSeps, &(pch)); - } - } // Reset the usage, whatever it was before. - else while (iStream < pClient->iStreamCount) - pBufferFill[iStream++].stream_usage = 0; + } // Reset the usage, whatever it was before. + else while (iStream < pClient->iStreamCount) + pBufferFill[iStream++].stream_usage = 0; + } + + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); return pBufferFill; } @@ -1067,6 +1276,26 @@ /** + * Setting audio output device: + * SET CHANNEL AUDIO_OUTPUT_DEVICE + * + * @param pClient Pointer to client instance structure. + * @param iSamplerChannel Sampler channel number. + * @param iAudioDevice Audio output device number identifier. + */ +lscp_status_t lscp_set_channel_audio_device ( lscp_client_t *pClient, int iSamplerChannel, int iAudioDevice ) +{ + char szQuery[LSCP_BUFSIZ]; + + if (iSamplerChannel < 0 || iAudioDevice < 0) + return LSCP_FAILED; + + sprintf(szQuery, "SET CHANNEL AUDIO_OUTPUT_DEVICE %d %d\r\n", iSamplerChannel, iAudioDevice); + return lscp_client_query(pClient, szQuery); +} + + +/** * Setting audio output channel: * SET CHANNEL AUDIO_OUTPUT_CHANNEL * @@ -1084,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); } @@ -1112,6 +1341,26 @@ /** + * Setting MIDI input device: + * SET CHANNEL MIDI_INPUT_DEVICE + * + * @param pClient Pointer to client instance structure. + * @param iSamplerChannel Sampler channel number. + * @param iMidiDevice MIDI input device number identifier. + */ +lscp_status_t lscp_set_channel_midi_device ( lscp_client_t *pClient, int iSamplerChannel, int iMidiDevice ) +{ + char szQuery[LSCP_BUFSIZ]; + + if (iSamplerChannel < 0 || iMidiDevice < 0) + return LSCP_FAILED; + + sprintf(szQuery, "SET CHANNEL MIDI_INPUT_DEVICE %d %d\r\n", iSamplerChannel, iMidiDevice); + return lscp_client_query(pClient, szQuery); +} + + +/** * Setting MIDI input port: * SET CHANNEL MIDI_INPUT_PORT * @@ -1139,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. */ @@ -1151,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); } @@ -1204,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