--- liblscp/trunk/src/client.c 2008/12/07 13:50:08 1802 +++ liblscp/trunk/src/client.c 2019/12/22 13:01:23 3665 @@ -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,69 @@ *****************************************************************************/ +#include #include "common.h" +#include +#ifdef WIN32 +# include +#else +# include +#endif + // 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 +101,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 +163,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 +203,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 +232,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 +248,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 +279,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 +300,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 +323,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 +364,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 +425,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 +450,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 +647,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 +956,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 +981,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 +1012,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 +1204,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 +1272,7 @@ const char *pszCrlf = "\r\n"; char *pszToken; char *pch; + struct _locale_t locale; if (pClient == NULL) return NULL; @@ -1123,6 +1285,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 +1371,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 +1388,8 @@ } else pChannelInfo = NULL; + _restore_locale(&locale); + // Unlock this section up. lscp_mutex_unlock(pClient->mutex); @@ -1367,7 +1533,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 +1607,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 +1631,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 +1656,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 +1680,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 +1704,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 +1728,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 +1753,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 +1762,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 +1783,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 +1818,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 +1848,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 +1874,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); } @@ -1859,6 +2052,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 +2060,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,14 +2088,19 @@ 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 @@ -1912,7 +2115,7 @@ * negative value on error (e.g. if sampler doesn't support * this command). */ -int lscp_get_voices(lscp_client_t *pClient) +int lscp_get_voices ( lscp_client_t *pClient ) { int iVoices = -1; @@ -1931,6 +2134,7 @@ return iVoices; } + /** * Setting global voice limit setting: * @code @@ -1949,7 +2153,7 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_voices(lscp_client_t *pClient, int iMaxVoices) +lscp_status_t lscp_set_voices ( lscp_client_t *pClient, int iMaxVoices ) { char szQuery[LSCP_BUFSIZ]; @@ -1960,6 +2164,7 @@ return lscp_client_query(pClient, szQuery); } + /** * Get global disk streams limit setting: * @code @@ -1974,7 +2179,7 @@ * or a negative value on error (e.g. if sampler doesn't * support this command). */ -int lscp_get_streams(lscp_client_t *pClient) +int lscp_get_streams ( lscp_client_t *pClient ) { int iStreams = -1; @@ -1993,6 +2198,7 @@ return iStreams; } + /** * Setting global disk streams limit setting: * @code @@ -2011,7 +2217,7 @@ * * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ -lscp_status_t lscp_set_streams(lscp_client_t *pClient, int iMaxStreams) +lscp_status_t lscp_set_streams ( lscp_client_t *pClient, int iMaxStreams ) { char szQuery[LSCP_BUFSIZ]; @@ -2022,20 +2228,22 @@ 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]; @@ -2048,7 +2256,8 @@ // 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); @@ -2075,14 +2284,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); } @@ -2172,7 +2383,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]; @@ -2181,6 +2393,7 @@ const char *pszCrlf = "\r\n"; char *pszToken; char *pch; + struct _locale_t locale; if (pClient == NULL) return NULL; @@ -2190,6 +2403,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); @@ -2219,19 +2434,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 @@ -2245,17 +2463,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 @@ -2269,14 +2490,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); } @@ -2293,14 +2516,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); } @@ -2316,14 +2542,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); } @@ -2552,9 +2784,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; @@ -2568,9 +2804,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: @@ -2605,7 +2843,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]; @@ -2698,7 +2937,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); @@ -2718,7 +2958,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]; @@ -2727,6 +2968,7 @@ const char *pszCrlf = "\r\n"; char *pszToken; char *pch; + struct _locale_t locale; if (pClient == NULL) return NULL; @@ -2740,6 +2982,8 @@ // 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); @@ -2793,13 +3037,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); @@ -2832,6 +3078,7 @@ return lscp_client_query(pClient, szQuery); } + /** * Open an instrument editor application for the instrument * on the given sampler channel: