--- liblscp/trunk/src/client.c 2004/09/27 14:40:08 253 +++ liblscp/trunk/src/client.c 2006/11/28 15:31:20 948 @@ -2,7 +2,7 @@ // /**************************************************************************** liblscp - LinuxSampler Control Protocol API - Copyright (C) 2004, rncbc aka Rui Nuno Capela. All rights reserved. + Copyright (C) 2004-2006, 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 @@ -14,9 +14,9 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ @@ -40,13 +40,13 @@ static void _lscp_client_evt_proc ( void *pvClient ) { lscp_client_t *pClient = (lscp_client_t *) pvClient; - + 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"; @@ -69,7 +69,7 @@ // Use the timeout (x10) select feature ... iTimeout = 10 * pClient->iTimeout; - if (iTimeout > 1000) { + if (iTimeout >= 1000) { tv.tv_sec = iTimeout / 1000; iTimeout -= tv.tv_sec * 1000; } @@ -111,10 +111,10 @@ } } // Check if select has in error. else if (iSelect < 0) { - lscp_socket_perror("_lscp_client_call: select"); + lscp_socket_perror("_lscp_client_evt_proc: select"); pClient->evt.iState = 0; } - + // Finally, always signal the event. lscp_cond_signal(pClient->cond); } @@ -164,10 +164,10 @@ 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); } @@ -198,7 +198,7 @@ // Wait on response. lscp_cond_wait(pClient->cond, pClient->mutex); - + // Update as naively as we can... if (iSubscribe) pClient->events |= event; @@ -327,6 +327,7 @@ pClient->midi_devices = NULL; pClient->engines = NULL; pClient->channels = NULL; + pClient->midi_instruments = 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)); @@ -337,8 +338,10 @@ 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_server_info_init(&(pClient->server_info)); lscp_engine_info_init(&(pClient->engine_info)); lscp_channel_info_init(&(pClient->channel_info)); + lscp_midi_instrument_info_init(&(pClient->midi_instrument_info)); // Initialize error stuff. pClient->pszResult = NULL; pClient->iErrno = -1; @@ -347,6 +350,7 @@ pClient->iStreamCount = 0; // Default timeout value. pClient->iTimeout = LSCP_TIMEOUT_MSECS; + pClient->iTimeoutCount = 0; // Initialize the transaction mutex. lscp_mutex_init(pClient->mutex); @@ -396,10 +400,12 @@ // Lock this section up. lscp_mutex_lock(pClient->mutex); - + // Free up all cached members. + lscp_midi_instrument_info_free(&(pClient->midi_instrument_info)); lscp_channel_info_free(&(pClient->channel_info)); lscp_engine_info_free(&(pClient->engine_info)); + lscp_server_info_free(&(pClient->server_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)); @@ -417,10 +423,15 @@ lscp_isplit_destroy(pClient->midi_devices); lscp_szsplit_destroy(pClient->engines); lscp_isplit_destroy(pClient->channels); + lscp_midi_instruments_destroy(pClient->midi_instruments); // Make them null. pClient->audio_drivers = NULL; pClient->midi_drivers = NULL; + pClient->audio_devices = NULL; + pClient->midi_devices = NULL; pClient->engines = NULL; + pClient->channels = NULL; + pClient->midi_instruments = NULL; // Free result error stuff. lscp_client_set_result(pClient, NULL, 0); // Free stream usage stuff. @@ -500,16 +511,16 @@ lscp_status_t lscp_client_query ( lscp_client_t *pClient, const char *pszQuery ) { lscp_status_t ret; - + // Lock this section up. lscp_mutex_lock(pClient->mutex); // Just make the now guarded call. - ret = lscp_client_call(pClient, pszQuery); - + ret = lscp_client_call(pClient, pszQuery, 0); + // Unlock this section down. lscp_mutex_unlock(pClient->mutex); - + return ret; } @@ -553,7 +564,7 @@ /** * Register frontend for receiving event messages: - * SUBSCRIBE CHANNELS | VOICE_COUNT | STREAM_COUNT | BUFFER_FILL + * SUBSCRIBE CHANNEL_COUNT | VOICE_COUNT | STREAM_COUNT | BUFFER_FILL * | CHANNEL_INFO | MISCELLANEOUS * * @param pClient Pointer to client instance structure. @@ -574,10 +585,10 @@ // 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_CHANNEL_COUNT)) + ret = _lscp_client_evt_request(pClient, 1, LSCP_EVENT_CHANNEL_COUNT); 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)) @@ -598,7 +609,7 @@ /** * Deregister frontend from receiving UDP event messages anymore: - * SUBSCRIBE CHANNELS | VOICE_COUNT | STREAM_COUNT | BUFFER_FILL + * SUBSCRIBE CHANNEL_COUNT | VOICE_COUNT | STREAM_COUNT | BUFFER_FILL * | CHANNEL_INFO | MISCELLANEOUS * * @param pClient Pointer to client instance structure. @@ -617,8 +628,8 @@ 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_CHANNEL_COUNT)) + ret = _lscp_client_evt_request(pClient, 0, LSCP_EVENT_CHANNEL_COUNT); 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)) @@ -744,7 +755,7 @@ // Lock this section up. lscp_mutex_lock(pClient->mutex); - if (lscp_client_call(pClient, "GET CHANNELS\r\n") == LSCP_OK) + if (lscp_client_call(pClient, "GET CHANNELS\r\n", 0) == LSCP_OK) iChannels = atoi(lscp_client_get_result(pClient)); // Unlock this section doen. @@ -769,7 +780,7 @@ if (pClient == NULL) return NULL; - + // Lock this section up. lscp_mutex_lock(pClient->mutex); @@ -778,7 +789,7 @@ pClient->channels = NULL; } - if (lscp_client_call(pClient, "LIST CHANNELS\r\n") == LSCP_OK) + if (lscp_client_call(pClient, "LIST CHANNELS\r\n", 0) == LSCP_OK) pClient->channels = lscp_isplit_create(lscp_client_get_result(pClient), pszSeps); // Unlock this section down. @@ -804,9 +815,9 @@ // Lock this section up. lscp_mutex_lock(pClient->mutex); - if (lscp_client_call(pClient, "ADD CHANNEL\r\n") == LSCP_OK) + if (lscp_client_call(pClient, "ADD CHANNEL\r\n", 0) == LSCP_OK) iSamplerChannel = atoi(lscp_client_get_result(pClient)); - + // Unlock this section down. lscp_mutex_unlock(pClient->mutex); @@ -836,15 +847,41 @@ /** - * Getting all available engines: + * Getting all available engines count: * GET AVAILABLE_ENGINES * * @param pClient Pointer to client instance structure. * + * @returns The current total number of sampler engines on success, + * -1 otherwise. + */ +int lscp_get_available_engines ( lscp_client_t *pClient ) +{ + int iAvailableEngines = -1; + + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + if (lscp_client_call(pClient, "GET AVAILABLE_ENGINES\r\n", 0) == 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 = ","; @@ -856,7 +893,7 @@ pClient->engines = NULL; } - if (lscp_client_call(pClient, "GET AVAILABLE_ENGINES\r\n") == LSCP_OK) + if (lscp_client_call(pClient, "LIST AVAILABLE_ENGINES\r\n", 0) == LSCP_OK) pClient->engines = lscp_szsplit_create(lscp_client_get_result(pClient), pszSeps); // Unlock this section down. @@ -896,7 +933,7 @@ lscp_engine_info_reset(pEngineInfo); sprintf(szQuery, "GET ENGINE INFO %s\r\n", pszEngineName); - if (lscp_client_call(pClient, szQuery) == LSCP_OK) { + if (lscp_client_call(pClient, szQuery, 1) == LSCP_OK) { pszResult = lscp_client_get_result(pClient); pszToken = lscp_strtok((char *) pszResult, pszSeps, &(pch)); while (pszToken) { @@ -914,7 +951,7 @@ } } else pEngineInfo = NULL; - + // Unlock this section down. lscp_mutex_unlock(pClient->mutex); @@ -947,12 +984,12 @@ // 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_call(pClient, szQuery) == LSCP_OK) { + if (lscp_client_call(pClient, szQuery, 1) == LSCP_OK) { pszResult = lscp_client_get_result(pClient); pszToken = lscp_strtok((char *) pszResult, pszSeps, &(pch)); while (pszToken) { @@ -989,6 +1026,11 @@ 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) @@ -1006,19 +1048,34 @@ } else if (strcasecmp(pszToken, "MIDI_INPUT_CHANNEL") == 0) { pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); - if (pszToken) - pChannelInfo->midi_channel = atoi(lscp_ltrim(pszToken)); + if (pszToken) { + pszToken = lscp_ltrim(pszToken); + if (strcasecmp(pszToken, "ALL") == 0) + pChannelInfo->midi_channel = LSCP_MIDI_CHANNEL_ALL; + else + pChannelInfo->midi_channel = atoi(pszToken); + } } else if (strcasecmp(pszToken, "VOLUME") == 0) { pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); if (pszToken) pChannelInfo->volume = (float) atof(lscp_ltrim(pszToken)); } + else if (strcasecmp(pszToken, "MUTE") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + pChannelInfo->mute = (strcasecmp(lscp_unquote(&pszToken, 0), "TRUE") == 0); + } + else if (strcasecmp(pszToken, "SOLO") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + pChannelInfo->solo = (strcasecmp(lscp_unquote(&pszToken, 0), "TRUE") == 0); + } pszToken = lscp_strtok(NULL, pszSeps, &(pch)); } } else pChannelInfo = NULL; - + // Unlock this section up. lscp_mutex_unlock(pClient->mutex); @@ -1047,7 +1104,7 @@ lscp_mutex_lock(pClient->mutex); sprintf(szQuery, "GET CHANNEL VOICE_COUNT %d\r\n", iSamplerChannel); - if (lscp_client_call(pClient, szQuery) == LSCP_OK) + if (lscp_client_call(pClient, szQuery, 0) == LSCP_OK) iVoiceCount = atoi(lscp_client_get_result(pClient)); // Unlock this section down. @@ -1078,7 +1135,7 @@ lscp_mutex_lock(pClient->mutex); sprintf(szQuery, "GET CHANNEL STREAM_COUNT %d\r\n", iSamplerChannel); - if (lscp_client_call(pClient, szQuery) == LSCP_OK) + if (lscp_client_call(pClient, szQuery, 0) == LSCP_OK) iStreamCount = atoi(lscp_client_get_result(pClient)); // Unlock this section down. @@ -1116,7 +1173,7 @@ iStream = 0; sprintf(szQuery, "GET CHANNEL BUFFER_FILL PERCENTAGE %d\r\n", iSamplerChannel); - if (lscp_client_call(pClient, szQuery) == LSCP_OK) { + if (lscp_client_call(pClient, szQuery, 0) == LSCP_OK) { pszResult = lscp_client_get_result(pClient); pszToken = lscp_strtok((char *) pszResult, pszSeps, &(pch)); while (pszToken) { @@ -1193,7 +1250,7 @@ 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) { + if (lscp_client_call(pClient, szQuery, 0) == LSCP_OK) { pszResult = lscp_client_get_result(pClient); pszToken = lscp_strtok((char *) pszResult, pszSeps, &(pch)); while (pszToken && iStream < pClient->iStreamCount) { @@ -1211,7 +1268,7 @@ else while (iStream < pClient->iStreamCount) pBufferFill[iStream++].stream_usage = 0; } - + // Unlock this section down. lscp_mutex_unlock(pClient->mutex); @@ -1226,6 +1283,8 @@ * @param pClient Pointer to client instance structure. * @param iSamplerChannel Sampler channel number. * @param pszAudioDriver Audio output driver type (e.g. "ALSA" or "JACK"). + * + * @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 ) { @@ -1246,6 +1305,8 @@ * @param pClient Pointer to client instance structure. * @param iSamplerChannel Sampler channel number. * @param iAudioDevice Audio output device number identifier. + * + * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ lscp_status_t lscp_set_channel_audio_device ( lscp_client_t *pClient, int iSamplerChannel, int iAudioDevice ) { @@ -1311,6 +1372,8 @@ * @param pClient Pointer to client instance structure. * @param iSamplerChannel Sampler channel number. * @param iMidiDevice MIDI input device number identifier. + * + * @returns LSCP_OK on success, LSCP_FAILED otherwise. */ lscp_status_t lscp_set_channel_midi_device ( lscp_client_t *pClient, int iSamplerChannel, int iMidiDevice ) { @@ -1352,8 +1415,8 @@ * * @param pClient Pointer to client instance structure. * @param iSamplerChannel Sampler channel number. - * @param iMidiChannel MIDI channel number to listen (0-15) or - * LSCP_MIDI_CHANNEL_ALL (-1) 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. */ @@ -1361,13 +1424,13 @@ { char szQuery[LSCP_BUFSIZ]; - if (iSamplerChannel < 0 || iMidiChannel < LSCP_MIDI_CHANNEL_ALL || iMidiChannel > 15) + 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); } @@ -1397,6 +1460,54 @@ /** + * Muting a sampler channel: + * SET CHANNEL MUTE + * + * @param pClient Pointer to client instance structure. + * @param iSamplerChannel Sampler channel number. + * @param iMute Sampler channel mute state as a boolean value, + * either 1 (one) to mute the channel or 0 (zero) + * to unmute the channel. + * + * @returns LSCP_OK on success, LSCP_FAILED otherwise. + */ +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); + return lscp_client_query(pClient, szQuery); +} + + +/** + * Soloing a sampler channel: + * SET CHANNEL SOLO + * + * @param pClient Pointer to client instance structure. + * @param iSamplerChannel Sampler channel number. + * @param iSolo Sampler channel solo state as a boolean value, + * either 1 (one) to solo the channel or 0 (zero) + * to unsolo the channel. + * + * @returns LSCP_OK on success, LSCP_FAILED otherwise. + */ +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); + return lscp_client_query(pClient, szQuery); +} + + +/** * Resetting a sampler channel: * RESET CHANNEL * @@ -1431,4 +1542,371 @@ } +/** + * Getting information about the server. + * GET SERVER INFO + * + * @param pClient Pointer to client instance structure. + * + * @returns A pointer to a @ref lscp_server_info_t structure, with all the + * information of the current connected server, or NULL in case of failure. + */ +lscp_server_info_t *lscp_get_server_info ( lscp_client_t *pClient ) +{ + lscp_server_info_t *pServerInfo; + const char *pszResult; + const char *pszSeps = ":"; + const char *pszCrlf = "\r\n"; + char *pszToken; + char *pch; + + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + pServerInfo = &(pClient->server_info); + lscp_server_info_reset(pServerInfo); + + if (lscp_client_call(pClient, "GET SERVER INFO\r\n", 1) == 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(&(pServerInfo->description), &pszToken); + } + else if (strcasecmp(pszToken, "VERSION") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + lscp_unquote_dup(&(pServerInfo->version), &pszToken); + } + pszToken = lscp_strtok(NULL, pszSeps, &(pch)); + } + } + else pServerInfo = NULL; + + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); + + return pServerInfo; +} + + +/** + * Current total number of active voices: + * GET TOTAL_VOICE_COUNT + * + * @param pClient Pointer to client instance structure. + * + * @returns The total number of voices currently active, + * -1 in case of failure. + */ +int lscp_get_total_voice_count ( lscp_client_t *pClient ) +{ + int iVoiceCount = -1; + + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + if (lscp_client_call(pClient, "GET TOTAL_VOICE_COUNT\r\n", 0) == LSCP_OK) + iVoiceCount = atoi(lscp_client_get_result(pClient)); + + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); + + return iVoiceCount; +} + + +/** + * Maximum amount of active voices: + * GET TOTAL_VOICE_COUNT_MAX + * + * @param pClient Pointer to client instance structure. + * + * @returns The maximum amount of voices currently active, + * -1 in case of failure. + */ +int lscp_get_total_voice_count_max ( lscp_client_t *pClient ) +{ + int iVoiceCount = -1; + + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + if (lscp_client_call(pClient, "GET TOTAL_VOICE_COUNT_MAX\r\n", 0) == LSCP_OK) + iVoiceCount = atoi(lscp_client_get_result(pClient)); + + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); + + return iVoiceCount; +} + + +/** + * Create or replace a MIDI instrumnet map entry: + * MAP MIDI_INSTRUMENT + * [] + * + * @param pClient Pointer to client instance structure. + * @param pMidiInstr MIDI instrument bank and program parameter key. + * @param pszEngineName Engine name. + * @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 + * less than 1.0 for attenuation, and greater than + * 1.0 for amplification. + * @param load_mode Instrument load life-time strategy, either + * @ref LSCP_LOAD_DEFAULT, or + * @ref LSCP_LOAD_ON_DEMAND, or + * @ref LSCP_LOAD_ON_DEMAND_HOLD, or + * @ref LSCP_LOAD_PERSISTENT. + * @param pszName Instrument custom name for the map entry. + * + * @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 ) +{ + char szQuery[LSCP_BUFSIZ]; + + if (pMidiInstr->bank_msb < 0 || pMidiInstr->bank_msb > 127) + return LSCP_FAILED; + if (pMidiInstr->bank_lsb < 0 || pMidiInstr->bank_lsb > 127) + return LSCP_FAILED; + if (pMidiInstr->program < 0 || pMidiInstr->program > 127) + return LSCP_FAILED; + if (pszEngineName == NULL || pszFileName == NULL) + return LSCP_FAILED; + + if (fVolume < 0.0f) + fVolume = 1.0f; + + sprintf(szQuery, "MAP MIDI_INSTRUMENT %d %d %d %s '%s' %d %g", + pMidiInstr->bank_msb, pMidiInstr->bank_lsb, pMidiInstr->program, + pszEngineName, pszFileName, iInstrIndex, fVolume); + + switch (load_mode) { + case LSCP_LOAD_PERSISTENT: + strcat(szQuery, " PERSISTENT"); + break; + case LSCP_LOAD_ON_DEMAND_HOLD: + strcat(szQuery, " ON_DEMAND_HOLD"); + break; + case LSCP_LOAD_ON_DEMAND: + strcat(szQuery, " ON_DEMAND_HOLD"); + break; + case LSCP_LOAD_DEFAULT: + default: + break; + } + + if (pszName) + sprintf(szQuery + strlen(szQuery), " '%s'", pszName); + + strcat(szQuery, "\r\n"); + + return lscp_client_query(pClient, szQuery); +} + + +/** + * Remove an entry from the MIDI instrument map: + * UNMAP MIDI_INSTRUMENT + * + * @param pClient Pointer to client instance structure. + * @param pMidiInstr MIDI instrument bank and program parameter key. + * + * @returns LSCP_OK on success, LSCP_FAILED otherwise. + */ +lscp_status_t lscp_unmap_midi_instrument ( lscp_client_t *pClient, lscp_midi_instrument_t *pMidiInstr ) +{ + char szQuery[LSCP_BUFSIZ]; + + if (pMidiInstr->bank_msb < 0 || pMidiInstr->bank_msb > 127) + return LSCP_FAILED; + if (pMidiInstr->bank_lsb < 0 || pMidiInstr->bank_lsb > 127) + return LSCP_FAILED; + if (pMidiInstr->program < 0 || pMidiInstr->program > 127) + return LSCP_FAILED; + + sprintf(szQuery, "UNMAP MIDI_INSTRUMENT %d %d %d\r\n", + pMidiInstr->bank_msb, pMidiInstr->bank_lsb, pMidiInstr->program); + + return lscp_client_query(pClient, szQuery); +} + + +/** + * Get the total count of MIDI instrument map entries: + * GET MIDI_INSTRUMENTS + * + * @param pClient Pointer to client instance structure. + * + * @returns The current total number of MIDI instrument map entries + * on success, -1 otherwise. + */ +int lscp_get_midi_instruments ( lscp_client_t *pClient ) +{ + int iInstruments = -1; + + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + if (lscp_client_call(pClient, "GET MIDI_INSTRUMENTS\r\n", 0) == LSCP_OK) + iInstruments = atoi(lscp_client_get_result(pClient)); + + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); + + return iInstruments; +} + + +/** + * Getting indeces of all MIDI instrument map entries: + * LIST MIDI_INSTRUMENTS + * + * @param pClient Pointer to client instance structure. + * + * @returns An array of @ref lscp_midi_instrument_t, terminated with the + * {-1,-1,-1} triplet, NULL otherwise. + */ +lscp_midi_instrument_t *lscp_list_midi_instruments ( lscp_client_t *pClient ) +{ + if (pClient == NULL) + return NULL; + + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + if (pClient->midi_instruments) { + lscp_midi_instruments_destroy(pClient->midi_instruments); + pClient->midi_instruments = NULL; + } + + if (lscp_client_call(pClient, "LIST MIDI_INSTRUMENTS\r\n", 0) == LSCP_OK) + pClient->midi_instruments = lscp_midi_instruments_create(lscp_client_get_result(pClient)); + + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); + + return pClient->midi_instruments; +} + + +/** + * Getting information about a MIDI instrument map entry: + * GET MIDI_INSTRUMENT INFO + * + * @param pClient Pointer to client instance structure. + * @param pMidiInstr MIDI instrument bank and program parameter key. + * + * @returns A pointer to a @ref lscp_midi_instrument_info_t structure, + * 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 *pInstrInfo; + char szQuery[LSCP_BUFSIZ]; + const char *pszResult; + const char *pszSeps = ":"; + const char *pszCrlf = "\r\n"; + char *pszToken; + char *pch; + + if (pMidiInstr->bank_msb < 0 || pMidiInstr->bank_msb > 127) + return NULL; + if (pMidiInstr->bank_lsb < 0 || pMidiInstr->bank_lsb > 127) + return NULL; + if (pMidiInstr->program < 0 || pMidiInstr->program > 127) + return NULL; + + // Lock this section up. + lscp_mutex_lock(pClient->mutex); + + pInstrInfo = &(pClient->midi_instrument_info); + lscp_midi_instrument_info_reset(pInstrInfo); + + sprintf(szQuery, "GET MIDI_INSTRUMENT INFO %d %d %d\r\n", + pMidiInstr->bank_msb, pMidiInstr->bank_lsb, pMidiInstr->program); + if (lscp_client_call(pClient, szQuery, 1) == LSCP_OK) { + pszResult = lscp_client_get_result(pClient); + pszToken = lscp_strtok((char *) pszResult, pszSeps, &(pch)); + while (pszToken) { + if (strcasecmp(pszToken, "NAME") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + lscp_unquote_dup(&(pInstrInfo->name), &pszToken); + } + else if (strcasecmp(pszToken, "ENGINE_NAME") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + lscp_unquote_dup(&(pInstrInfo->engine_name), &pszToken); + } + else if (strcasecmp(pszToken, "INSTRUMENT_FILE") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + lscp_unquote_dup(&(pInstrInfo->instrument_file), &pszToken); + } + else if (strcasecmp(pszToken, "INSTRUMENT_NR") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + pInstrInfo->instrument_nr = atoi(lscp_ltrim(pszToken)); + } + else if (strcasecmp(pszToken, "INSTRUMENT_NAME") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + lscp_unquote_dup(&(pInstrInfo->instrument_name), &pszToken); + } + else if (strcasecmp(pszToken, "LOAD_MODE") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) { + pszToken = lscp_ltrim(pszToken); + if (strcasecmp(pszToken, "ON_DEMAND") == 0) + pInstrInfo->load_mode = LSCP_LOAD_ON_DEMAND; + else + if (strcasecmp(pszToken, "ON_DEMAND_HOLD") == 0) + pInstrInfo->load_mode = LSCP_LOAD_ON_DEMAND_HOLD; + else + if (strcasecmp(pszToken, "PERSISTENT") == 0) + pInstrInfo->load_mode = LSCP_LOAD_PERSISTENT; + else + pInstrInfo->load_mode = LSCP_LOAD_DEFAULT; + } + } + else if (strcasecmp(pszToken, "VOLUME") == 0) { + pszToken = lscp_strtok(NULL, pszCrlf, &(pch)); + if (pszToken) + pInstrInfo->volume = (float) atof(lscp_ltrim(pszToken)); + } + pszToken = lscp_strtok(NULL, pszSeps, &(pch)); + } + } + else pInstrInfo = NULL; + + // Unlock this section down. + lscp_mutex_unlock(pClient->mutex); + + return pInstrInfo; +} + + +/** + * Clear the MIDI instrumnet map: + * CLEAR MIDI_INSTRUMENTS + * + * @param pClient Pointer to client instance structure. + * @param iSamplerChannel Sampler channel number. + * + * @returns LSCP_OK on success, LSCP_FAILED otherwise. + */ +lscp_status_t lscp_clear_midi_instruments ( lscp_client_t *pClient ) +{ + return lscp_client_query(pClient, "CLEAR MIDI_INSTRUMENTS\r\n"); +} + + // end of client.c