--- liblscp/trunk/src/common.c 2004/06/30 15:16:03 163 +++ liblscp/trunk/src/common.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. *****************************************************************************/ @@ -49,47 +49,20 @@ pClient->pszResult = strdup(lscp_ltrim(pszResult)); } -// The main client requester call executive. -lscp_status_t lscp_client_call ( lscp_client_t *pClient, const char *pszQuery ) + +// The common client receiver executive. +lscp_status_t lscp_client_recv ( lscp_client_t *pClient, char *pchBuffer, int *pcchBuffer, int iTimeout ) { fd_set fds; // File descriptor list for select(). int fd, fdmax; // Maximum file descriptor number. struct timeval tv; // For specifying a timeout value. int iSelect; // Holds select return status. - int iTimeout; - int cchQuery; - char achResult[LSCP_BUFSIZ]; - int cchResult; - const char *pszSeps = ":[]"; - char *pszResult; - char *pszToken; - char *pch; - int iErrno; lscp_status_t ret = LSCP_FAILED; if (pClient == NULL) return ret; - pszResult = NULL; - iErrno = -1; - - // Check if command socket socket is still valid. - if (pClient->cmd.sock == INVALID_SOCKET) { - pszResult = "Connection closed or no longer valid"; - lscp_client_set_result(pClient, pszResult, iErrno); - return ret; - } - - // Send data, and then, wait for the result... - cchQuery = strlen(pszQuery); - if (send(pClient->cmd.sock, pszQuery, cchQuery, 0) < cchQuery) { - lscp_socket_perror("_lscp_client_call: send"); - pszResult = "Failure during send operation"; - lscp_client_set_result(pClient, pszResult, iErrno); - return ret; - } - // Prepare for waiting on select... fd = (int) pClient->cmd.sock; FD_ZERO(&fds); @@ -97,8 +70,9 @@ fdmax = fd; // Use the timeout select feature... - iTimeout = pClient->iTimeout; - if (iTimeout > 1000) { + if (iTimeout < 1) + iTimeout = pClient->iTimeout; + if (iTimeout >= 1000) { tv.tv_sec = iTimeout / 1000; iTimeout -= tv.tv_sec * 1000; } @@ -109,35 +83,149 @@ iSelect = select(fdmax + 1, &fds, NULL, NULL, &tv); if (iSelect > 0 && FD_ISSET(fd, &fds)) { // May recv now... - cchResult = recv(pClient->cmd.sock, achResult, sizeof(achResult), 0); - if (cchResult > 0) { - // Assume early success. + *pcchBuffer = recv(pClient->cmd.sock, pchBuffer, *pcchBuffer, 0); + if (*pcchBuffer > 0) ret = LSCP_OK; - // Always force the result to be null terminated (and trim trailing CRLFs)! - while (cchResult > 0 && (achResult[cchResult - 1] == '\n' || achResult[cchResult- 1] == '\r')) - cchResult--; - achResult[cchResult] = (char) 0; + else if (*pcchBuffer < 0) + lscp_socket_perror("lscp_client_recv: recv"); + else if (*pcchBuffer == 0) { + // Damn, server probably disconnected, + // we better free everything down here. + lscp_socket_agent_free(&(pClient->evt)); + lscp_socket_agent_free(&(pClient->cmd)); + // Fake a result message. + ret = LSCP_QUIT; + } + } // Check if select has timed out. + else if (iSelect == 0) + ret = LSCP_TIMEOUT; + else + lscp_socket_perror("lscp_client_recv: select"); + + return ret; +} + + +// The main client requester call executive. +lscp_status_t lscp_client_call ( lscp_client_t *pClient, const char *pszQuery, int iResult ) +{ + int cchQuery; + char achBuffer[LSCP_BUFSIZ]; + int cchBuffer; + const char *pszSeps = ":[]"; + char *pszBuffer; + char *pszToken; + char *pch; + int iErrno; + char *pszResult; + int cchResult; + + lscp_status_t ret = LSCP_FAILED; + + if (pClient == NULL) + return ret; + + iErrno = -1; + cchResult = 0; + pszResult = NULL; + pszBuffer = NULL; + + // Check if command socket socket is still valid. + if (pClient->cmd.sock == INVALID_SOCKET) { + pszResult = "Connection closed or no longer valid"; + lscp_client_set_result(pClient, pszResult, iErrno); + return ret; + } + + // Check if last transaction has timed out, in which case + // we'll retry wait and flush for some pending garbage... + if (pClient->iTimeoutCount > 0) { + // We'll hope to get rid of timeout trouble... + pClient->iTimeoutCount = 0; + cchBuffer = sizeof(achBuffer); + ret = lscp_client_recv(pClient, achBuffer, &cchBuffer, pClient->iTimeout); + if (ret != LSCP_OK) { + // Things seems to be unresolved. Fake a result message. + iErrno = (int) ret; + pszResult = "Failure during flush timeout operation"; + lscp_client_set_result(pClient, pszResult, iErrno); + return ret; + } + } + + // Send data, and then, wait for the result... + cchQuery = strlen(pszQuery); + if (send(pClient->cmd.sock, pszQuery, cchQuery, 0) < cchQuery) { + lscp_socket_perror("lscp_client_call: send"); + pszResult = "Failure during send operation"; + lscp_client_set_result(pClient, pszResult, iErrno); + return ret; + } + + // Keep receiving while result is not the expected one: + // single-line result (iResult = 0) : one single CRLF ends the receipt; + // multi-line result (iResult > 0) : one "." followed by a last CRLF; + + while (pszResult == NULL) { + + // Wait for receive event... + cchBuffer = sizeof(achBuffer) - 1; + ret = lscp_client_recv(pClient, achBuffer, &cchBuffer, pClient->iTimeout); + + switch (ret) { + + case LSCP_OK: + // Always force the result to be null terminated. + achBuffer[cchBuffer] = (char) 0; // Check if the response it's an error or warning message. - if (strncasecmp(achResult, "WRN:", 4) == 0) + if (strncasecmp(achBuffer, "WRN:", 4) == 0) ret = LSCP_WARNING; - else if (strncasecmp(achResult, "ERR:", 4) == 0) + else if (strncasecmp(achBuffer, "ERR:", 4) == 0) ret = LSCP_ERROR; // So we got a result... if (ret == LSCP_OK) { // Reset errno in case of success. iErrno = 0; // Is it a special successful response? - if (strncasecmp(achResult, "OK[", 3) == 0) { + if (iResult < 1 && strncasecmp(achBuffer, "OK[", 3) == 0) { // Parse the OK message, get the return string under brackets... - pszToken = lscp_strtok(achResult, pszSeps, &(pch)); + pszToken = lscp_strtok(achBuffer, pszSeps, &(pch)); if (pszToken) pszResult = lscp_strtok(NULL, pszSeps, &(pch)); + } else { + // It can be specially long response... + cchResult += sizeof(achBuffer); + pszResult = malloc(cchResult + 1); + pszResult[0] = (char) 0; + if (pszBuffer) { + strcat(pszResult, pszBuffer); + free(pszBuffer); + } + strcat(pszResult, achBuffer); + pszBuffer = pszResult; + pszResult = NULL; + // Check for correct end-of-transmission... + // Depending whether its single or multi-line we'll + // flag end-of-transmission... + cchBuffer = strlen(pszBuffer); + if (cchBuffer > 2 + && pszBuffer[cchBuffer - 1] == '\n' + && pszBuffer[cchBuffer - 2] == '\r' + && (iResult < 1 || pszBuffer[cchBuffer - 3] == '.')) { + // Get rid of the trailing dot and CRLF anyway... + while (cchBuffer > 0 && ( + pszBuffer[cchBuffer - 1] == '\r' || + pszBuffer[cchBuffer - 1] == '\n' || + pszBuffer[cchBuffer - 1] == '.')) + cchBuffer--; + pszBuffer[cchBuffer] = (char) 0; + pszResult = pszBuffer; + } } - else pszResult = achResult; // The result string is now set to the command response, if any. } else { // Parse the error/warning message, skip first colon... - pszToken = lscp_strtok(achResult, pszSeps, &(pch)); + pszToken = lscp_strtok(achBuffer, pszSeps, &(pch)); if (pszToken) { // Get the error number... pszToken = lscp_strtok(NULL, pszSeps, &(pch)); @@ -149,32 +237,37 @@ } // The result string is set to the error/warning message text. } - } - else if (cchResult == 0) { - // Damn, server disconnected, we better free everything down here. - lscp_socket_agent_free(&(pClient->evt)); - lscp_socket_agent_free(&(pClient->cmd)); + break; + + case LSCP_TIMEOUT: + // We have trouble... + pClient->iTimeoutCount++; + // Fake a result message. + pszResult = "Timeout during receive operation"; + iErrno = (int) ret; + break; + + case LSCP_QUIT: // Fake a result message. - ret = LSCP_QUIT; pszResult = "Server terminated the connection"; iErrno = (int) ret; - } else { + break; + + case LSCP_FAILED: + default: // What's down? - lscp_socket_perror("_lscp_client_call: recv"); pszResult = "Failure during receive operation"; + break; } - } // Check if select has timed out. - else if (iSelect == 0) { - // Fake a result message. - ret = LSCP_TIMEOUT; - pszResult = "Timeout during receive operation"; - iErrno = (int) ret; - } - else lscp_socket_perror("_lscp_client_call: select"); - + } + // Make the result official... lscp_client_set_result(pClient, pszResult, iErrno); - + + // Free long-buffer, if any... + if (pszBuffer) + free(pszBuffer); + return ret; } @@ -223,6 +316,18 @@ return psz; } +// Unquote and make a duplicate of an in-split string. +void lscp_unquote_dup ( char **ppszDst, char **ppszSrc ) +{ + // Free desteny string, if already there. + if (*ppszDst) + free(*ppszDst); + *ppszDst = NULL; + // Unquote and duplicate. + if (*ppszSrc) + *ppszDst = lscp_unquote(ppszSrc, 1); +} + // Custom tokenizer. char *lscp_strtok ( char *pchBuffer, const char *pszSeps, char **ppch ) @@ -283,9 +388,9 @@ --pch; *pch = (char) 0; // Make it official. - ppszSplit[i++] = lscp_unquote(&pszHead, 0); + ppszSplit[i] = lscp_unquote(&pszHead, 0); // Do we need to grow? - if (i >= iSize) { + if (++i >= iSize) { // Yes, but only grow in chunks. iSize += LSCP_SPLIT_CHUNK1; // Allocate and copy to new split array. @@ -346,6 +451,11 @@ int iSize, i, j, cchSeps; int *piSplit, *piNewSplit; + // Get it clean first. + pchHead = lscp_ltrim((char *) pszCsv); + if (*pchHead == (char) 0) + return NULL; + // Initial size is one chunk away. iSize = LSCP_SPLIT_CHUNK1; // Allocate and split... @@ -355,7 +465,6 @@ // Make a copy of the original string. i = 0; - pchHead = (char *) pszCsv; if ((piSplit[i++] = atoi(pchHead)) < 0) { free(piSplit); return NULL; @@ -367,9 +476,9 @@ // Pre-advance to next item. pchHead = pch + cchSeps; // Make it official. - piSplit[i++] = atoi(pchHead); + piSplit[i] = atoi(pchHead); // Do we need to grow? - if (i >= iSize) { + if (++i >= iSize) { // Yes, but only grow in chunks. iSize += LSCP_SPLIT_CHUNK1; // Allocate and copy to new split array. @@ -506,6 +615,239 @@ #endif // LSCP_PSPLIT_COUNT +// Allocate a parameter list, optionally copying an existing one. +void lscp_plist_alloc (lscp_param_t **ppList) +{ + lscp_param_t *pParams; + int iSize, i; + + if (ppList) { + iSize = LSCP_SPLIT_CHUNK1; + pParams = (lscp_param_t *) malloc(iSize * sizeof(lscp_param_t)); + if (pParams) { + for (i = 0 ; i < iSize; i++) { + pParams[i].key = NULL; + pParams[i].value = NULL; + } + } + *ppList = pParams; + } +} + + +// Destroy a parameter list, including all it's contents. +void lscp_plist_free ( lscp_param_t **ppList ) +{ + lscp_param_t *pParams; + int i; + + if (ppList) { + if (*ppList) { + pParams = *ppList; + for (i = 0; pParams && pParams[i].key; i++) { + free(pParams[i].key); + free(pParams[i].value); + } + free(pParams); + } + *ppList = NULL; + } +} + + +// Add an item to a parameter list, growing it as fit. +void lscp_plist_append ( lscp_param_t **ppList, const char *pszKey, const char *pszValue ) +{ + lscp_param_t *pParams; + lscp_param_t *pNewParams; + int iSize, iNewSize; + int i = 0; + + if (ppList && *ppList) { + pParams = *ppList; + while (pParams[i].key) { + if (strcasecmp(pParams[i].key, pszKey) == 0) { + if (pParams[i].value) + free(pParams[i].value); + pParams[i].value = strdup(pszValue); + return; + } + i++; + } + iSize = LSCP_SPLIT_SIZE(i); + pParams[i].key = strdup(pszKey); + pParams[i].value = strdup(pszValue); + if (++i >= iSize) { + iNewSize = iSize + LSCP_SPLIT_CHUNK1; + pNewParams = (lscp_param_t *) malloc(iNewSize * sizeof(lscp_param_t)); + for (i = 0; i < iSize; i++) { + pNewParams[i].key = pParams[i].key; + pNewParams[i].value = pParams[i].value; + } + for ( ; i < iNewSize; i++) { + pNewParams[i].key = NULL; + pNewParams[i].value = NULL; + } + free(pParams); + *ppList = pNewParams; + } + } +} + +#ifdef LSCP_PLIST_COUNT + +// Compute a parameter list valid item count. +int lscp_plist_count ( lscp_param_t **ppList ) +{ + lscp_param_t *pParams; + int i = 0; + if (ppList && *ppList) { + pParams = *ppList; + while (pParams[i].key) + i++; + } + return i; +} + +// Compute the legal parameter list size. +int lscp_plist_size ( lscp_param_t **ppList ) +{ + return LSCP_SPLIT_SIZE(lscp_plist_count(ppList)); +} + +#endif // LSCP_PLIST_COUNT + + +// Split a string into an array of MIDI instrument triplets. +lscp_midi_instrument_t *lscp_midi_instruments_create ( const char *pszCsv ) +{ + char *pchHead, *pch; + int iSize, i, j, k; + lscp_midi_instrument_t *pInstrs; + lscp_midi_instrument_t *pNewInstrs; + + // Get it clean first. + pchHead = lscp_ltrim((char *) pszCsv); + if (*pchHead == (char) 0) + return NULL; + + // Initial size is one chunk away. + iSize = LSCP_SPLIT_CHUNK1; + // Allocate and split... + pInstrs = (lscp_midi_instrument_t *) malloc(iSize * sizeof(lscp_midi_instrument_t)); + if (pInstrs == NULL) + return NULL; + + // Go on for it... + i = 0; + k = 0; + + while ((pch = strpbrk(pchHead, "{,}")) != NULL) { + // Pre-advance to next item. + switch (*pch) { + case '{': + pchHead = pch + 1; + if (k == 0) { + pInstrs[i].bank_msb = atoi(pchHead); + k++; + } + break; + case ',': + pchHead = pch + 1; + if (k == 1) { + pInstrs[i].bank_lsb = atoi(pchHead); + k++; + } + else + if (k == 2) { + pInstrs[i].program = atoi(pchHead); + k++; + } + break; + case '}': + pchHead = pch + 1; + k = 0; + break; + } + // Do we need to grow? + if (k == 3 && ++i >= iSize) { + // Yes, but only grow in chunks. + iSize += LSCP_SPLIT_CHUNK1; + // Allocate and copy to new split array. + pNewInstrs = (lscp_midi_instrument_t *) malloc(iSize * sizeof(lscp_midi_instrument_t)); + if (pNewInstrs) { + for (j = 0; j < i; j++) { + pNewInstrs[j].bank_msb = pInstrs[j].bank_msb; + pNewInstrs[j].bank_lsb = pInstrs[j].bank_lsb; + pNewInstrs[j].program = pInstrs[j].program; + } + free(pInstrs); + pInstrs = pNewInstrs; + } + } + } + + // Special terminate split array. + for ( ; i < iSize; i++) { + pInstrs[i].bank_msb = -1; + pInstrs[i].bank_lsb = -1; + pInstrs[i].program = -1; + } + + return pInstrs; +} + +// Destroy a MIDI instrument triplet array. +void lscp_midi_instruments_destroy ( lscp_midi_instrument_t *pInstrs ) +{ + if (pInstrs) + free(pInstrs); +} + +#ifdef LSCP_MIDI_INSTRUMENTS_COUNT + +// Compute a MIDI instrument array item count. +int lscp_midi_instruments_count ( lscp_midi_instrument_t *pInstrs ) +{ + int i = 0; + while (pInstrs && pInstrs[i].program >= 0) + i++; + return i; +} + +// Compute a MIDI instrument array size. +int lscp_midi_instruments_size ( lscp_midi_instrument_t *pInstrs ) +{ + return LSCP_SPLIT_SIZE(lscp_midi_instruments_count(pInstrs)); +} + +#endif // LSCP_MIDI_INSTRUMENTS_COUNT + + +//------------------------------------------------------------------------- +// Server info struct helper functions. + +void lscp_server_info_init ( lscp_server_info_t *pServerInfo ) +{ + pServerInfo->description = NULL; + pServerInfo->version = NULL; +} + +void lscp_server_info_free ( lscp_server_info_t *pServerInfo ) +{ + if (pServerInfo->description) + free(pServerInfo->description); + if (pServerInfo->version) + free(pServerInfo->version); +} + +void lscp_server_info_reset ( lscp_server_info_t *pServerInfo ) +{ + lscp_server_info_free(pServerInfo); + lscp_server_info_init(pServerInfo); +} + + //------------------------------------------------------------------------- // Engine info struct helper functions. @@ -515,13 +857,17 @@ pEngineInfo->version = NULL; } -void lscp_engine_info_reset ( lscp_engine_info_t *pEngineInfo ) +void lscp_engine_info_free ( lscp_engine_info_t *pEngineInfo ) { if (pEngineInfo->description) free(pEngineInfo->description); if (pEngineInfo->version) free(pEngineInfo->version); +} +void lscp_engine_info_reset ( lscp_engine_info_t *pEngineInfo ) +{ + lscp_engine_info_free(pEngineInfo); lscp_engine_info_init(pEngineInfo); } @@ -537,14 +883,17 @@ pChannelInfo->audio_routing = NULL; pChannelInfo->instrument_file = NULL; pChannelInfo->instrument_nr = 0; + pChannelInfo->instrument_name = NULL; pChannelInfo->instrument_status = 0; pChannelInfo->midi_device = 0; pChannelInfo->midi_port = 0; pChannelInfo->midi_channel = 0; pChannelInfo->volume = 0.0; + pChannelInfo->mute = 0; + pChannelInfo->solo = 0; } -void lscp_channel_info_reset ( lscp_channel_info_t *pChannelInfo ) +void lscp_channel_info_free ( lscp_channel_info_t *pChannelInfo ) { if (pChannelInfo->engine_name) free(pChannelInfo->engine_name); @@ -552,7 +901,13 @@ lscp_szsplit_destroy(pChannelInfo->audio_routing); if (pChannelInfo->instrument_file) free(pChannelInfo->instrument_file); + if (pChannelInfo->instrument_name) + free(pChannelInfo->instrument_name); +} +void lscp_channel_info_reset ( lscp_channel_info_t *pChannelInfo ) +{ + lscp_channel_info_free(pChannelInfo); lscp_channel_info_init(pChannelInfo); } @@ -567,19 +922,69 @@ pDriverInfo->parameters = NULL; } -void lscp_driver_info_reset ( lscp_driver_info_t *pDriverInfo ) +void lscp_driver_info_free ( lscp_driver_info_t *pDriverInfo ) { if (pDriverInfo->description) free(pDriverInfo->description); if (pDriverInfo->version) free(pDriverInfo->version); lscp_szsplit_destroy(pDriverInfo->parameters); +} +void lscp_driver_info_reset ( lscp_driver_info_t *pDriverInfo ) +{ + lscp_driver_info_free(pDriverInfo); lscp_driver_info_init(pDriverInfo); } //------------------------------------------------------------------------- +// Device info struct functions. + +void lscp_device_info_init ( lscp_device_info_t *pDeviceInfo ) +{ + pDeviceInfo->driver = NULL; + lscp_plist_alloc(&(pDeviceInfo->params)); +} + +void lscp_device_info_free ( lscp_device_info_t *pDeviceInfo ) +{ + if (pDeviceInfo->driver) + free(pDeviceInfo->driver); + lscp_plist_free(&(pDeviceInfo->params)); +} + +void lscp_device_info_reset ( lscp_device_info_t *pDeviceInfo ) +{ + lscp_device_info_free(pDeviceInfo); + lscp_device_info_init(pDeviceInfo); +} + + +//------------------------------------------------------------------------- +// Device channel/port info struct functions. + +void lscp_device_port_info_init ( lscp_device_port_info_t *pDevicePortInfo ) +{ + pDevicePortInfo->name = NULL; + lscp_plist_alloc(&(pDevicePortInfo->params)); +} + +void lscp_device_port_info_free ( lscp_device_port_info_t *pDevicePortInfo ) +{ + if (pDevicePortInfo->name) + free(pDevicePortInfo->name); + lscp_plist_free(&(pDevicePortInfo->params)); +} + +void lscp_device_port_info_reset ( lscp_device_port_info_t *pDevicePortInfo ) +{ + lscp_device_port_info_free(pDevicePortInfo); + lscp_device_port_info_init(pDevicePortInfo); +} + + +//------------------------------------------------------------------------- // Parameter struct helper functions. void lscp_param_info_init ( lscp_param_info_t *pParamInfo ) @@ -596,7 +1001,7 @@ pParamInfo->possibilities = NULL; } -void lscp_param_info_reset ( lscp_param_info_t *pParamInfo ) +void lscp_param_info_free ( lscp_param_info_t *pParamInfo ) { if (pParamInfo->description) free(pParamInfo->description); @@ -608,31 +1013,76 @@ if (pParamInfo->range_max) free(pParamInfo->range_max); lscp_szsplit_destroy(pParamInfo->possibilities); - +} + +void lscp_param_info_reset ( lscp_param_info_t *pParamInfo ) +{ + lscp_param_info_free(pParamInfo); lscp_param_info_init(pParamInfo); } //------------------------------------------------------------------------- -// Concatenate a parameter list (key='value'...) into a string. +// Concatenate a parameter list (key='value'...) into a string, +// appending a crlf terminator. int lscp_param_concat ( char *pszBuffer, int cchMaxBuffer, lscp_param_t *pParams ) { int cchBuffer, cchParam, i; - if (pszBuffer == NULL || pParams == NULL) + if (pszBuffer == NULL) return 0; cchBuffer = strlen(pszBuffer); - for (i = 0; pParams[i].key && pParams[i].value; i++) { + for (i = 0; pParams && pParams[i].key && pParams[i].value; i++) { cchParam = strlen(pParams[i].key) + strlen(pParams[i].value) + 4; - if (cchBuffer + cchParam < cchMaxBuffer) { + if (cchBuffer + cchParam + 2 < cchMaxBuffer) { sprintf(pszBuffer + cchBuffer, " %s='%s'", pParams[i].key, pParams[i].value); cchBuffer += cchParam; } } + + if (cchBuffer + 2 < cchMaxBuffer) { + pszBuffer[cchBuffer++] = '\r'; + pszBuffer[cchBuffer++] = '\n'; + pszBuffer[cchBuffer ] = (char) 0; + } + return cchBuffer; } +//------------------------------------------------------------------------- +// MIDI instrument info struct helper functions. + +void lscp_midi_instrument_info_init ( lscp_midi_instrument_info_t *pInstrInfo ) +{ + pInstrInfo->name = NULL; + pInstrInfo->engine_name = NULL; + pInstrInfo->instrument_file = NULL; + pInstrInfo->instrument_nr = 0; + pInstrInfo->instrument_name = NULL; + pInstrInfo->load_mode = LSCP_LOAD_DEFAULT; + pInstrInfo->volume = 0.0; +} + +void lscp_midi_instrument_info_free ( lscp_midi_instrument_info_t *pInstrInfo ) +{ + if (pInstrInfo->name) + free(pInstrInfo->name); + if (pInstrInfo->engine_name) + free(pInstrInfo->engine_name); + if (pInstrInfo->instrument_file) + free(pInstrInfo->instrument_file); + if (pInstrInfo->instrument_name) + free(pInstrInfo->instrument_name); +} + +void lscp_midi_instrument_info_reset ( lscp_midi_instrument_info_t *pInstrInfo ) +{ + lscp_midi_instrument_info_free(pInstrInfo); + lscp_midi_instrument_info_init(pInstrInfo); +} + + // end of common.c