--- liblscp/trunk/src/client.c 2008/02/16 19:31:32 1697 +++ liblscp/trunk/src/client.c 2019/12/22 12:53:26 3664 @@ -2,7 +2,7 @@ // /**************************************************************************** liblscp - LinuxSampler Control Protocol API - Copyright (C) 2004-2008, rncbc aka Rui Nuno Capela. All rights reserved. + Copyright (C) 2004-2019, 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 @@ -20,18 +20,64 @@ *****************************************************************************/ +#include #include "common.h" +#include +#include // Default timeout value (in milliseconds). #define LSCP_TIMEOUT_MSECS 500 +// Whether to use getaddrinfo() instead +// of deprecated gethostbyname() +#if !defined(WIN32) +#define USE_GETADDRINFO 1 +#endif + + // Local prototypes. -static void _lscp_client_evt_proc (void *pvClient); +static void _lscp_client_evt_proc (void *pvClient); -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); +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); + + +//------------------------------------------------------------------------- +// General helper functions. + +struct _locale_t { + char numeric[32]; + char ctype[32]; +}; + +// we need to ensure a constant locale setting e.g. for parsing +// floating point numbers with atof(), as the floating point separator +// character varies by the invidual locale settings +static void _save_and_set_c_locale(struct _locale_t* locale) +{ + strncpy(locale->numeric, setlocale(LC_NUMERIC, NULL), 32); + strncpy(locale->ctype, setlocale(LC_CTYPE, NULL), 32); + setlocale(LC_NUMERIC, "C"); + setlocale(LC_CTYPE, "C"); +} + +// restore the original locale setting as nothing happened +static void _restore_locale(struct _locale_t* locale) +{ + setlocale(LC_NUMERIC, locale->numeric); + setlocale(LC_CTYPE, locale->ctype); +} + +// seems the standard atof() function doesnt care much about locale +// runtime modifications, so we use this workaround +static float _atof(const char* txt) { + float f; + sscanf(txt, "%f", &f); // yeah, you're a good boy sscanf() + return f; +} //------------------------------------------------------------------------- @@ -50,9 +96,10 @@ 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 @@ -111,11 +158,13 @@ } else { lscp_socket_perror("_lscp_client_evt_proc: recv"); pClient->evt.iState = 0; + pClient->iErrno = -errno; } } // Check if select has in error. else if (iSelect < 0) { lscp_socket_perror("_lscp_client_evt_proc: select"); pClient->evt.iState = 0; + pClient->iErrno = -errno; } // Finally, always signal the event. @@ -149,7 +198,8 @@ } #if defined(WIN32) - if (setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) + if (setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, + (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) lscp_socket_perror("lscp_client_evt_connect: setsockopt(SO_DONTLINGER)"); #endif @@ -177,7 +227,8 @@ // Subscribe to a single event. -static lscp_status_t _lscp_client_evt_request ( lscp_client_t *pClient, int iSubscribe, lscp_event_t 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]; @@ -192,7 +243,8 @@ return LSCP_FAILED; // Build the query string... - cchQuery = sprintf(szQuery, "%sSUBSCRIBE %s\n\n", (iSubscribe == 0 ? "UN" : ""), pszEvent); + 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"); @@ -222,8 +274,8 @@ /** Retrieve the current client library version string. */ const char* lscp_client_version (void) { return LSCP_VERSION; } -/** Retrieve the current client library build timestamp string. */ -const char* lscp_client_build (void) { return __DATE__ " " __TIME__; } +/** Retrieve the current client library build string. */ +const char* lscp_client_build (void) { return LSCP_BUILD; } //------------------------------------------------------------------------- @@ -243,13 +295,20 @@ * @returns The new client instance pointer if successfull, which shall be * used on all subsequent client calls, NULL otherwise. */ -lscp_client_t* lscp_client_create ( const char *pszHost, int iPort, lscp_client_proc_t pfnCallback, void *pvData ) +lscp_client_t* lscp_client_create ( const char *pszHost, int iPort, + lscp_client_proc_t pfnCallback, void *pvData ) { lscp_client_t *pClient; +#if defined(USE_GETADDRINFO) + char szPort[33]; + struct addrinfo hints; + struct addrinfo *result, *res; +#else struct hostent *pHost; - lscp_socket_t sock; struct sockaddr_in addr; int cAddr; +#endif /* !USE_GETADDRINFO */ + lscp_socket_t sock; #if defined(WIN32) int iSockOpt = (-1); #endif @@ -259,12 +318,34 @@ return NULL; } +#if defined(USE_GETADDRINFO) + + // Convert port number to string/name... + snprintf(szPort, sizeof(szPort), "%d", iPort); + + // Obtain address(es) matching host/port... + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + result = NULL; + + if (getaddrinfo(pszHost, szPort, &hints, &result)) { + lscp_socket_herror("lscp_client_create: getaddrinfo"); + return NULL; + } + +#else + + // Obtain host matching name... pHost = gethostbyname(pszHost); if (pHost == NULL) { lscp_socket_herror("lscp_client_create: gethostbyname"); return NULL; } +#endif /* !USE_GETADDRINFO */ + // Allocate client descriptor... pClient = (lscp_client_t *) malloc(sizeof(lscp_client_t)); @@ -278,11 +359,59 @@ pClient->pvData = pvData; #ifdef DEBUG - fprintf(stderr, "lscp_client_create: pClient=%p: pszHost=%s iPort=%d.\n", pClient, pszHost, iPort); + fprintf(stderr, + "lscp_client_create: pClient=%p: pszHost=%s iPort=%d.\n", + pClient, pszHost, iPort); #endif // Prepare the command connection socket... +#if defined(USE_GETADDRINFO) + + // getaddrinfo() returns a list of address structures; + // try each address until we successfully connect(2); + // if socket or connect fails, we close the socket and + // try the next address... + sock = INVALID_SOCKET; + + for (res = result; res; res = res->ai_next) { + sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sock == INVALID_SOCKET) + continue; + #if defined(WIN32) + if (setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, + (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) + lscp_socket_perror("lscp_client_create: cmd: setsockopt(SO_DONTLINGER)"); + #endif + #ifdef DEBUG + lscp_socket_getopts("lscp_client_create: cmd", sock); + #endif + if (connect(sock, res->ai_addr, res->ai_addrlen) != SOCKET_ERROR) + break; + closesocket(sock); + } + + if (sock == INVALID_SOCKET) { + lscp_socket_perror("lscp_client_create: cmd: socket"); + free(pClient); + return NULL; + } + + if (res == NULL) { + lscp_socket_perror("lscp_client_create: cmd: connect"); + free(pClient); + return NULL; + } + + // Initialize the command socket agent struct... + lscp_socket_agent_init(&(pClient->cmd), sock, + (struct sockaddr_in *) res->ai_addr, res->ai_addrlen); + + // No longer needed... + freeaddrinfo(result); + +#else + sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { lscp_socket_perror("lscp_client_create: cmd: socket"); @@ -291,7 +420,8 @@ } #if defined(WIN32) - if (setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) + if (setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, + (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) lscp_socket_perror("lscp_client_create: cmd: setsockopt(SO_DONTLINGER)"); #endif @@ -315,8 +445,14 @@ // Initialize the command socket agent struct... lscp_socket_agent_init(&(pClient->cmd), sock, &addr, cAddr); +#endif /* !USE_GETADDRINFO */ + #ifdef DEBUG - 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)); + 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 // Initialize the event service socket struct... @@ -506,6 +642,20 @@ return pClient->iTimeout; } +/** + * Check whether connection to server is lost. + * + * @param pClient Pointer to client instance structure. + * + * @returns @c true if client lost connection to server, @c false otherwise. + */ +bool lscp_client_connection_lost ( lscp_client_t *pClient ) +{ + int err = lscp_client_get_errno(pClient); + if (err >= 0) return false; + return err == -EPIPE || err == -ECONNRESET || err == -ECONNABORTED; +} + //------------------------------------------------------------------------- // Client common protocol functions. @@ -801,14 +951,16 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_load_instrument ( lscp_client_t *pClient, const char *pszFileName, int iInstrIndex, int iSamplerChannel ) +lscp_status_t lscp_load_instrument ( 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 '%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); } @@ -824,14 +976,16 @@ * * @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 ) +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); + sprintf(szQuery, "LOAD INSTRUMENT NON_MODAL '%s' %d %d\r\n", + pszFileName, iInstrIndex, iSamplerChannel); return lscp_client_query(pClient, szQuery); } @@ -853,7 +1007,8 @@ if (pszEngineName == NULL || iSamplerChannel < 0) return LSCP_FAILED; - sprintf(szQuery, "LOAD ENGINE %s %d\r\n", pszEngineName, iSamplerChannel); + sprintf(szQuery, "LOAD ENGINE %s %d\r\n", + pszEngineName, iSamplerChannel); return lscp_client_query(pClient, szQuery); } @@ -1044,7 +1199,8 @@ * @returns A pointer to a @ref lscp_engine_info_t structure, with all the * information of the given sampler engine, or NULL in case of failure. */ -lscp_engine_info_t *lscp_get_engine_info ( lscp_client_t *pClient, const char *pszEngineName ) +lscp_engine_info_t *lscp_get_engine_info ( lscp_client_t *pClient, + const char *pszEngineName ) { lscp_engine_info_t *pEngineInfo; char szQuery[LSCP_BUFSIZ]; @@ -1111,6 +1267,7 @@ const char *pszCrlf = "\r\n"; char *pszToken; char *pch; + struct _locale_t locale; if (pClient == NULL) return NULL; @@ -1123,6 +1280,8 @@ pChannelInfo = &(pClient->channel_info); lscp_channel_info_reset(pChannelInfo); + _save_and_set_c_locale(&locale); + sprintf(szQuery, "GET CHANNEL INFO %d\r\n", iSamplerChannel); if (lscp_client_call(pClient, szQuery, 1) == LSCP_OK) { pszResult = lscp_client_get_result(pClient); @@ -1207,7 +1366,7 @@ else if (strcasecmp(pszToken, "VOLUME") == 0) { pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); if (pszToken) - pChannelInfo->volume = (float) atof(lscp_ltrim(pszToken)); + pChannelInfo->volume = _atof(lscp_ltrim(pszToken)); } else if (strcasecmp(pszToken, "MUTE") == 0) { pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); @@ -1224,6 +1383,8 @@ } else pChannelInfo = NULL; + _restore_locale(&locale); + // Unlock this section up. lscp_mutex_unlock(pClient->mutex); @@ -1367,7 +1528,8 @@ * information of the current disk stream buffer fill usage, for the given * sampler channel, or NULL in case of failure. */ -lscp_buffer_fill_t *lscp_get_channel_buffer_fill ( lscp_client_t *pClient, lscp_usage_t usage_type, int iSamplerChannel ) +lscp_buffer_fill_t *lscp_get_channel_buffer_fill ( lscp_client_t *pClient, + lscp_usage_t usage_type, int iSamplerChannel ) { lscp_buffer_fill_t *pBufferFill; char szQuery[LSCP_BUFSIZ]; @@ -1440,14 +1602,16 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_channel_audio_type ( lscp_client_t *pClient, int iSamplerChannel, const char *pszAudioDriver ) +lscp_status_t lscp_set_channel_audio_type ( lscp_client_t *pClient, + int iSamplerChannel, const char *pszAudioDriver ) { char szQuery[LSCP_BUFSIZ]; if (iSamplerChannel < 0 || pszAudioDriver == NULL) return LSCP_FAILED; - sprintf(szQuery, "SET CHANNEL AUDIO_OUTPUT_TYPE %d %s\r\n", iSamplerChannel, pszAudioDriver); + sprintf(szQuery, "SET CHANNEL AUDIO_OUTPUT_TYPE %d %s\r\n", + iSamplerChannel, pszAudioDriver); return lscp_client_query(pClient, szQuery); } @@ -1462,14 +1626,16 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_channel_audio_device ( lscp_client_t *pClient, int iSamplerChannel, int iAudioDevice ) +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); + sprintf(szQuery, "SET CHANNEL AUDIO_OUTPUT_DEVICE %d %d\r\n", + iSamplerChannel, iAudioDevice); return lscp_client_query(pClient, szQuery); } @@ -1485,14 +1651,16 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_channel_audio_channel ( lscp_client_t *pClient, int iSamplerChannel, int iAudioOut, int iAudioIn ) +lscp_status_t lscp_set_channel_audio_channel ( lscp_client_t *pClient, + int iSamplerChannel, int iAudioOut, int iAudioIn ) { char szQuery[LSCP_BUFSIZ]; if (iSamplerChannel < 0 || iAudioOut < 0 || iAudioIn < 0) return LSCP_FAILED; - sprintf(szQuery, "SET CHANNEL AUDIO_OUTPUT_CHANNEL %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); } @@ -1507,14 +1675,16 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_channel_midi_type ( lscp_client_t *pClient, int iSamplerChannel, const char *pszMidiDriver ) +lscp_status_t lscp_set_channel_midi_type ( lscp_client_t *pClient, + int iSamplerChannel, const char *pszMidiDriver ) { char szQuery[LSCP_BUFSIZ]; if (iSamplerChannel < 0 || pszMidiDriver == NULL) return LSCP_FAILED; - sprintf(szQuery, "SET CHANNEL MIDI_INPUT_TYPE %d %s\r\n", iSamplerChannel, pszMidiDriver); + sprintf(szQuery, "SET CHANNEL MIDI_INPUT_TYPE %d %s\r\n", + iSamplerChannel, pszMidiDriver); return lscp_client_query(pClient, szQuery); } @@ -1529,14 +1699,16 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_channel_midi_device ( lscp_client_t *pClient, int iSamplerChannel, int iMidiDevice ) +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); + sprintf(szQuery, "SET CHANNEL MIDI_INPUT_DEVICE %d %d\r\n", + iSamplerChannel, iMidiDevice); return lscp_client_query(pClient, szQuery); } @@ -1551,14 +1723,16 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_channel_midi_port ( lscp_client_t *pClient, int iSamplerChannel, int iMidiPort ) +lscp_status_t lscp_set_channel_midi_port ( lscp_client_t *pClient, + int iSamplerChannel, int iMidiPort ) { char szQuery[LSCP_BUFSIZ]; if (iSamplerChannel < 0 || iMidiPort < 0) return LSCP_FAILED; - sprintf(szQuery, "SET CHANNEL MIDI_INPUT_PORT %d %d\r\n", iSamplerChannel, iMidiPort); + sprintf(szQuery, "SET CHANNEL MIDI_INPUT_PORT %d %d\r\n", + iSamplerChannel, iMidiPort); return lscp_client_query(pClient, szQuery); } @@ -1574,7 +1748,8 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_channel_midi_channel ( lscp_client_t *pClient, int iSamplerChannel, int iMidiChannel ) +lscp_status_t lscp_set_channel_midi_channel ( lscp_client_t *pClient, + int iSamplerChannel, int iMidiChannel ) { char szQuery[LSCP_BUFSIZ]; @@ -1582,9 +1757,11 @@ return LSCP_FAILED; if (iMidiChannel == LSCP_MIDI_CHANNEL_ALL) - sprintf(szQuery, "SET CHANNEL MIDI_INPUT_CHANNEL %d ALL\r\n", iSamplerChannel); + 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); + sprintf(szQuery, "SET CHANNEL MIDI_INPUT_CHANNEL %d %d\r\n", + iSamplerChannel, iMidiChannel); return lscp_client_query(pClient, szQuery); } @@ -1601,7 +1778,8 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_channel_midi_map ( lscp_client_t *pClient, int iSamplerChannel, int iMidiMap ) +lscp_status_t lscp_set_channel_midi_map ( lscp_client_t *pClient, + int iSamplerChannel, int iMidiMap ) { char szQuery[LSCP_BUFSIZ]; @@ -1635,14 +1813,20 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_channel_volume ( lscp_client_t *pClient, int iSamplerChannel, float fVolume ) +lscp_status_t lscp_set_channel_volume ( lscp_client_t *pClient, + int iSamplerChannel, float fVolume ) { char szQuery[LSCP_BUFSIZ]; + struct _locale_t locale; if (iSamplerChannel < 0 || fVolume < 0.0f) return LSCP_FAILED; - sprintf(szQuery, "SET CHANNEL VOLUME %d %g\r\n", iSamplerChannel, fVolume); + _save_and_set_c_locale(&locale); + sprintf(szQuery, "SET CHANNEL VOLUME %d %g\r\n", + iSamplerChannel, fVolume); + _restore_locale(&locale); + return lscp_client_query(pClient, szQuery); } @@ -1659,14 +1843,16 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_channel_mute ( lscp_client_t *pClient, int iSamplerChannel, int iMute ) +lscp_status_t lscp_set_channel_mute ( lscp_client_t *pClient, + int iSamplerChannel, int iMute ) { char szQuery[LSCP_BUFSIZ]; if (iSamplerChannel < 0 || iMute < 0 || iMute > 1) return LSCP_FAILED; - sprintf(szQuery, "SET CHANNEL MUTE %d %d\r\n", iSamplerChannel, iMute); + sprintf(szQuery, "SET CHANNEL MUTE %d %d\r\n", + iSamplerChannel, iMute); return lscp_client_query(pClient, szQuery); } @@ -1683,14 +1869,16 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_channel_solo ( lscp_client_t *pClient, int iSamplerChannel, int iSolo ) +lscp_status_t lscp_set_channel_solo ( lscp_client_t *pClient, + int iSamplerChannel, int iSolo ) { char szQuery[LSCP_BUFSIZ]; if (iSamplerChannel < 0 || iSolo < 0 || iSolo > 1) return LSCP_FAILED; - sprintf(szQuery, "SET CHANNEL SOLO %d %d\r\n", iSamplerChannel, iSolo); + sprintf(szQuery, "SET CHANNEL SOLO %d %d\r\n", + iSamplerChannel, iSolo); return lscp_client_query(pClient, szQuery); } @@ -1726,7 +1914,7 @@ */ lscp_status_t lscp_reset_sampler ( lscp_client_t *pClient ) { - // Do actual whole sampler reset... + // Do actual whole sampler reset... return lscp_client_query(pClient, "RESET\r\n"); } @@ -1859,6 +2047,7 @@ float lscp_get_volume ( lscp_client_t *pClient ) { float fVolume = 0.0f; + struct _locale_t locale; if (pClient == NULL) return 0.0f; @@ -1866,8 +2055,12 @@ // Lock this section up. lscp_mutex_lock(pClient->mutex); + _save_and_set_c_locale(&locale); + if (lscp_client_call(pClient, "GET VOLUME\r\n", 0) == LSCP_OK) - fVolume = (float) atof(lscp_client_get_result(pClient)); + fVolume = _atof(lscp_client_get_result(pClient)); + + _restore_locale(&locale); // Unlock this section down. lscp_mutex_unlock(pClient->mutex); @@ -1890,29 +2083,162 @@ lscp_status_t lscp_set_volume ( lscp_client_t *pClient, float fVolume ) { char szQuery[LSCP_BUFSIZ]; + struct _locale_t locale; if (fVolume < 0.0f) return LSCP_FAILED; + _save_and_set_c_locale(&locale); sprintf(szQuery, "SET VOLUME %g\r\n", fVolume); + _restore_locale(&locale); + + return lscp_client_query(pClient, szQuery); +} + + +/** + * Get global voice limit setting: + * @code + * GET VOICES + * @endcode + * This value reflects the maximum amount of voices a sampler engine + * processes simultaniously before voice stealing kicks in. + * + * @param pClient Pointer to client instance structure. + * + * @returns The current global maximum amount of voices limit or a + * negative value on error (e.g. if sampler doesn't support + * this command). + */ +int lscp_get_voices ( lscp_client_t *pClient ) +{ + int iVoices = -1; + + if (pClient == NULL) + return -1; + + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + if (lscp_client_call(pClient, "GET VOICES\r\n", 0) == LSCP_OK) + iVoices = atoi(lscp_client_get_result(pClient)); + + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); + + return iVoices; +} + + +/** + * Setting global voice limit setting: + * @code + * SET VOICES + * @endcode + * This value reflects the maximum amount of voices a sampler engine + * processes simultaniously before voice stealing kicks in. Note that + * this value will be passed to all sampler engine instances, that is + * the total amount of maximum voices on the running system is thus + * @param iMaxVoices multiplied with the current amount of sampler + * engine instances. + * + * @param pClient Pointer to client instance structure. + * @param iMaxVoices Global voice limit setting as integer value larger + * or equal to 1. + * + * @returns LSCP_OK on success, LSCP_FAILED otherwise. + */ +lscp_status_t lscp_set_voices ( lscp_client_t *pClient, int iMaxVoices ) +{ + char szQuery[LSCP_BUFSIZ]; + + if (iMaxVoices < 1) + return LSCP_FAILED; + + sprintf(szQuery, "SET VOICES %d\r\n", iMaxVoices); + return lscp_client_query(pClient, szQuery); +} + + +/** + * Get global disk streams limit setting: + * @code + * GET STREAMS + * @endcode + * This value reflects the maximum amount of disk streams a sampler + * engine processes simultaniously. + * + * @param pClient Pointer to client instance structure. + * + * @returns The current global maximum amount of disk streams limit + * or a negative value on error (e.g. if sampler doesn't + * support this command). + */ +int lscp_get_streams ( lscp_client_t *pClient ) +{ + int iStreams = -1; + + if (pClient == NULL) + return -1; + + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + if (lscp_client_call(pClient, "GET STREAMS\r\n", 0) == LSCP_OK) + iStreams = atoi(lscp_client_get_result(pClient)); + + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); + + return iStreams; +} + + +/** + * Setting global disk streams limit setting: + * @code + * SET STREAMS + * @endcode + * This value reflects the maximum amount of dist streams a sampler + * engine instance processes simultaniously. Note that this value will + * be passed to all sampler engine instances, that is the total amount + * of maximum disk streams on the running system is thus + * @param iMaxStreams multiplied with the current amount of sampler + * engine instances. + * + * @param pClient Pointer to client instance structure. + * @param iMaxStreams Global streams limit setting as positive integer + * value (larger or equal to 0). + * + * @returns LSCP_OK on success, LSCP_FAILED otherwise. + */ +lscp_status_t lscp_set_streams ( lscp_client_t *pClient, int iMaxStreams ) +{ + char szQuery[LSCP_BUFSIZ]; + + if (iMaxStreams < 0) + return LSCP_FAILED; + + sprintf(szQuery, "SET STREAMS %d\r\n", iMaxStreams); return lscp_client_query(pClient, szQuery); } /** * Add an effect send to a sampler channel: - * CREATE FX_SEND [] + * CREATE FX_SEND [] * * @param pClient Pointer to client instance structure. * @param iSamplerChannel Sampler channel number. * @param iMidiController MIDI controller used to alter the effect, * usually a number between 0 and 127. - * @param pszName Optional name for the effect send entity, + * @param pszFxName Optional name for the effect send entity, * does not have to be unique. * * @returns The new effect send number identifier, or -1 in case of failure. */ -int lscp_create_fxsend ( lscp_client_t *pClient, int iSamplerChannel, int iMidiController, const char *pszFxName ) +int lscp_create_fxsend ( lscp_client_t *pClient, + int iSamplerChannel, int iMidiController, const char *pszFxName ) { int iFxSend = -1; char szQuery[LSCP_BUFSIZ]; @@ -1925,8 +2251,9 @@ // Lock this section up. lscp_mutex_lock(pClient->mutex); - sprintf(szQuery, "CREATE FX_SEND %d %d", iSamplerChannel, iMidiController); - + sprintf(szQuery, "CREATE FX_SEND %d %d", + iSamplerChannel, iMidiController); + if (pszFxName) sprintf(szQuery + strlen(szQuery), " '%s'", pszFxName); @@ -1952,14 +2279,16 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_destroy_fxsend ( lscp_client_t *pClient, int iSamplerChannel, int iFxSend ) +lscp_status_t lscp_destroy_fxsend ( lscp_client_t *pClient, + int iSamplerChannel, int iFxSend ) { char szQuery[LSCP_BUFSIZ]; if (iSamplerChannel < 0 || iFxSend < 0) return LSCP_FAILED; - sprintf(szQuery, "DESTROY FX_SEND %d %d\r\n", iSamplerChannel, iFxSend); + sprintf(szQuery, "DESTROY FX_SEND %d %d\r\n", + iSamplerChannel, iFxSend); return lscp_client_query(pClient, szQuery); } @@ -2049,7 +2378,8 @@ * @returns A pointer to a @ref lscp_fxsend_info_t structure, with the * information of the given FX send, or NULL in case of failure. */ -lscp_fxsend_info_t *lscp_get_fxsend_info ( lscp_client_t *pClient, int iSamplerChannel, int iFxSend ) +lscp_fxsend_info_t *lscp_get_fxsend_info ( lscp_client_t *pClient, + int iSamplerChannel, int iFxSend ) { lscp_fxsend_info_t *pFxSendInfo; char szQuery[LSCP_BUFSIZ]; @@ -2058,6 +2388,7 @@ const char *pszCrlf = "\r\n"; char *pszToken; char *pch; + struct _locale_t locale; if (pClient == NULL) return NULL; @@ -2067,6 +2398,8 @@ // Lock this section up. lscp_mutex_lock(pClient->mutex); + _save_and_set_c_locale(&locale); + pFxSendInfo = &(pClient->fxsend_info); lscp_fxsend_info_reset(pFxSendInfo); @@ -2096,19 +2429,22 @@ else if (strcasecmp(pszToken, "LEVEL") == 0) { pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); if (pszToken) - pFxSendInfo->level = (float) atof(lscp_ltrim(pszToken)); + pFxSendInfo->level = _atof(lscp_ltrim(pszToken)); } pszToken = lscp_strtok(NULL, pszSeps, &(pch)); } } else pFxSendInfo = NULL; + _restore_locale(&locale); + // Unlock this section up. lscp_mutex_unlock(pClient->mutex); return pFxSendInfo; } + /** * Alter effect send's name: * @code @@ -2122,17 +2458,20 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_fxsend_name ( lscp_client_t *pClient, int iSamplerChannel, int iFxSend, const char *pszFxName ) +lscp_status_t lscp_set_fxsend_name ( lscp_client_t *pClient, + int iSamplerChannel, int iFxSend, const char *pszFxName ) { char szQuery[LSCP_BUFSIZ]; if (!pClient || iSamplerChannel < 0 || iFxSend < 0 || !pszFxName) return LSCP_FAILED; - snprintf(szQuery, LSCP_BUFSIZ, "SET FX_SEND NAME %d %d '%s'\r\n", iSamplerChannel, iFxSend, pszFxName); + snprintf(szQuery, LSCP_BUFSIZ, "SET FX_SEND NAME %d %d '%s'\r\n", + iSamplerChannel, iFxSend, pszFxName); return lscp_client_query(pClient, szQuery); } + /** * Alter effect send's audio routing: * SET FX_SEND AUDIO_OUTPUT_CHANNEL @@ -2146,14 +2485,16 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_fxsend_audio_channel ( lscp_client_t *pClient, int iSamplerChannel, int iFxSend, int iAudioSrc, int iAudioDst ) +lscp_status_t lscp_set_fxsend_audio_channel ( lscp_client_t *pClient, + int iSamplerChannel, int iFxSend, int iAudioSrc, int iAudioDst ) { char szQuery[LSCP_BUFSIZ]; if (iSamplerChannel < 0 || iFxSend < 0 || iAudioSrc < 0 || iAudioDst < 0) return LSCP_FAILED; - sprintf(szQuery, "SET FX_SEND AUDIO_OUTPUT_CHANNEL %d %d %d %d\r\n", iSamplerChannel, iFxSend, iAudioSrc, iAudioDst); + sprintf(szQuery, "SET FX_SEND AUDIO_OUTPUT_CHANNEL %d %d %d %d\r\n", + iSamplerChannel, iFxSend, iAudioSrc, iAudioDst); return lscp_client_query(pClient, szQuery); } @@ -2170,14 +2511,17 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_fxsend_midi_controller ( lscp_client_t *pClient, int iSamplerChannel, int iFxSend, int iMidiController ) +lscp_status_t lscp_set_fxsend_midi_controller ( lscp_client_t *pClient, + int iSamplerChannel, int iFxSend, int iMidiController ) { char szQuery[LSCP_BUFSIZ]; - if (iSamplerChannel < 0 || iFxSend < 0 || iMidiController < 0 || iMidiController > 127) + if (iSamplerChannel < 0 || iFxSend < 0 || + iMidiController < 0 || iMidiController > 127) return LSCP_FAILED; - sprintf(szQuery, "SET FX_SEND MIDI_CONTROLLER %d %d %d\r\n", iSamplerChannel, iFxSend, iMidiController); + sprintf(szQuery, "SET FX_SEND MIDI_CONTROLLER %d %d %d\r\n", + iSamplerChannel, iFxSend, iMidiController); return lscp_client_query(pClient, szQuery); } @@ -2193,14 +2537,20 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_fxsend_level ( lscp_client_t *pClient, int iSamplerChannel, int iFxSend, float fLevel ) +lscp_status_t lscp_set_fxsend_level ( lscp_client_t *pClient, + int iSamplerChannel, int iFxSend, float fLevel ) { char szQuery[LSCP_BUFSIZ]; + struct _locale_t locale; if (iSamplerChannel < 0 || iFxSend < 0 || fLevel < 0.0f) return LSCP_FAILED; - sprintf(szQuery, "SET FX_SEND LEVEL %d %d %f\r\n", iSamplerChannel, iFxSend, fLevel); + _save_and_set_c_locale(&locale); + sprintf(szQuery, "SET FX_SEND LEVEL %d %d %f\r\n", + iSamplerChannel, iFxSend, fLevel); + _restore_locale(&locale); + return lscp_client_query(pClient, szQuery); } @@ -2227,7 +2577,7 @@ lscp_mutex_lock(pClient->mutex); strcpy(szQuery, "ADD MIDI_INSTRUMENT_MAP"); - + if (pszMapName) sprintf(szQuery + strlen(szQuery), " '%s'", pszMapName); @@ -2353,7 +2703,7 @@ // Lock this section up. lscp_mutex_lock(pClient->mutex); - + if (pClient->midi_map_name) { free(pClient->midi_map_name); pClient->midi_map_name = NULL; @@ -2417,7 +2767,7 @@ * @param pszFileName Instrument file name. * @param iInstrIndex Instrument index number. * @param fVolume Reflects the master volume of the instrument as - * a positive floating point number, where a value + * a positive floating point number, where a value * less than 1.0 for attenuation, and greater than * 1.0 for amplification. * @param load_mode Instrument load life-time strategy, either @@ -2429,9 +2779,13 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_map_midi_instrument ( lscp_client_t *pClient, lscp_midi_instrument_t *pMidiInstr, const char *pszEngineName, const char *pszFileName, int iInstrIndex, float fVolume, lscp_load_mode_t load_mode, const char *pszName ) +lscp_status_t lscp_map_midi_instrument ( lscp_client_t *pClient, + lscp_midi_instrument_t *pMidiInstr, const char *pszEngineName, + const char *pszFileName, int iInstrIndex, float fVolume, + lscp_load_mode_t load_mode, const char *pszName ) { char szQuery[LSCP_BUFSIZ]; + struct _locale_t locale; if (pMidiInstr->map < 0) return LSCP_FAILED; @@ -2445,9 +2799,11 @@ if (fVolume < 0.0f) fVolume = 1.0f; + _save_and_set_c_locale(&locale); sprintf(szQuery, "MAP MIDI_INSTRUMENT %d %d %d %s '%s' %d %g", pMidiInstr->map, pMidiInstr->bank, pMidiInstr->prog, pszEngineName, pszFileName, iInstrIndex, fVolume); + _restore_locale(&locale); switch (load_mode) { case LSCP_LOAD_PERSISTENT: @@ -2482,7 +2838,8 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_unmap_midi_instrument ( lscp_client_t *pClient, lscp_midi_instrument_t *pMidiInstr ) +lscp_status_t lscp_unmap_midi_instrument ( lscp_client_t *pClient, + lscp_midi_instrument_t *pMidiInstr ) { char szQuery[LSCP_BUFSIZ]; @@ -2575,7 +2932,8 @@ strcat(szQuery, "\r\n"); if (lscp_client_call(pClient, szQuery, 0) == LSCP_OK) - pClient->midi_instruments = lscp_midi_instruments_create(lscp_client_get_result(pClient)); + pClient->midi_instruments = lscp_midi_instruments_create( + lscp_client_get_result(pClient)); // Unlock this section down. lscp_mutex_unlock(pClient->mutex); @@ -2595,7 +2953,8 @@ * with all the information of the given MIDI instrument map entry, * or NULL in case of failure. */ -lscp_midi_instrument_info_t *lscp_get_midi_instrument_info ( lscp_client_t *pClient, lscp_midi_instrument_t *pMidiInstr ) +lscp_midi_instrument_info_t *lscp_get_midi_instrument_info ( lscp_client_t *pClient, + lscp_midi_instrument_t *pMidiInstr ) { lscp_midi_instrument_info_t *pInstrInfo; char szQuery[LSCP_BUFSIZ]; @@ -2604,6 +2963,7 @@ const char *pszCrlf = "\r\n"; char *pszToken; char *pch; + struct _locale_t locale; if (pClient == NULL) return NULL; @@ -2616,7 +2976,9 @@ // Lock this section up. lscp_mutex_lock(pClient->mutex); - + + _save_and_set_c_locale(&locale); + pInstrInfo = &(pClient->midi_instrument_info); lscp_midi_instrument_info_reset(pInstrInfo); @@ -2670,13 +3032,15 @@ else if (strcasecmp(pszToken, "VOLUME") == 0) { pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); if (pszToken) - pInstrInfo->volume = (float) atof(lscp_ltrim(pszToken)); + pInstrInfo->volume = _atof(lscp_ltrim(pszToken)); } pszToken = lscp_strtok(NULL, pszSeps, &(pch)); } } else pInstrInfo = NULL; + _restore_locale(&locale); + // Unlock this section down. lscp_mutex_unlock(pClient->mutex); @@ -2709,6 +3073,7 @@ return lscp_client_query(pClient, szQuery); } + /** * Open an instrument editor application for the instrument * on the given sampler channel: