Beispiel #1
0
//---------------------------------------------------------
//   processEvent
//    All events from the sequencer go here
//---------------------------------------------------------
bool ISynth::processEvent(const MusECore::MidiPlayEvent& ev)
      {
      switch(ev.type()) {
            case MusECore::ME_CONTROLLER:
                setController(ev.channel(), ev.dataA(), ev.dataB());
                //return true;  // ??
                break;            
            case MusECore::ME_NOTEON:
                  return playNote(ev.channel(), ev.dataA(), ev.dataB());
            case MusECore::ME_NOTEOFF:
                  return playNote(ev.channel(), ev.dataA(), 0);
            case MusECore::ME_SYSEX:
                  return sysex(ev.len(), ev.data());
            case MusECore::ME_PITCHBEND:
                setController(ev.channel(), MusECore::CTRL_PITCH, ev.dataA());
                break;
            // Synths are not allowed to receive ME_PROGRAM, CTRL_HBANK, or CTRL_LBANK alone anymore - only CTRL_PROGRAM.
            //case MusECore::ME_PROGRAM:
            //    setController(ev.channel(), MusECore::CTRL_PROGRAM, ev.dataA());
            //    break;   
            default:
                break;
            }
      return false;
      }
bool MIDIFileReadMultiTrack::mf_sysex ( MIDIClockTime time, int type, int len, unsigned char *s ) // funcVRM
{
    MIDITimedMessage msg;
    msg.SetSysEx( type ); // set msg status byte (0xF0 or 0xF7)

    int num = len; // number of possible SysExURT header data bytes in msg, 0...5
    if ( num > 5 )
        num = 5;

    // add up to 5 starting bytes for SysExURT functions
    if ( num > 0 )
        msg.SetByte2( s[0] );
    if ( num > 1 )
        msg.SetByte3( s[1] );
    if ( num > 2 )
        msg.SetByte4( s[2] );
    if ( num > 3 )
        msg.SetByte5( s[3] );
    if ( num > 4 )
        msg.SetByte6( s[4] );

    msg.SetTime ( time );
    MIDISystemExclusive sysex( len );

    for ( int i = 0; i < len; ++i )
    {
        sysex.PutSysByte ( s[i] );
    }

   msg.SetDataLength( num );
   return AddEventToMultiTrack ( msg, &sysex, cur_track );
}
Beispiel #3
0
void pluginCore::processSysex(int length, char *data)
{
  tMidiSysex sx;
  sx.length = length;
  sx.data = new unsigned char[length];
  memcpy(sx.data, data, length * sizeof(char));
  m_midi_sysex.push(sx);
  sysex(length, data[0]);
}
Beispiel #4
0
bool Mess::processEvent(const MidiPlayEvent& ev)
      {
      switch(ev.type()) {
            case ME_NOTEON:
                  return playNote(ev.channel(), ev.dataA(), ev.dataB());
            case ME_NOTEOFF:
                  return playNote(ev.channel(), ev.dataA(), 0);
            case ME_SYSEX:
	            return sysex(ev.len(), ev.data());
            case ME_CONTROLLER:
                  return setController(ev.channel(), ev.dataA(), ev.dataB());
            case ME_PITCHBEND:       // Tim.
                  return setController(ev.channel(), CTRL_PITCH, ev.dataA());
            }
      return false;
      }
bool MIDIFileReadMultiTrack::mf_text ( MIDIClockTime time, int type, int len, unsigned char *s ) // VRM
{
    MIDITimedMessage msg;
    msg.SetStatus ( META_EVENT );
    msg.SetMetaType ( ( uchar ) type ); // remember - MF_META_* id codes match META_* codes
    msg.SetTime ( time );

    MIDISystemExclusive sysex( len ); // VRM

    for ( int i = 0; i < len; ++i )
    {
        sysex.PutSysByte ( s[i] ); // VRM
    }

    msg.SetDataLength( 0 ); // VRM // variable data length don't saved to data_length
    return AddEventToMultiTrack ( msg, &sysex, cur_track ); // VRM
}
Beispiel #6
0
//---------------------------------------------------------
//   processEvent
//    All events from the sequencer go here
//---------------------------------------------------------
bool ISynth::processEvent(const MusECore::MidiPlayEvent& ev)
      {
      switch(ev.type()) {
            case MusECore::ME_CONTROLLER:
                setController(ev.channel(), ev.dataA(), ev.dataB());
                //return true;  // ??
                break;            
            case MusECore::ME_NOTEON:
                  return playNote(ev.channel(), ev.dataA(), ev.dataB());
            case MusECore::ME_NOTEOFF:
                  return playNote(ev.channel(), ev.dataA(), 0);
            case MusECore::ME_SYSEX:
                  return sysex(ev.len(), ev.data());
            case MusECore::ME_PITCHBEND:
                setController(ev.channel(), MusECore::CTRL_PITCH, ev.dataA());
                break;            
            case MusECore::ME_PROGRAM:
                setController(ev.channel(), MusECore::CTRL_PROGRAM, ev.dataA());
                break;   
            default:
                break;
            }
      return false;
      }
Beispiel #7
0
VstIntPtr VSTCALLBACK IPlugVST::VSTDispatcher(AEffect *pEffect, VstInt32 opCode, VstInt32 idx, VstIntPtr value, void *ptr, float opt)
{
  // VSTDispatcher is an IPlugVST class member, we can access anything in IPlugVST from here.
  IPlugVST* _this = (IPlugVST*) pEffect->object;
  if (!_this)
  {
    return 0;
  }
  IPlugBase::IMutexLock lock(_this);

  // Handle a couple of opcodes here to make debugging easier.
  switch (opCode)
  {
    case effEditIdle:
    case __effIdleDeprecated:
    #ifdef USE_IDLE_CALLS
    _this->OnIdle();
    #endif
    return 0;
  }

  Trace(TRACELOC, "%d(%s):%d:%d", opCode, VSTOpcodeStr(opCode), idx, (int) value);

  switch (opCode)
  {
    case effOpen:
    {
      _this->HostSpecificInit();
      _this->OnParamReset();
      return 0;
    }
    case effClose:
    {
      lock.Destroy();
      DELETE_NULL(_this);
      return 0;
    }
    case effGetParamLabel:
    {
      if (idx >= 0 && idx < _this->NParams())
      {
        strcpy((char*) ptr, _this->GetParam(idx)->GetLabelForHost());
      }
      return 0;
    }
    case effGetParamDisplay:
    {
      if (idx >= 0 && idx < _this->NParams())
      {
        _this->GetParam(idx)->GetDisplayForHost((char*) ptr);
      }
      return 0;
    }
    case effGetParamName:
    {
      if (idx >= 0 && idx < _this->NParams())
      {
        strcpy((char*) ptr, _this->GetParam(idx)->GetNameForHost());
      }
      return 0;
    }
      //could implement effGetParameterProperties to group parameters, but can't find a host that supports it
//    case effGetParameterProperties:
//    {
//      if (idx >= 0 && idx < _this->NParams())
//      {
//        VstParameterProperties* props = (VstParameterProperties*) ptr;
//        
//        props->flags = kVstParameterSupportsDisplayCategory;
//        props->category = idx+1;
//        props->numParametersInCategory = 1;
//        strcpy(props->categoryLabel, "test");
//      }
//      return 1;
//    }
    case effGetParameterProperties:
    {
      if (idx >= 0 && idx < _this->NParams())
      {
        VstParameterProperties* props = (VstParameterProperties*) ptr;
        props->flags = 0;
        IParam* pParam = _this->GetParam(idx);
        if (pParam->Type() == IParam::kTypeBool) {
          props->flags |= kVstParameterIsSwitch;
        }
        if (pParam->Type() == IParam::kTypeEnum || pParam->Type() == IParam::kTypeInt) {
          props->flags |= kVstParameterUsesFloatStep;
          int possibleValuesCount = (int) (pParam->GetMax() - pParam->GetMin());
          props->stepFloat = 1.0 / possibleValuesCount;
          props->smallStepFloat = props->stepFloat;
          props->largeStepFloat = props->stepFloat;
        }
      }
      return 1;
    }
    case effString2Parameter:
    {
      if (idx >= 0 && idx < _this->NParams())
      {
        if (ptr)
        {
          double v;
          IParam* pParam = _this->GetParam(idx);
          if (pParam->GetNDisplayTexts())
          {
            int vi;
            if (!pParam->MapDisplayText((char*)ptr, &vi)) return 0;
            v = (double)vi;
          }
          else
          {
            v = atof((char*)ptr);
            if (pParam->DisplayIsNegated()) v = -v;
          }
          if (_this->GetGUI()) _this->GetGUI()->SetParameterFromPlug(idx, v, false);
          pParam->Set(v);
          _this->OnParamChange(idx);
        }
        return 1;
      }
      return 0;
    }
    case effSetSampleRate:
    {
      _this->SetSampleRate(opt);
      _this->Reset();
      return 0;
    }
    case effSetBlockSize:
    {
      _this->SetBlockSize(value);
      _this->Reset();
      return 0;
    }
    case effMainsChanged:
    {
      if (!value)
      {
        _this->OnActivate(false);
        _this->Reset();
      }
      else
      {
        _this->OnActivate(true);
      }
      return 0;
    }
    case effEditGetRect:
    {
      if (ptr && _this->GetGUI())
      {
        *(ERect**) ptr = &(_this->mEditRect);
        return 1;
      }
      ptr = 0;
      return 0;
    }
    case effEditOpen:
    {
      IGraphics* pGraphics = _this->GetGUI();
      
      if (pGraphics)
      {
        #ifdef _WIN32
          if (!pGraphics->OpenWindow(ptr)) pGraphics=0;
        #else   // OSX, check if we are in a Cocoa VST host
          #if defined(__LP64__)
          if (!pGraphics->OpenWindow(ptr)) pGraphics=0;
          #else
          bool iscocoa = (_this->mHasVSTExtensions&VSTEXT_COCOA);
          if (iscocoa && !pGraphics->OpenWindow(ptr)) pGraphics=0;
          if (!iscocoa && !pGraphics->OpenWindow(ptr, 0)) pGraphics=0;
          #endif
        #endif
        if (pGraphics)
        {
          _this->OnGUIOpen();
          return 1;
        }
      }
      return 0;
    }
    case effEditClose:
    {
      if (_this->GetGUI())
      {
        _this->OnGUIClose();
        _this->GetGUI()->CloseWindow();
        return 1;
      }
      return 0;
    }
    case __effIdentifyDeprecated:
    {
      return 'NvEf';  // Random deprecated magic.
    }
    case effGetChunk:
    {
      BYTE** ppData = (BYTE**) ptr;
      if (ppData)
      {
        bool isBank = (!idx);
        ByteChunk* pChunk = (isBank ? &(_this->mBankState) : &(_this->mState));
        _this->InitChunkWithIPlugVer(pChunk);
        bool savedOK = true;
        
        if (isBank)
        {
          _this->ModifyCurrentPreset();
          savedOK = _this->SerializePresets(pChunk);
        }
        else
        {
          savedOK = _this->SerializeState(pChunk);
        }
        
        if (savedOK && pChunk->Size())
        {
          *ppData = pChunk->GetBytes();
          return pChunk->Size();
        }
      }
      return 0;
    }
    case effSetChunk:
    {
      if (ptr)
      {
        bool isBank = (!idx);
        ByteChunk* pChunk = (isBank ? &(_this->mBankState) : &(_this->mState));
        pChunk->Resize(value);
        memcpy(pChunk->GetBytes(), ptr, value);
        int pos = 0;
        int iplugVer = _this->GetIPlugVerFromChunk(pChunk, &pos);
        isBank &= (iplugVer >= 0x010000);
        
        if (isBank)
        {
          pos = _this->UnserializePresets(pChunk, pos);
        }
        else
        {
          pos = _this->UnserializeState(pChunk, pos);
          _this->ModifyCurrentPreset();
        }
        
        if (pos >= 0)
        {
          _this->RedrawParamControls();
          return 1;
        }
      }
      return 0;
    }
    case effProcessEvents:
    {
      VstEvents* pEvents = (VstEvents*) ptr;
      if (pEvents && pEvents->events)
      {
        for (int i = 0; i < pEvents->numEvents; ++i)
        {
          VstEvent* pEvent = pEvents->events[i];
          if (pEvent)
          {
            if (pEvent->type == kVstMidiType)
            {
              VstMidiEvent* pME = (VstMidiEvent*) pEvent;
              IMidiMsg msg(pME->deltaFrames, pME->midiData[0], pME->midiData[1], pME->midiData[2]);
              _this->ProcessMidiMsg(&msg);
              //#ifdef TRACER_BUILD
              //  msg.LogMsg();
              //#endif
            }
            else if (pEvent->type == kVstSysExType) 
            {
              VstMidiSysexEvent* pSE = (VstMidiSysexEvent*) pEvent;
              ISysEx sysex(pSE->deltaFrames, (const BYTE*)pSE->sysexDump, pSE->dumpBytes);
              _this->ProcessSysEx(&sysex);
            }
          }
        }
        return 1;
      }
      return 0;
    }
    case effCanBeAutomated:
    {
      return 1;
    }
    case effGetInputProperties:
    {
      if (ptr && idx >= 0 && idx < _this->NInChannels())
      {
        VstPinProperties* pp = (VstPinProperties*) ptr;
        pp->flags = kVstPinIsActive;
        if (!(idx%2) && idx < _this->NInChannels()-1)
        {
          pp->flags |= kVstPinIsStereo;
        }

        if (_this->GetInputLabel(idx)->GetLength())
        {
          sprintf(pp->label, "%s", _this->GetInputLabel(idx)->Get());
        }
        else
        {
          sprintf(pp->label, "Input %d", idx + 1);
        }

        return 1;
      }
      return 0;
    }
    case effGetOutputProperties:
    {
      if (ptr && idx >= 0 && idx < _this->NOutChannels())
      {
        VstPinProperties* pp = (VstPinProperties*) ptr;
        pp->flags = kVstPinIsActive;
        if (!(idx%2) && idx < _this->NOutChannels()-1)
        {
          pp->flags |= kVstPinIsStereo;
        }

        if (_this->GetOutputLabel(idx)->GetLength())
        {
          sprintf(pp->label, "%s", _this->GetOutputLabel(idx)->Get());
        }
        else
        {
          sprintf(pp->label, "Output %d", idx + 1);
        }

        return 1;
      }
      return 0;
    }
    case effGetPlugCategory:
    {
      if (_this->IsInst()) return kPlugCategSynth;
      return kPlugCategEffect;
    }
    case effProcessVarIo:
    {
      // VstVariableIo* pIO = (VstVariableIo*) ptr; // For offline processing (of audio files?)
      return 0;
    }
    case effSetSpeakerArrangement:
    {
      VstSpeakerArrangement* pInputArr = (VstSpeakerArrangement*) value;
      VstSpeakerArrangement* pOutputArr = (VstSpeakerArrangement*) ptr;
      if (pInputArr)
      {
        int n = pInputArr->numChannels;
        _this->SetInputChannelConnections(0, n, true);
        _this->SetInputChannelConnections(n, _this->NInChannels() - n, false);
      }
      if (pOutputArr)
      {
        int n = pOutputArr->numChannels;
        _this->SetOutputChannelConnections(0, n, true);
        _this->SetOutputChannelConnections(n, _this->NOutChannels() - n, false);
      }
      return 1;
    }
    case effGetSpeakerArrangement:
    {
      VstSpeakerArrangement** ppInputArr = (VstSpeakerArrangement**) value;
      VstSpeakerArrangement** ppOutputArr = (VstSpeakerArrangement**) ptr;
      if (ppInputArr)
      {
        *ppInputArr = &(_this->mInputSpkrArr);
      }
      if (ppOutputArr)
      {
        *ppOutputArr = &(_this->mOutputSpkrArr);
      }
      return 1;
    }
    case effGetEffectName:
    {
      if (ptr)
      {
        strcpy((char*) ptr, _this->GetEffectName());
        return 1;
      }
      return 0;
    }
    case effGetProductString:
    {
      if (ptr)
      {
        strcpy((char*) ptr, _this->GetProductName());
        return 1;
      }
      return 0;
    }
    case effGetVendorString:
    {
      if (ptr)
      {
        strcpy((char*) ptr, _this->GetMfrName());
        return 1;
      }
      return 0;
    }
    case effCanDo:
    {
      if (ptr)
      {
        Trace(TRACELOC, "VSTCanDo(%s)", (char*) ptr);
        if (!strcmp((char*) ptr, "receiveVstTimeInfo"))
        {
          return 1;
        }
        if (_this->DoesMIDI())
        {
          if (!strcmp((char*) ptr, "sendVstEvents") ||
              !strcmp((char*) ptr, "sendVstMidiEvent") ||
              !strcmp((char*) ptr, "receiveVstEvents") ||
              !strcmp((char*) ptr, "receiveVstMidiEvent"))   // ||
          {
            //!strcmp((char*) ptr, "midiProgramNames")) {
            return 1;
          }
        }
        // Support Reaper VST extensions: http://www.reaper.fm/sdk/vst/
        if (!strcmp((char*) ptr, "hasCockosExtensions"))
        {
          _this->mHasVSTExtensions |= VSTEXT_COCKOS;
          return 0xbeef0000;
        }
        else if (!strcmp((char*) ptr, "hasCockosViewAsConfig"))
        {
          _this->mHasVSTExtensions |= VSTEXT_COCOA;
          return 0xbeef0000;
        }
      }
      return 0;
    }
    case effGetTailSize:
    {
      return _this->GetTailSize();
    }
    case effVendorSpecific:
    {
      // Support Reaper VST extensions: http://www.reaper.fm/sdk/vst/
      if (idx == effGetParamDisplay && ptr)
      {
        if (value >= 0 && value < _this->NParams())
        {
          _this->GetParam(value)->GetDisplayForHost((double) opt, true, (char*) ptr);
        }
        return 0xbeef;
      }

      if (idx == kVstParameterUsesIntStep)
      {
        if (value >= 0 && value < _this->NParams())
        {
          if (_this->GetParam(value)->Type() != IParam::kTypeDouble)
          {
            return 0xbeef;
          }
        }
      }

      return 0;
    }
    case effGetProgram:
    {
      return _this->GetCurrentPresetIdx();
    }
    case effSetProgram:
    {
      if (_this->DoesStateChunks() == false)
      {
        _this->ModifyCurrentPreset(); // TODO: test, something is funny about this http://forum.cockos.com/showpost.php?p=485113&postcount=22
      }
      _this->RestorePreset((int) value);
      return 0;
    }
    case effGetProgramNameIndexed:
    {
      strcpy((char*) ptr, _this->GetPresetName(idx));
      return (CSTR_NOT_EMPTY((char*) ptr) ? 1 : 0);
    }
    case effSetProgramName:
    {
      if (ptr)
      {
        _this->ModifyCurrentPreset((char*) ptr);
        _this->PresetsChangedByHost();
      }
      return 0;
    }
    case effGetProgramName:
    {
      if (ptr)
      {
        int idx = _this->GetCurrentPresetIdx();
        strcpy((char*) ptr, _this->GetPresetName(idx));
      }
      return 0;
    }
    case effGetMidiKeyName:
    {
      if (ptr)
      {
        MidiKeyName* pMKN = (MidiKeyName*) ptr;
        pMKN->keyName[0] = '\0';
        if (_this->MidiNoteName(pMKN->thisKeyNumber, pMKN->keyName))
        {
          return 1;
        }
      }
      return 0;
    }
    case effGetVstVersion:
    {
      return VST_VERSION;
    }
    case effEndSetProgram:
    case effBeginSetProgram:
    case effGetMidiProgramName:
    case effHasMidiProgramsChanged:
    case effGetMidiProgramCategory:
    case effGetCurrentMidiProgram:
    case effSetBypass:
    default:
    {
      return 0;
    }
  }
}
Beispiel #8
0
VstIntPtr VSTCALLBACK IPlugVST::VSTDispatcher(AEffect *pEffect, VstInt32 opCode, VstInt32 idx, VstIntPtr value, void *ptr, float opt)
{
	// VSTDispatcher is an IPlugVST class member, we can access anything in IPlugVST from here.
	IPlugVST* _this = (IPlugVST*) pEffect->object;
	if (!_this) {
		return 0;
	}
  IPlugBase::IMutexLock lock(_this);

  // Handle a couple of opcodes here to make debugging easier.
  switch (opCode) {
    case effEditIdle:
    case __effIdleDeprecated:
      #ifdef USE_IDLE_CALLS
        _this->OnIdle();
      #endif
    	return 0;
  }

  Trace(TRACELOC, "%d(%s):%d:%d", opCode, VSTOpcodeStr(opCode), idx, (int) value);

  switch (opCode) {

    case effOpen: {
      _this->HostSpecificInit();
	    _this->OnParamReset();
	    return 0;
    }
    case effClose: {
      lock.Destroy();
	    DELETE_NULL(_this);
	    return 0;
    }
    case effGetParamLabel: {
      if (idx >= 0 && idx < _this->NParams())
      {
	      strcpy((char*) ptr, _this->GetParam(idx)->GetLabelForHost());
      }
      return 0;
    }
    case effGetParamDisplay: {
      if (idx >= 0 && idx < _this->NParams())
      {
	      _this->GetParam(idx)->GetDisplayForHost((char*) ptr);
      }
	    return 0;
    }
    case effGetParamName: {
      if (idx >= 0 && idx < _this->NParams())
      {
	      strcpy((char*) ptr, _this->GetParam(idx)->GetNameForHost());      
      }
	    return 0;
    }
    case effString2Parameter:
    {
      if (idx >= 0 && idx < _this->NParams())
      {
        if (ptr)
        {
          IParam* pParam = _this->GetParam(idx);
          double v = VSTString2Parameter(pParam, (char*)ptr);
          if (_this->GetGUI()) _this->GetGUI()->SetParameterFromPlug(idx, v, false);
          pParam->Set(v);
          _this->OnParamChange(idx);
        }
        return 1;
      }
      return 0;
    }
    case effSetSampleRate: {
	    _this->SetSampleRate(opt);
	    _this->Reset();
	    return 0;
    }
    case effSetBlockSize: {
	    _this->SetBlockSize(value);
	    _this->Reset();
	    return 0;
    }
    case effMainsChanged: {
      if (!value) {
        _this->OnActivate(false);
		    _this->Reset();
	    }
      else {
        _this->OnActivate(true);
      }
	    return 0;
    }
    case effEditGetRect: {
	    if (ptr && _this->GetGUI()) {
		    *(ERect**) ptr = &(_this->mEditRect);
		    return 1;
	    }
	    ptr = 0;
	    return 0;
    }
    case effEditOpen:
    {
      IGraphics* pGraphics = _this->GetGUI();
	    if (pGraphics)
      {
#if defined(_WIN32) || defined(IPLUG_NO_CARBON_SUPPORT)
        if (!pGraphics->OpenWindow(ptr)) pGraphics=0;
#else   // OSX, check if we are in a Cocoa VST host
        bool iscocoa = (_this->mHasVSTExtensions&VSTEXT_COCOA);
        if (iscocoa && !pGraphics->OpenWindow(ptr)) pGraphics=0;
        if (!iscocoa && !pGraphics->OpenWindow(ptr, 0)) pGraphics=0;
#endif
        if (pGraphics)
        {
          _this->OnGUIOpen();
          return 1;
        }
	    }
	    return 0;
    }
    case effEditClose: {
	    if (_this->GetGUI()) {
		    _this->OnGUIClose();
        _this->GetGUI()->CloseWindow();  
		    return 1;
	    }
	    return 0;
    }
    case __effIdentifyDeprecated: {
      return 'NvEf';  // Random deprecated magic.
    }
    case effGetChunk: {
	    BYTE** ppData = (BYTE**) ptr;
      if (ppData) {
        bool isBank = (!idx);
        ByteChunk* pChunk = (isBank ? &(_this->mBankState) : &(_this->mState));
        InitializeVSTChunk(pChunk);
        bool savedOK = true;
        if (isBank) {
          _this->ModifyCurrentPreset();
          savedOK = _this->SerializePresets(pChunk);
          //savedOK = _this->SerializeState(pChunk);
        }
        else {
          savedOK = _this->SerializeState(pChunk);
        }
        if (savedOK && pChunk->Size()) {
          *ppData = pChunk->GetBytes();
          return pChunk->Size();
        }
      }
      return 0;
    }
    case effSetChunk: {
      if (ptr) {
        bool isBank = (!idx);
        ByteChunk* pChunk = (isBank ? &(_this->mBankState) : &(_this->mState));
        pChunk->Resize(value);
        memcpy(pChunk->GetBytes(), ptr, value);
        int pos = 0;
        int iplugVer = GetIPlugVerFromChunk(pChunk, &pos);
        isBank &= (iplugVer >= 0x010000);
        if (isBank) {
          pos = _this->UnserializePresets(pChunk, pos);
          //pos = _this->UnserializeState(pChunk, pos);
        }
        else {
          pos = _this->UnserializeState(pChunk, pos);
          _this->ModifyCurrentPreset();
        }
        if (pos >= 0) {
          _this->RedrawParamControls();
		      return 1;
	      }
      }
	    return 0;
    }
    case effProcessEvents: {
	    VstEvents* pEvents = (VstEvents*) ptr;
	    if (pEvents && pEvents->events) {
		    for (int i = 0; i < pEvents->numEvents; ++i) {
          VstEvent* pEvent = pEvents->events[i];
			    if (pEvent) {
				    if (pEvent->type == kVstMidiType) {
					    VstMidiEvent* pME = (VstMidiEvent*) pEvent;
              IMidiMsg msg(pME->deltaFrames, pME->midiData[0], pME->midiData[1], pME->midiData[2]);
              _this->ProcessMidiMsg(&msg);
              //#ifdef TRACER_BUILD
              //  msg.LogMsg();
              //#endif
				    }
				    else if (pEvent->type == kVstSysExType) {
				        VstMidiSysexEvent* pSE = (VstMidiSysexEvent*) pEvent;
				        ISysEx sysex(pSE->deltaFrames, (const BYTE*)pSE->sysexDump, pSE->dumpBytes);
				        _this->ProcessSysEx(&sysex);
				    }
			    }
		    }
		    return 1;
	    }
	    return 0;
    }
	  case effCanBeAutomated: {
	  	return 1;
    }
	  case effGetInputProperties: {
      if (ptr && idx >= 0 && idx < _this->NInChannels()) {
        VstPinProperties* pp = (VstPinProperties*) ptr;
        pp->flags = kVstPinIsActive;
        if (!(idx%2) && idx < _this->NInChannels()-1)
        {
          pp->flags |= kVstPinIsStereo;
        }
        sprintf(pp->label, "Input %d", idx + 1);
        return 1;
      }
      return 0;
    }
    case effGetOutputProperties: {
	    if (ptr && idx >= 0 && idx < _this->NOutChannels()) {
		    VstPinProperties* pp = (VstPinProperties*) ptr;
			  pp->flags = kVstPinIsActive;
        if (!(idx%2) && idx < _this->NOutChannels()-1)
        {
			  	pp->flags |= kVstPinIsStereo;
			  }
		    sprintf(pp->label, "Output %d", idx + 1);
		    return 1;
	    }
	    return 0;
    }
    case effGetPlugCategory: {
      if (_this->IsInst()) return kPlugCategSynth;
	    return kPlugCategEffect;
    }
    case effProcessVarIo: {
	    // VstVariableIo* pIO = (VstVariableIo*) ptr;		// For offline processing (of audio files?)
	    return 0;
    }
    case effSetSpeakerArrangement: {
	    VstSpeakerArrangement* pInputArr = (VstSpeakerArrangement*) value;
	    VstSpeakerArrangement* pOutputArr = (VstSpeakerArrangement*) ptr;
	    if (pInputArr) {
        int n = pInputArr->numChannels;
        _this->SetInputChannelConnections(0, n, true);
        _this->SetInputChannelConnections(n, _this->NInChannels() - n, false);
      }
	    if (pOutputArr) {
        int n = pOutputArr->numChannels;
        _this->SetOutputChannelConnections(0, n, true);
        _this->SetOutputChannelConnections(n, _this->NOutChannels() - n, false);
	    }
	    return 1;
    }
    case effGetSpeakerArrangement: {
	    VstSpeakerArrangement** ppInputArr = (VstSpeakerArrangement**) value;
	    VstSpeakerArrangement** ppOutputArr = (VstSpeakerArrangement**) ptr;
      if (ppInputArr) {
        *ppInputArr = &(_this->mInputSpkrArr);
      }
      if (ppOutputArr) {
        *ppOutputArr = &(_this->mOutputSpkrArr);
      }
      return 1;
    }
    case effGetEffectName: {
	    if (ptr) {
		    strcpy((char*) ptr, _this->GetEffectName());
 		    return 1;
	    }
	    return 0;
    }
    case effGetProductString: {
	    if (ptr) {
		    strcpy((char*) ptr, _this->GetProductName());
		    return 1;
	    }
	    return 0;
    }
    case effGetVendorString: {
	    if (ptr) {
		    strcpy((char*) ptr, _this->GetMfrName());
		    return 1;
	    }
	    return 0;
    }
    case effGetVendorVersion: {
      return _this->GetEffectVersion(true);
    }
    case effCanDo: {
	    if (ptr) {
        Trace(TRACELOC, "VSTCanDo(%s)", (char*) ptr);
        if (!strcmp((char*) ptr, "receiveVstTimeInfo")) {
          return 1;
        }
        if (_this->DoesMIDI()) {
          if (_this->DoesMIDI() & 1) {
            if (!strcmp((char*) ptr, "sendVstEvents") ||
                !strcmp((char*) ptr, "sendVstMidiEvent")) {
              return 1;
            }
          }
          if (_this->DoesMIDI() <= 2) {
            if (!strcmp((char*) ptr, "receiveVstEvents") ||
                !strcmp((char*) ptr, "receiveVstMidiEvent")) {
              return 1;
            }
          }
          //if (!strcmp((char*) ptr, "midiProgramNames")) {
          //  return 1;
          //}
        }
        // Support Reaper VST extensions: http://www.reaper.fm/sdk/vst/
        if (!strcmp((char*) ptr, "hasCockosExtensions"))
        {
          _this->mHasVSTExtensions |= VSTEXT_COCKOS;
          return 0xbeef0000;
        }
        else if (!strcmp((char*) ptr, "hasCockosViewAsConfig")) 
        {
          _this->mHasVSTExtensions |= VSTEXT_COCOA;
          return 0xbeef0000; 
        }
      }
	    return 0;
    }
    case effVendorSpecific: {
      switch (idx) {
        // Mouse wheel
        case 0x73744341: {
          if (value == 0x57686565) {
            IGraphics* pGraphics = _this->GetGUI();
            if (pGraphics) {
              return pGraphics->ProcessMouseWheel(opt);
            }
          }
          break;
        }
        // Support Reaper VST extensions: http://www.reaper.fm/sdk/vst/
        case effGetParamDisplay: {
          if (ptr) {
            if (value >= 0 && value < _this->NParams()) {
              _this->GetParam(value)->GetDisplayForHost((double) opt, true, (char*) ptr);
            }
            return 0xbeef;
          }
          break;
        }
        case effString2Parameter: {
          if (ptr && value >= 0 && value < _this->NParams()) {
            if (*(char*) ptr != '\0') {
              IParam* pParam = _this->GetParam(value);
              sprintf((char*) ptr, "%.17f", pParam->GetNormalized(VSTString2Parameter(pParam, (char*) ptr)));
            }
            return 0xbeef;
          }
          break;
        }
        case kVstParameterUsesIntStep: {
          if (value >= 0 && value < _this->NParams()) {
            IParam* pParam = _this->GetParam(value);
            switch (pParam->Type()) {
              case IParam::kTypeBool: {
                return 0xbeef;
              }
              case IParam::kTypeInt:
              case IParam::kTypeEnum: {
                double min, max;
                pParam->GetBounds(&min, &max);
                if (fabs(max - min) < 1.5) {
                  return 0xbeef;
                }
                break;
              }
            }
          }
          break;
        }
      }
      return 0;
    }
    case effGetProgram: {
      return _this->GetCurrentPresetIdx();
    }
    case effSetProgram: {
      //if (!(_this->DoesStateChunks())) {
        _this->ModifyCurrentPreset();
      //}
      _this->RestorePreset((int) value);
      return 0;
    }
    case effGetProgramNameIndexed: {
      strcpy((char*) ptr, _this->GetPresetName(idx));
      return (CSTR_NOT_EMPTY((char*) ptr) ? 1 : 0);
    }
    case effSetProgramName: {
      if (ptr) {
        _this->ModifyCurrentPreset((char*) ptr);
      }
      return 0;
    }
    case effGetProgramName: {
      if (ptr) {
        int idx = _this->GetCurrentPresetIdx();      
        strcpy((char*) ptr, _this->GetPresetName(idx));
      }
      return 0;
    }
    case effGetMidiKeyName: {
	    if (ptr) {
		    MidiKeyName* pMKN = (MidiKeyName*) ptr;
		    pMKN->keyName[0] = '\0';
		    if (_this->MidiNoteName(pMKN->thisKeyNumber, pMKN->keyName)) {
			    return 1;
		    }
	    }
	    return 0;
    }
    case effGetVstVersion: {
	    return VST_VERSION;
    }
    case effBeginSetProgram:
    case effEndSetProgram:
    case effGetMidiProgramName: 
    case effHasMidiProgramsChanged:
    case effGetMidiProgramCategory: 
    case effGetCurrentMidiProgram:
    case effSetBypass:
    default: {
	    return 0;
    }
	}
}
Beispiel #9
0
static int readtrack(void) /* read a track chunk */
{
    /* This array is indexed by the high half of a status byte.  It's */
    /* value is either the number of bytes needed (1 or 2) for a channel */
    /* message, or 0 (meaning it's not  a channel message). */
    static int chantype[] = {
        0, 0, 0, 0, 0, 0, 0, 0,		/* 0x00 through 0x70 */
        2, 2, 2, 2, 1, 1, 2, 0		/* 0x80 through 0xf0 */
    };
    long lookfor;
    int c, c1, type;
    int sysexcontinue = 0; /* 1 if last message was an unfinished sysex */
    int running = 0;       /* 1 when running status used */
    int status = 0;        /* status value (e.g. 0x90==note-on) */
    int needed;

    if (readmt("MTrk") == EOF)
        return(0);

    Mf_toberead = read32bit();
    Mf_currtime = 0;

    if (Mf_starttrack)
        (*Mf_starttrack)();

    while (Mf_toberead > 0) {
        Mf_currtime += readvarinum();	/* delta time */

        c = egetc();

        if (sysexcontinue && c != 0xf7)
            mferror("didn't find expected continuation of a sysex");

        if ((c & 0x80) == 0) {	 /* running status? */
            if (status == 0)
                mferror("unexpected running status");
            running = 1;
            c1 = c;
            c = status;
        } else if (c < 0xf0) {
            status = c;
            running = 0;
        }

        needed = chantype[(c>>4) & 0xf];

        if (needed) { /* ie. is it a channel message? */
            if (! running)
                c1 = egetc();
            chanmessage(status, c1, (needed>1) ? egetc() : 0);
            continue;;
        }

        switch (c) {
            case 0xff:			/* meta event */
                type = egetc();
                lookfor = Mf_toberead - readvarinum();
                msginit();

                while (Mf_toberead > lookfor)
                    msgadd(egetc());

                metaevent(type);
                break;

            case 0xf0:		/* start of system exclusive */
                lookfor = Mf_toberead - readvarinum();
                msginit();
                msgadd(0xf0);

                while (Mf_toberead > lookfor)
                    msgadd(c = egetc());

                if (c == 0xf7 || Mf_nomerge == 0)
                    sysex();
                else
                    sysexcontinue = 1;  /* merge into next msg */
                break;

            case 0xf7:	/* sysex continuation or arbitrary stuff */
                lookfor = Mf_toberead - readvarinum();

                if (! sysexcontinue)
                    msginit();

                while (Mf_toberead > lookfor)
                    msgadd(c=egetc());

                if ( ! sysexcontinue ) {
                    if (Mf_arbitrary)
                        (*Mf_arbitrary)(msgleng(),msg());
                } else if (c == 0xf7) {
                    sysex();
                    sysexcontinue = 0;
                }
                break;
            default:
                badbyte(c);
                break;
        }
    }

    if (Mf_endtrack)
        (*Mf_endtrack)();
    return(1);
}
Beispiel #10
0
/* Read a MIDI event, returning a freshly allocated element that can
   be linked to the event list */
static MidiEventList *read_midi_event(struct md *d)
{
  static uint8 laststatus, lastchan;
  static uint8 nrpn=0, rpn_msb[MAXCHAN], rpn_lsb[MAXCHAN]; /* one per channel */
  uint8 me, type, a,b,c;
  int32 len, i;
  MidiEventList *newev;

  for (;;)
    {
#ifdef tplus
      if ( (len=getvl(d)) < 0) return 0;
      d->at+= len;
#else
      d->at+=getvl(d);
#endif

#ifdef tplus
      if((i = tf_getc(d)) == EOF)
	{
	    if(errno)
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "\"%s\": read_midi_event: %s",
			  d->midi_name, strerror(errno));
	    else
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "Warning: \"%s\": Short midi file.",
			  d->midi_name);
	    return 0;
	}
      me = (uint8)i;
#else
      if (fread(&me,1,1,d->fp)!=1)
	{
	  ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "\"%s\": read_midi_event: %s", 
	       d->midi_name, sys_errlist[errno]);
	  return 0;
	}
#endif
      
      if(me==0xF0 || me == 0xF7) /* SysEx event */
	{
	  int32 sret;
	  uint8 sysa=0, sysb=0, syschan=0;
#ifdef tplus
          if ( (len=getvl(d)) < 0) return 0;
#else
	  len=getvl(d);
#endif
	  sret=sysex(len, &syschan, &sysa, &sysb, d);
	  if (sret)
	   {
	     MIDIEVENT(d->at, sret, syschan, sysa, sysb);
	   }
	}
      else if(me==0xFF) /* Meta event */
	{
#ifdef tplus
      if((i = tf_getc(d)) == EOF)
	{
	    if(errno)
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "\"%s\": read_midi_event: %s",
			  d->midi_name, strerror(errno));
	    else
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "Warning: \"%s\": Short midi file.",
			  d->midi_name);
	    return 0;
	}
      type = (uint8)i;
#else
	  fread(&type,1,1,d->fp);
#endif
#ifdef tplus
          if ( (len=getvl(d)) < 0) return 0;
#else
	  len=getvl(d);
#endif
	  if (type>0 && type<16)
	    {
	      static const char *label[]={
		"text: ", "text: ", "Copyright: ", "track: ",
		"instrument: ", "lyric: ", "marker: ", "cue point: "};
	      dumpstring(len, label[(type>7) ? 0 : type], type, d);
	    }
	  else
	    switch(type)
	      {

	      case 0x21: /* MIDI port number */
		if(len == 1)
		{
	  	    fread(&d->midi_port_number,1,1,d->fp);
		    if(d->midi_port_number == EOF)
		    {
			    ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
				      "Warning: \"%s\": Short midi file.",
				      d->midi_name);
			    return 0;
		    }
		    d->midi_port_number &= 0x0f;
		    if (d->midi_port_number)
			ctl->cmsg(CMSG_INFO, VERB_VERBOSE,
			  "(MIDI port number %d)", d->midi_port_number);
		    d->midi_port_number &= 0x03;
		}
		else skip(d->fp, len);
		break;

	      case 0x2F: /* End of Track */
		return MAGIC_EOT;

	      case 0x51: /* Tempo */
#ifdef tplus
      if((i = tf_getc(d)) == EOF)
	{
	    if(errno)
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "\"%s\": read_midi_event: %s",
			  d->midi_name, strerror(errno));
	    else
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "Warning: \"%s\": Short midi file.",
			  d->midi_name);
	    return 0;
	}
      a = (uint8)i;
      if((i = tf_getc(d)) == EOF)
	{
	    if(errno)
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "\"%s\": read_midi_event: %s",
			  d->midi_name, strerror(errno));
	    else
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "Warning: \"%s\": Short midi file.",
			  d->midi_name);
	    return 0;
	}
      b = (uint8)i;
      if((i = tf_getc(d)) == EOF)
	{
	    if(errno)
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "\"%s\": read_midi_event: %s",
			  d->midi_name, strerror(errno));
	    else
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "Warning: \"%s\": Short midi file.",
			  d->midi_name);
	    return 0;
	}
      c = (uint8)i;
#else
		fread(&a,1,1,d->fp); fread(&b,1,1,d->fp); fread(&c,1,1,d->fp);
#endif
		MIDIEVENT(d->at, ME_TEMPO, c, a, b);
		
	      default:
		ctl->cmsg(CMSG_INFO, VERB_DEBUG, 
		     "(Meta event type 0x%02x, length %ld)", type, len);
		skip(d->fp, len);
		break;
	      }
	}
      else
	{
	  a=me;
	  if (a & 0x80) /* status byte */
	    {
	      lastchan = MERGE_CHANNEL_PORT(a & 0x0F);
	      laststatus=(a>>4) & 0x07;
#ifdef tplus
      if((i = tf_getc(d)) == EOF)
	{
	    if(errno)
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "\"%s\": read_midi_event: %s",
			  d->midi_name, strerror(errno));
	    else
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "Warning: \"%s\": Short midi file.",
			  d->midi_name);
	    return 0;
	}
      a = (uint8)i;
#else
	      fread(&a, 1,1, d->fp);
#endif
	      a &= 0x7F;
	    }
	  switch(laststatus)
	    {
	    case 0: /* Note off */
#ifdef tplus
      if((i = tf_getc(d)) == EOF)
	{
	    if(errno)
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "\"%s\": read_midi_event: %s",
			  d->midi_name, strerror(errno));
	    else
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "Warning: \"%s\": Short midi file.",
			  d->midi_name);
	    return 0;
	}
      b = (uint8)i;
#else
	      fread(&b, 1,1, d->fp);
#endif
	      b &= 0x7F;
	      /*MIDIEVENT(d->at, ME_NOTEOFF, lastchan, a,b);*/
	      MIDIEVENT(d->at, ME_NOTEON, lastchan, a,0);

	    case 1: /* Note on */
#ifdef tplus
      if((i = tf_getc(d)) == EOF)
	{
	    if(errno)
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "\"%s\": read_midi_event: %s",
			  d->midi_name, strerror(errno));
	    else
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "Warning: \"%s\": Short midi file.",
			  d->midi_name);
	    return 0;
	}
      b = (uint8)i;
#else
	      fread(&b, 1,1, d->fp);
#endif
	      b &= 0x7F;
	      MIDIEVENT(d->at, ME_NOTEON, lastchan, a,b);

	      if (d->curr_track == d->curr_title_track && d->track_info > 1) d->title[0] = '\0';

	    case 2: /* Key Pressure */
#ifdef tplus
      if((i = tf_getc(d)) == EOF)
	{
	    if(errno)
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "\"%s\": read_midi_event: %s",
			  d->midi_name, strerror(errno));
	    else
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "Warning: \"%s\": Short midi file.",
			  d->midi_name);
	    return 0;
	}
      b = (uint8)i;
#else
	      fread(&b, 1,1, d->fp);
#endif
	      b &= 0x7F;
	      MIDIEVENT(d->at, ME_KEYPRESSURE, lastchan, a, b);

	    case 3: /* Control change */
#ifdef tplus
      if((i = tf_getc(d)) == EOF)
	{
	    if(errno)
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "\"%s\": read_midi_event: %s",
			  d->midi_name, strerror(errno));
	    else
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "Warning: \"%s\": Short midi file.",
			  d->midi_name);
	    return 0;
	}
      b = (uint8)i;
#else
	      fread(&b, 1,1, d->fp);
#endif
	      b &= 0x7F;
	      {
		int control=255;
		switch(a)
		  {
#ifdef tplus
		  case 1: control=ME_MODULATION_WHEEL; break;
		  case 5: control=ME_PORTAMENTO_TIME_MSB; break;
		  case 37: control=ME_PORTAMENTO_TIME_LSB; break;
		  case 65: control=ME_PORTAMENTO; break;
#endif
		  case 7: control=ME_MAINVOLUME; break;
		  case 10: control=ME_PAN; break;
		  case 11: control=ME_EXPRESSION; break;
#ifdef tplus
		  case 64: control=ME_SUSTAIN; b = (b >= 64); break;
#else
		  case 64: control=ME_SUSTAIN; break;
#endif
		  case 71: control=ME_HARMONICCONTENT; break;
		  case 72: control=ME_RELEASETIME; break;
		  case 73: control=ME_ATTACKTIME; break;
		  case 74: control=ME_BRIGHTNESS; break;
		  case 91: control=ME_REVERBERATION; break;
		  case 93: control=ME_CHORUSDEPTH; break;
#ifdef CHANNEL_EFFECT
		  case 94: control=ME_CELESTE; break;
ctl->cmsg(CMSG_INFO, VERB_NORMAL, "CELESTE");
		  case 95: control=ME_PHASER; break;
ctl->cmsg(CMSG_INFO, VERB_NORMAL, "PHASER");
#endif
		  case 120: control=ME_ALL_SOUNDS_OFF; break;
		  case 121: control=ME_RESET_CONTROLLERS; break;
		  case 123: control=ME_ALL_NOTES_OFF; break;

		    /* These should be the SCC-1 tone bank switch
		       commands. I don't know why there are two, or
		       why the latter only allows switching to bank 0.
		       Also, some MIDI files use 0 as some sort of
		       continuous controller. This will cause lots of
		       warnings about undefined tone banks. */
		  case 0:
		    if (d->XG_System_On)
		    	control=ME_TONE_KIT;
		    else control=ME_TONE_BANK;
		    break;
		  case 32:
	            if (d->XG_System_On) control=ME_TONE_BANK;
		    break;

		  case 100: nrpn=0; rpn_msb[lastchan]=b; break;
		  case 101: nrpn=0; rpn_lsb[lastchan]=b; break;
		  case 98: nrpn=1; rpn_lsb[lastchan]=b; break;
		  case 99: nrpn=1; rpn_msb[lastchan]=b; break;
		  case 6:
		    if (nrpn)
		      {
			if (rpn_msb[lastchan]==1) switch (rpn_lsb[lastchan])
			 {
#ifdef tplus
			   case 0x08: control=ME_VIBRATO_RATE; break;
			   case 0x09: control=ME_VIBRATO_DEPTH; break;
			   case 0x0a: control=ME_VIBRATO_DELAY; break;
#endif
			   case 0x20: control=ME_BRIGHTNESS; break;
			   case 0x21: control=ME_HARMONICCONTENT; break;
			/*
			   case 0x63: envelope attack rate
			   case 0x64: envelope decay rate
			   case 0x66: envelope release rate
			*/
			 }
			else switch (rpn_msb[lastchan])
			 {
			/*
			   case 0x14: filter cutoff frequency
			   case 0x15: filter resonance
			   case 0x16: envelope attack rate
			   case 0x17: envelope decay rate
			   case 0x18: pitch coarse
			   case 0x19: pitch fine
			*/
			   case 0x1a: d->drumvolume[lastchan][0x7f & rpn_lsb[lastchan]] = b; break;
			   case 0x1c:
			     if (!b) b=(int) (127.0*rand()/(RAND_MAX));
			     d->drumpanpot[lastchan][0x7f & rpn_lsb[lastchan]] = b;
			     break;
			   case 0x1d: d->drumreverberation[lastchan][0x7f & rpn_lsb[lastchan]] = b; break;
			   case 0x1e: d->drumchorusdepth[lastchan][0x7f & rpn_lsb[lastchan]] = b; break;
			/*
			   case 0x1f: variation send level
			*/
			 }
			ctl->cmsg(CMSG_INFO, VERB_DEBUG, 
				  "(Data entry (MSB) for NRPN %02x,%02x: %ld)",
				  rpn_msb[lastchan], rpn_lsb[lastchan],
				  b);
			break;
		      }
		    
		    switch((rpn_msb[lastchan]<<8) | rpn_lsb[lastchan])
		      {
		      case 0x0000: /* Pitch bend sensitivity */
			control=ME_PITCH_SENS;
			break;
#ifdef tplus
		      case 0x0001: control=ME_FINE_TUNING; break;
		      case 0x0002: control=ME_COARSE_TUNING; break;
#endif

		      case 0x7F7F: /* RPN reset */
			/* reset pitch bend sensitivity to 2 */
			MIDIEVENT(d->at, ME_PITCH_SENS, lastchan, 2, 0);

		      default:
			ctl->cmsg(CMSG_INFO, VERB_DEBUG, 
				  "(Data entry (MSB) for RPN %02x,%02x: %ld)",
				  rpn_msb[lastchan], rpn_lsb[lastchan],
				  b);
			break;
		      }
		    break;
		    
		  default:
		    ctl->cmsg(CMSG_INFO, VERB_DEBUG, 
			      "(Control %d: %d)", a, b);
		    break;
		  }
		if (control != 255)
		  { 
		    MIDIEVENT(d->at, control, lastchan, b, 0); 
		  }
	      }
	      break;

	    case 4: /* Program change */
	      a &= 0x7f;
	      MIDIEVENT(d->at, ME_PROGRAM, lastchan, a, 0);

#ifdef tplus
	    case 5: /* Channel pressure */
	      MIDIEVENT(d->at, ME_CHANNEL_PRESSURE, lastchan, a, 0);
#else
	    case 5: /* Channel pressure - NOT IMPLEMENTED */
	      break;
#endif

	    case 6: /* Pitch wheel */
#ifdef tplus
      if((i = tf_getc(d)) == EOF)
	{
	    if(errno)
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "\"%s\": read_midi_event: %s",
			  d->midi_name, strerror(errno));
	    else
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
			  "Warning: \"%s\": Short midi file.",
			  d->midi_name);
	    return 0;
	}
      b = (uint8)i;
#else
	      fread(&b, 1,1, d->fp);
#endif
	      b &= 0x7F;
	      MIDIEVENT(d->at, ME_PITCHWHEEL, lastchan, a, b);

	    case 7:
	      break;

	    default: 
	      ctl->cmsg(CMSG_ERROR, VERB_NORMAL, 
		   "*** Can't happen: status 0x%02X, channel 0x%02X",
		   laststatus, lastchan);
	      break;
	    }
	}
    }