/***************************************************************************
*   NAME:  MIDISYSX.C $Revision: 1.14 $
**  COPYRIGHT:
**  "Copyright (c) 1994,1995 by e-Tek Labs"
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************
* $Log: midisysx.c $
* Revision 1.14  1996/01/31 15:46:55  unknown
* Made a couple of fixes (RFAR's added)
* Revision 1.13  1995/10/13 17:33:12  mleibow
* Changed patch loader to use iwllist.c and iwllist.h instead of list.c and list.h
* Revision 1.12  1995/09/29 13:44:00  teckert
* Added iwReloadPatchMap and fixed editable patch loading (i.e. now makes sure
* that any non-editable patch with the same patch and bank # is unloaded).
* Revision 1.11  1995/08/07 14:16:21  teckert
* Fixed bug in changing attack and release point counts
* Revision 1.10  1995/08/03 15:11:16  teckert
* Added Jim Selikoff's changes and a GPF bug fix
* Revision 1.9  1995/05/30 14:18:20  teckert
* 
* Revision 1.8  1995/05/12 13:14:15  teckert
* Completed new patch load sysex handling debugging
* Revision 1.7  1995/05/11 16:16:04  teckert
* Added new patch editor calling code
* Revision 1.6  1995/05/03 14:19:53  teckert
* Revision 1.5  1995/04/28 16:03:53  teckert
* Editable patch loading and unloading now uses new patch.c functions
* Revision 1.4  1995/04/19 18:37:36  teckert
* Updated with new patch.c helper routines
* Revision 1.3  1995/03/01 16:58:25  unknown
* Added file header
* Revision 1.1  1995/02/23 15:14:59  sdsmith
* Initial revision
***************************************************************************/
#include <windows.h>
#include <mmsystem.h>
#include <mmddk.h>
#include <string.h>
#include <iw.h>
#include <os.h>
#include "midi.h"
#include "patch.h"
#include <malloc.h>

extern int nKernelLoaded;
extern int load16as8;
extern char RFAR *pload_buff;
char patch_ed_path[256];
struct patch_lib RFAR *pEdLib=NULL;
void bad_load_cb(struct iwu_fast_patch RFAR *, int attempt);
void unload_editable_patch(int prog, int bank);
struct iwu_fast_patch RFAR *iwu_load_editable_patch(int prog, int bank,
	char RFAR *diskbuff,int bsize, void (*free_memory_cb)(struct iwu_fast_patch RFAR *fp, int attempt),
	int load_as_8,struct patch_lib RFAR *pLib);

void LoadEditablePatch(int,int,char far *);
void UnLoadEditablePatch(int,int);
int SetPatchParam(int,int,unsigned char,unsigned short);
int SetLayerParam(int,int,int,unsigned char,int);
int SetEnvelopeParam(int,int,int,int,int,unsigned char,int);
int MakeParam(unsigned char nMSB,unsigned char nLSB);
#define MakeParam(nMSB,nLSB) (((nMSB)&0x7F)<<7|((nLSB)&0x7F))



/****************************************************************************

FUNCTION DEFINITION:
OutputSysexParam - for debugging outputs a hex param using OutputDebugString

RETURNS: void
*/
#ifdef DEBUG
void OutputSysexParam(BYTE bParam)
{
	char pString[3];
	
	pString[0] = ((bParam&0x70)>>4)+'0';
	pString[1] = (bParam&0x0F)>9?(bParam&0x0F)-10+'A':(bParam&0x0F)+'0';
	pString[2] = 0; 
	OutputDebugString(pString);
}
#endif


/****************************************************************************

FUNCTION DEFINITION:
GetPatchChannels - records the patch channel assignments for the given patch
 
DESCRIPTION:                                        

RETURNS: void
*/
void GetPatchChannels(
    unsigned int RFAR *pFlagArray,
    int nProg,
    int nBank)
{
    int nClient,nChannel;       
    struct iwu_fast_patch RFAR *pFP;
				    
    for(nClient=0;nClient<MAXMIDICLIENTS;nClient++){
        pFlagArray[nClient] = 0;
	for(nChannel=0;nChannel<16;nChannel++){
	    if(MidiClients[nClient].CurPatch[nChannel] &&
	       MidiClients[nClient].CurPatch[nChannel]->id.h.program == nProg &&
	       MidiClients[nClient].CurPatch[nChannel]->id.h.bank == nBank){
	        pFlagArray[nClient] |= (1<<nChannel);
    	    }
	}
    }   
}


/****************************************************************************

FUNCTION DEFINITION:
SetPatchChannels - assigns the given patch to the channels indicated
 
DESCRIPTION:                                        

RETURNS: void
*/
void SetPatchChannels(
    unsigned int RFAR *pFlagArray,
    struct iwu_fast_patch RFAR *pPatch)
{
    int nClient,nChannel;       
				    
    for(nClient=0;nClient<MAXMIDICLIENTS;nClient++){
	for(nChannel=0;nChannel<16;nChannel++){
	    if(pFlagArray[nClient] & (1<<nChannel)){  
	        MidiClients[nClient].CurPatch[nChannel] = pPatch;
                if(pPatch && nKernelLoaded){
                    iw_midi_change_program(nClient*16+nChannel,pPatch->p);
                }
    	    }
	}
    }   
}


/****************************************************************************

FUNCTION DEFINITION:
DoSysexMessage - executes the current sysex message for the client

DESCRIPTION:
After a sysex message has been successfully parsed, this function is called to
execute the message.

RETURNS: void
*/
void DoSysexMessage(
	int nClient )               // index of the client that sent the sysex message
{                         
	int nMustInformKernel = 0;
	struct SysexMessage RFAR*pMsg;
	struct iwu_fast_patch RFAR *pFP;
	struct patch_lib FAR *pLib;
	int nProg,nBank;
	unsigned int Channels[MAXMIDICLIENTS];

	pMsg = (struct SysexMessage RFAR*)&MidiClients[nClient].SysMsg;
	switch(pMsg->command){
		case SXM_PATCHLOAD:                     
			switch(pMsg->subcmnd){
				case SXMP_LOAD:
#ifdef DEBUG
					OutputDebugString("MSX: Load Patch \"");
					OutputDebugString(pMsg->data);
					OutputDebugString("\"\n\r");
#endif
				// If the specified library is not in the pedlib list add it.
					for(pLib=pEdLib;pLib;pLib=pLib->next){
					    if(_fstrcmp((char far *)pLib->patch_filename,(char far *)pMsg->data)==0)
						break;
					}       
					if(!pLib){
					    pLib = (struct patch_lib RFAR *)_fmalloc(sizeof(struct patch_lib));
					    if(!pLib) return;
					    _fstrcpy((char far *)pLib->patch_filename,(char far *)pMsg->data);
					    _fstrcat((char far *)pLib->patch_filename,".FFF");
					    _fstrcpy((char far *)pLib->patch_dir,(char far *)patch_ed_path);
					    pLib->next = pEdLib;
					    pEdLib = pLib;
					    load_fff(pLib);
					}
					GetPatchChannels(Channels,pMsg->program,pMsg->bank);
					SetPatchChannels(Channels,NULL);
					iwu_unload_patch(pMsg->program,pMsg->bank);
					pFP = iwu_load_editable_patch(pMsg->program,pMsg->bank,pload_buff, 
								      PLOAD_BSIZE, bad_load_cb, load16as8, pLib);
					SetPatchChannels(Channels,pFP);
					break;
				case SXMP_UNLOAD:    
#ifdef DEBUG
					OutputDebugString("MSX: Unload Patch\n\r");
#endif
					pFP = iwu_find_patch(pMsg->program,pMsg->bank,0);
					if(pFP && (pFP->flags & IWU_PATCH_EDITABLE)){         
					    GetPatchChannels(Channels,pMsg->program,pMsg->bank);
					    SetPatchChannels(Channels,NULL);
					    nProg = pFP->id.h.program;
					    nBank = pFP->id.h.bank;
					    unload_editable_patch(pMsg->program,pMsg->bank);      
					    pFP = iwu_find_patch(pMsg->program,pMsg->bank,0);
					    SetPatchChannels(Channels,pFP);
					}
				// If there are no other editable patches in the specified library
				// unload it.
					break;
			}
			break;
		case SXM_EDITPATCH:         
#ifdef DEBUG
			OutputDebugString("MSX: Patch Param ");
#endif
			switch(pMsg->subcmnd){
			    case SXMP_LAYERMODE:
			    case SXMP_EXCLUSIONMODE:
			    case SXMP_EFFECTONE:
			    case SXMP_EFFECTONEDEPTH:
			    case SXMP_EFFECTTWO:
			    case SXMP_EFFECTTWODEPTH:
				nMustInformKernel = 
				SetPatchParam(
					pMsg->program,
					pMsg->bank,
					pMsg->subcmnd,
					pMsg->data[0]);
				break;
			    case SXMP_EXCLUSIONGROUP:
				nMustInformKernel = 
				SetPatchParam(
					pMsg->program,
					pMsg->bank,
					pMsg->subcmnd,
					MakeParam(pMsg->data[1],pMsg->data[2]));
				break;
			}
			break;
		case SXM_EDITLAYER:
#ifdef DEBUG
			OutputDebugString("MSX: Layer Param ");
#endif
			switch(pMsg->subcmnd){
				case SXML_HIGHRANGE:        // One Byte of Data
				case SXML_LOWRANGE:
				case SXML_PANOFFSET:
				case SXML_PANFREQ:
				case SXML_VELMODE:
				case SXML_ATTEN:
				case SXML_FREQCENTER:
				case SXML_LAYEREVENT:
				case SXML_TREMSHAPE:
				case SXML_VIBSHAPE:
					nMustInformKernel = 
					SetLayerParam(
						pMsg->program,
						pMsg->bank,
						pMsg->data[0],
						pMsg->subcmnd,
						pMsg->data[1]);
					break;
				case SXML_TREMDEPTH:        // Two Bytes of Data
				case SXML_TREMRATE:
				case SXML_TREMSWEEP:
				case SXML_TREMDELAY:
				case SXML_VIBDEPTH:
				case SXML_VIBRATE:
				case SXML_VIBSWEEP:
				case SXML_VIBDELAY:
				case SXML_FREQSCALE:
				case SXML_FLAGS:
					nMustInformKernel = 
					SetLayerParam(
						pMsg->program,
						pMsg->bank,
						pMsg->data[0],
						pMsg->subcmnd,
						MakeParam(pMsg->data[1],pMsg->data[2]));
					break;
			}
			break;
		case SXM_EDITENVELOPE:
#ifdef DEBUG
			OutputDebugString("MSX: Envelope Param ");
#endif
			if(pMsg->subcmnd < SXME_GLOBAL_COMMANDS) {
			    switch (pMsg->subcmnd) {
				case SXME_MODE:
				case SXME_INDEX:
				case SXME_NUMENV:
				    nMustInformKernel = 
				    SetEnvelopeParam(
					    pMsg->program,
					    pMsg->bank,
					    pMsg->data[0],
					    pMsg->data[1],
					    pMsg->data[2],
					    pMsg->subcmnd,
					    pMsg->data[3]);
				    break;
				case SXME_FLAGS:
				    nMustInformKernel = 
				    SetEnvelopeParam(
					    pMsg->program,
					    pMsg->bank,
					    pMsg->data[0],
					    pMsg->data[1],
					    pMsg->data[2],
					    pMsg->subcmnd,
					    MakeParam(pMsg->data[3],pMsg->data[4]));                                    
				    break;
			    }
			} else if(pMsg->subcmnd < SXME_INDEXED_COMMANDS) {
			    switch (pMsg->subcmnd) {
				case SXME_HIRANGE:
				case SXME_NATTACK:
				case SXME_NRELEASE:
				    nMustInformKernel = 
				    SetEnvelopeParam(
					    pMsg->program,
					    pMsg->bank,
					    pMsg->data[0],
					    pMsg->data[1],
					    pMsg->data[2],
					    pMsg->subcmnd,
					    pMsg->data[3]);                
				    break;
				case SXME_SOFFSET:
				case SXME_SRATE:
				case SXME_RRATE:
				    nMustInformKernel = 
				    SetEnvelopeParam(
					    pMsg->program,
					    pMsg->bank,
					    pMsg->data[0],
					    pMsg->data[1],
					    pMsg->data[2],
					    pMsg->subcmnd,
					    MakeParam(pMsg->data[3],pMsg->data[4]));                
				    break;
			    }
			} else {
			    nMustInformKernel = 
			    SetEnvelopeParam(
				    pMsg->program,
				    pMsg->bank,
				    pMsg->data[0],
				    pMsg->data[1],
				    pMsg->data[2],
				    pMsg->subcmnd,
				    MakeParam(pMsg->data[3],pMsg->data[4]));
			}
			break;
/* Edit wave commands are on hold for now, use the Digital Music interface to edit waves
		case SXM_EDITWAVE:
			SetWaveParam(nProg,nBank,nLayer,nWave,nCmnd):
			break;
*/
	}
	if(nMustInformKernel && nKernelLoaded){
		pFP = iwu_find_patch(pMsg->program,pMsg->bank,0);    // should return the editable patch
		if(pFP && (pFP->flags & IWU_PATCH_EDITABLE)){         
			iw_change_patch(pFP->p, pFP->p->id.p);
		}
	}
}

struct iw_patch RFAR *FindPatch(
	int nProg,
	int nBank)
{
	struct iwu_fast_patch RFAR * pFP;
	
	pFP = iwu_find_patch(nProg,nBank,0);    // should return the editable patch
	if(!pFP || !(pFP->flags & IWU_PATCH_EDITABLE)){         
#ifdef DEBUG
		OutputDebugString("couldn't find patch ");
#endif   
		return NULL;   
	}                   
#ifdef DEBUG
	OutputSysexParam((BYTE)nProg);  
	OutputDebugString(" ");
	OutputSysexParam((BYTE)nBank);  
#endif
	return pFP->p->id.p;
}

int SetPatchParam(
	int nProg,
	int nBank,
	unsigned char nCmnd,
	unsigned short nData)
{
	struct iw_patch RFAR *pPatch;
								
	pPatch = FindPatch(nProg,nBank);
	if(!pPatch){
#ifdef DEBUG
		OutputDebugString("\r\n");         // FindPatch() already printed "couldn't find patch "
#endif
		return 0;
	}
	switch(nCmnd){
		case SXMP_LAYERMODE:
#ifdef DEBUG
			OutputDebugString(" Layer Mode: ");
			OutputSysexParam((BYTE)nData);
			OutputDebugString("\r\n");
#endif
			pPatch->layer_mode = nData;
			break;
		case SXMP_EXCLUSIONMODE:
#ifdef DEBUG
			OutputDebugString(" Exclusion Mode: ");
			OutputSysexParam((BYTE)nData);
			OutputDebugString("\r\n");
#endif
			pPatch->exclusion_mode = nData;
			break;
		case SXMP_EXCLUSIONGROUP:
#ifdef DEBUG
			OutputDebugString(" Exclusion Group: ");
			OutputSysexParam((BYTE)nData);
			OutputDebugString("\r\n");
#endif
			pPatch->exclusion_group = nData;
			break;
		case SXMP_EFFECTONE:
#ifdef DEBUG
			OutputDebugString(" Effect One: ");
			OutputSysexParam((BYTE)nData);
			OutputDebugString("\r\n");
#endif
			pPatch->effect1 = nData;
			break;
		case SXMP_EFFECTONEDEPTH:
#ifdef DEBUG
			OutputDebugString(" Effect One Depth: ");
			OutputSysexParam((BYTE)nData);
			OutputDebugString("\r\n");
#endif
			pPatch->effect1_depth = nData;
			break;
		case SXMP_EFFECTTWO:
#ifdef DEBUG
			OutputDebugString(" Effect Two: ");
			OutputSysexParam((BYTE)nData);
			OutputDebugString("\r\n");
#endif
			pPatch->effect2 = nData;
			break;
		case SXMP_EFFECTTWODEPTH:
#ifdef DEBUG
			OutputDebugString(" Effect Two Depth: ");
			OutputSysexParam((BYTE)nData);
			OutputDebugString("\r\n");
#endif
			pPatch->effect2_depth = nData;
			break;
	}            
	return 1;
}

struct iw_layer RFAR *FindLayer(
	struct iw_patch RFAR *pPatch,
	int nLayer)
{
	struct iw_layer RFAR *pLayer;
	int i;
	
	if(!pPatch)
		return NULL;
		
	pLayer = pPatch->layers;
	for(i=0;i<nLayer;i++){
		if(!pLayer)
			break;
		pLayer = pLayer->id.p;
	}
#ifdef DEBUG
	OutputDebugString(" Layer: ");
	OutputSysexParam((BYTE)nLayer); 
#endif
	return pLayer;
}

int SetLayerParam(
	int nProg,
	int nBank,
	int nLayer,
	unsigned char nCmnd,
	int nData)
{
	struct iw_patch RFAR *pPatch;
	struct iw_layer RFAR *pLayer;
		
	pPatch = FindPatch(nProg,nBank);
	pLayer = FindLayer(pPatch,nLayer);
	if(!pLayer){
#ifdef DEBUG
		OutputDebugString("couldn't find layer\r\n");
#endif
		return 0;
	}
		
	switch(nCmnd){        
	case SXML_FLAGS:
#ifdef DEBUG
			OutputDebugString(" Layer Flags: ");
#endif
			pLayer->flags = nData;
			break;
		case SXML_HIGHRANGE:
#ifdef DEBUG
			OutputDebugString(" High Range: ");
#endif
			pLayer->high_range = nData;
			break;
		case SXML_LOWRANGE:
#ifdef DEBUG
			OutputDebugString(" Low Range: ");
#endif
			pLayer->low_range = nData;
			break;
		case SXML_PANOFFSET:
#ifdef DEBUG
			OutputDebugString(" Pan Offset: ");
#endif
			pLayer->pan = nData;
			break;
		case SXML_PANFREQ:
#ifdef DEBUG
			OutputDebugString(" Pan Frequency: ");
#endif
			pLayer->pan_freq_scale = nData;
			break;
		case SXML_TREMDEPTH:
#ifdef DEBUG
			OutputDebugString(" Tremolo Depth: ");
#endif
			pLayer->tremolo.depth = nData | ((nData & 0x2000) ? 0xC000 : 0);
			break;
		case SXML_TREMRATE:
#ifdef DEBUG
			OutputDebugString(" Tremolo Rate: ");
#endif
			pLayer->tremolo.freq = nData;
			break;
		case SXML_TREMSWEEP:
#ifdef DEBUG
			OutputDebugString(" Tremolo Sweep: ");
#endif
			pLayer->tremolo.sweep = nData;
			break;
		case SXML_TREMDELAY:
#ifdef DEBUG
			OutputDebugString(" Tremolo Delay: ");
#endif
			pLayer->tremolo.delay = nData;
			break;
		case SXML_TREMSHAPE:
#ifdef DEBUG
			OutputDebugString(" Tremolo Shape: ");
#endif
			pLayer->tremolo.shape = nData;
			break;
		case SXML_VIBDEPTH:
#ifdef DEBUG
			OutputDebugString(" Vibrato Depth: ");
#endif
			pLayer->vibrato.depth = nData | ((nData & 0x2000) ? 0xC000 : 0);
			break;
		case SXML_VIBRATE:
#ifdef DEBUG
			OutputDebugString(" Vibrato Rate: ");
#endif
			pLayer->vibrato.freq = nData;
			break;
		case SXML_VIBSWEEP:
#ifdef DEBUG
			OutputDebugString(" Vibrato Sweep: ");
#endif
			pLayer->vibrato.sweep = nData;
			break;
		case SXML_VIBDELAY:
#ifdef DEBUG
			OutputDebugString(" Vibrato Delay: ");
#endif
			pLayer->vibrato.delay = nData;
			break;
		case SXML_VIBSHAPE:
#ifdef DEBUG
			OutputDebugString(" Vibrato Shape: ");
#endif
			pLayer->vibrato.shape = nData;
			break;
		case SXML_VELMODE: 
#ifdef DEBUG
			OutputDebugString(" Velocity Mode: ");
#endif
			pLayer->velocity_mode = nData;
			break;
		case SXML_ATTEN:
#ifdef DEBUG
			OutputDebugString(" Attenuation: ");
#endif
			pLayer->attenuation = nData;
			break;
		case SXML_FREQSCALE:
#ifdef DEBUG
			OutputDebugString(" Frequency Scale: ");
#endif
			pLayer->freq_scale = nData;
			break;
		case SXML_FREQCENTER:
#ifdef DEBUG
			OutputDebugString(" Frequency Center: ");
#endif
			pLayer->freq_center = nData;
			break;
		case SXML_LAYEREVENT:
#ifdef DEBUG
			OutputDebugString(" Layer Event: ");
#endif
			pLayer->layer_event = nData;
			break;
		default:
#ifdef DEBUG
			OutputDebugString(" Unknown\r\n");
#endif
			return(0);
	}
#ifdef DEBUG
	OutputSysexParam((BYTE)nData);
	OutputDebugString("\r\n");
#endif
	return 1;
}


/*  

DESCRIPTION
Returns a pointer to a specific envelope record given the envelope header and the index of 
the required record.  This will work for records 0 through num_envelopes (one past the last
record).
*/
struct iw_envp_record RFAR *FindEnvelopeRecord(
	struct iw_envp_header RFAR *pEHdr,
	int nRec)
{
	struct iw_envp_record RFAR *pRec;
	int i=0,nPoints;
	
	pRec = (struct iw_envp_record RFAR *)((char RFAR *)pEHdr + sizeof(struct iw_envp_header));
	while(i<nRec){
		nPoints = pRec->nattack + pRec->nrelease;
		pRec = (struct iw_envp_record RFAR *)((char RFAR *)pRec + sizeof(struct iw_envp_record) +
				4 * nPoints);
		i++;
	}
	return pRec;
}


int SetEnvelopeParam(
	int nProg,
	int nBank,
	int nLayer,
	int nEnv,
	int nType,
	unsigned char nCmnd,
	int nData)
{
	struct iw_patch RFAR *pPatch;
	struct iw_layer RFAR *pLayer;
	struct iw_envp RFAR *pEnvp;
	struct iw_envp_header RFAR *pEHdr;
	struct iw_envp_record RFAR *pERec;
	struct iw_envp_record RFAR *pAfterLastRec;
	unsigned short RFAR *pVar;
	unsigned short RFAR *pEndOfData;
	char RFAR *pFrom;
	char RFAR *pTo;
	int nPoint;
	int result = 0;
	
	// First resolve the pointers to the envelope and the envelope's header
	pPatch = FindPatch(nProg,nBank);
	pLayer = FindLayer(pPatch,nLayer);
	if(!pLayer){
#ifdef DEBUG
		OutputDebugString("couldn't find layer\r\n");
#endif
		return 0;
	}

	pEnvp = nType?pLayer->penv.p:pLayer->venv.p;

#ifdef DEBUG
	if(pEnvp){
		if(nType)
			OutputDebugString(" pitch envelope ");
		else
			OutputDebugString(" volume envelope ");
	}else{
		if(nType)
			OutputDebugString(", no pitch envelope\r\n");
		else
			OutputDebugString(", no volume envelope\r\n");
	}
#endif
	if(!pEnvp){
		return 0;
	}
		
	pEHdr = &pEnvp->h;
	
	if(nCmnd >= 0x10){            // Find the correct envelope and its variable data
		pERec = FindEnvelopeRecord(pEHdr,nEnv);
		pVar = (unsigned short RFAR *)((char RFAR *)pERec + sizeof(struct iw_envp_record));
#ifdef DEBUG
		OutputDebugString("rec: ");
		OutputSysexParam((BYTE)nEnv);   
#endif
	}
	
	switch(nCmnd){
		case SXME_MODE:
#ifdef DEBUG
			OutputDebugString(" mode: ");
#endif
			pEHdr->mode = nData;
			result = 1;
			break;
		case SXME_INDEX:
#ifdef DEBUG
			OutputDebugString(" index: ");
#endif
			pEHdr->index_type = nData;
			result = 1;
			break;
		case SXME_NUMENV:               
#ifdef DEBUG
			OutputDebugString(" number of envelopes: ");
#endif
			// If decreasing just reduce the number
			if(nData < pEHdr->num_envelopes){
				pEHdr->num_envelopes = nData;
				result = 1;
			}               
			// If increasing add envelope records to the end
			else if(nData > pEHdr->num_envelopes){
				if(nData > 16)  // Don't allow more than 16
					break;
				for(;pEHdr->num_envelopes<nData;pEHdr->num_envelopes++){
					pERec = FindEnvelopeRecord(pEHdr,pEHdr->num_envelopes);
					pERec->nattack = 0;
					pERec->nrelease = 0;
					pERec->hirange = 0;
				}       
				result = 1;
			}
			break;
		case SXME_FLAGS:
#ifdef DEBUG
			OutputDebugString(" Envelope Flags: ");
#endif
			pEHdr->flags = nData;
			result = 1;
			break;
		case SXME_HIRANGE:
#ifdef DEBUG
			OutputDebugString(" high range: ");
#endif
			pERec->hirange = nData;
			result = 1;
			break;
		case SXME_SOFFSET:
#ifdef DEBUG
			OutputDebugString(" sustain offset: ");
#endif
			pERec->sustain_offset = nData;
			result = 1;
			break;
		case SXME_SRATE:
#ifdef DEBUG
			OutputDebugString(" sustain rate: ");
#endif
			pERec->sustain_rate = nData;
			result = 1;
			break;
		case SXME_RRATE:
#ifdef DEBUG
			OutputDebugString(" release rate: ");
#endif
			pERec->release_rate = nData;
			result = 1;
			break;
		case SXME_NATTACK:              // This changes the size of the envelopes section
#ifdef DEBUG
			OutputDebugString(" number of attack points: ");
#endif
			pFrom = (char RFAR *)&pVar[pERec->nattack * 2];
			pTo = (char RFAR *)&pVar[nData * 2];
		case SXME_NRELEASE:
#ifdef DEBUG
			if(nCmnd==SXME_NRELEASE){
				OutputDebugString(" number of release points: ");
			}
#endif
			if(nData > 16)
				break;
			if(nCmnd==SXME_NRELEASE){
				pFrom = (char RFAR *)&pVar[(pERec->nattack + pERec->nrelease)* 2];
				pTo = (char RFAR *)&pVar[(pERec->nattack + nData)* 2];
			}
			if(pFrom == pTo)
				break;
			pAfterLastRec = FindEnvelopeRecord(pEHdr,pEHdr->num_envelopes);
			_fmemmove(pTo,pFrom,(char RFAR *)pAfterLastRec - pFrom);
			if(nCmnd==SXME_NRELEASE){
				pERec->nrelease = nData;
			}else{
				pERec->nattack = nData;
			}
			result = 1;
			break;
		default:
#ifdef DEBUG
			OutputDebugString(" set point parameter: ");
#endif
			if(nCmnd < SXME_INDEXED_COMMANDS)
				break;
			nPoint = (nCmnd - SXME_INDEXED_COMMANDS)>>1;
			if(nPoint >= 16)
				break;
			pVar[nCmnd - SXME_INDEXED_COMMANDS] = nData;
			result = 1;
			break;
	}
#ifdef DEBUG
	OutputSysexParam((BYTE)nData);
	OutputDebugString("\r\n");
#endif
	return result;
}
