/[svn]/linuxsampler/trunk/src/drivers/midi/MidiInputPort.cpp
ViewVC logotype

Diff of /linuxsampler/trunk/src/drivers/midi/MidiInputPort.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 2317 by persson, Sun Feb 19 12:13:19 2012 UTC revision 2500 by schoenebeck, Fri Jan 10 12:20:05 2014 UTC
# Line 3  Line 3 
3   *   LinuxSampler - modular, streaming capable sampler                     *   *   LinuxSampler - modular, streaming capable sampler                     *
4   *                                                                         *   *                                                                         *
5   *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *   *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *
6   *   Copyright (C) 2005 - 2012 Christian Schoenebeck                       *   *   Copyright (C) 2005 - 2014 Christian Schoenebeck                       *
7   *                                                                         *   *                                                                         *
8   *   This program is free software; you can redistribute it and/or modify  *   *   This program is free software; you can redistribute it and/or modify  *
9   *   it under the terms of the GNU General Public License as published by  *   *   it under the terms of the GNU General Public License as published by  *
# Line 77  namespace LinuxSampler { Line 77  namespace LinuxSampler {
77      MidiInputPort::MidiInputPort(MidiInputDevice* pDevice, int portNumber)      MidiInputPort::MidiInputPort(MidiInputDevice* pDevice, int portNumber)
78          : MidiChannelMapReader(MidiChannelMap),          : MidiChannelMapReader(MidiChannelMap),
79            SysexListenersReader(SysexListeners),            SysexListenersReader(SysexListeners),
80            virtualMidiDevicesReader(virtualMidiDevices) {            virtualMidiDevicesReader(virtualMidiDevices),
81              noteOnVelocityFilterReader(noteOnVelocityFilter)
82        {
83          this->pDevice = pDevice;          this->pDevice = pDevice;
84          this->portNumber = portNumber;          this->portNumber = portNumber;
85            runningStatusBuf[0] = 0;
86          Parameters["NAME"] = new ParameterName(this);          Parameters["NAME"] = new ParameterName(this);
87      }      }
88    
# Line 97  namespace LinuxSampler { Line 100  namespace LinuxSampler {
100    
101      void MidiInputPort::DispatchNoteOn(uint8_t Key, uint8_t Velocity, uint MidiChannel) {      void MidiInputPort::DispatchNoteOn(uint8_t Key, uint8_t Velocity, uint MidiChannel) {
102          if (Key > 127 || Velocity > 127 || MidiChannel > 16) return;          if (Key > 127 || Velocity > 127 || MidiChannel > 16) return;
103            
104            // apply velocity filter (if any)
105            const std::vector<uint8_t>& velocityFilter = noteOnVelocityFilterReader.Lock();
106            if (!velocityFilter.empty()) Velocity = velocityFilter[Velocity];
107            noteOnVelocityFilterReader.Unlock();
108            
109          const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock();          const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock();
110          // dispatch event for engines listening to the same MIDI channel          // dispatch event for engines listening to the same MIDI channel
111          {          {
# Line 122  namespace LinuxSampler { Line 131  namespace LinuxSampler {
131    
132      void MidiInputPort::DispatchNoteOn(uint8_t Key, uint8_t Velocity, uint MidiChannel, int32_t FragmentPos) {      void MidiInputPort::DispatchNoteOn(uint8_t Key, uint8_t Velocity, uint MidiChannel, int32_t FragmentPos) {
133          if (Key > 127 || Velocity > 127 || MidiChannel > 16) return;          if (Key > 127 || Velocity > 127 || MidiChannel > 16) return;
134            
135            // apply velocity filter (if any)
136            const std::vector<uint8_t>& velocityFilter = noteOnVelocityFilterReader.Lock();
137            if (!velocityFilter.empty()) Velocity = velocityFilter[Velocity];
138            noteOnVelocityFilterReader.Unlock();
139            
140          const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock();          const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock();
141          // dispatch event for engines listening to the same MIDI channel          // dispatch event for engines listening to the same MIDI channel
142          {          {
# Line 363  namespace LinuxSampler { Line 378  namespace LinuxSampler {
378          }          }
379          MidiChannelMapReader.Unlock();          MidiChannelMapReader.Unlock();
380      }      }
381        
382        /**
383         * Handles the so called MIDI "running status" mode, which allows devices
384         * to reduce bandwidth (data reduction).
385         *
386         * If the passed in MIDI data is regular MIDI data, this method will simply
387         * return the original data pointer and just stores the status byte for
388         * potential "running status" event eventually coming next.
389         *
390         * If the passed in MIDI data however seems to be in "running status" mode,
391         * this method will return another buffer, which allows the MIDI parser
392         * to handle the MIDI data as usually with "normal" MIDI data.
393         */
394        uint8_t* MidiInputPort::handleRunningStatus(uint8_t* pData) {
395            if ((pData[0] & 0x80) || !runningStatusBuf[0]) {
396                // store status byte for eventual "running status" in next event
397                if (pData[0] & 0x80) {
398                    if (pData[0] < 0xf0) {
399                        // "running status" is only allowed for channel messages
400                        runningStatusBuf[0] = pData[0];
401                    } else if (pData[0] < 0xf8) {
402                        // "system common" messages (0xf0..0xf7) shall reset any running
403                        // status, however "realtime" messages (0xf8..0xff) shall be
404                        // ignored here
405                        runningStatusBuf[0] = 0;
406                    }
407                }
408                // it's either a regular status byte, or some invalid "running status"
409                return pData;
410            } else { // "running status" mode ...
411                const uint8_t type = runningStatusBuf[0] & 0xf0;
412                const int size = (type == 0xc0 || type == 0xd0) ? 1 : 2; // only program change & channel pressure have 1 data bytes
413                memcpy(&runningStatusBuf[1], pData, size);
414                return runningStatusBuf;
415            }
416        }
417    
418      void MidiInputPort::DispatchRaw(uint8_t* pData) {      void MidiInputPort::DispatchRaw(uint8_t* pData) {
419            pData = handleRunningStatus(pData);
420            
421          uint8_t channel = pData[0] & 0x0f;          uint8_t channel = pData[0] & 0x0f;
422          switch (pData[0] & 0xf0) {          switch (pData[0] & 0xf0) {
423          case 0x80:          case 0x80:
# Line 398  namespace LinuxSampler { Line 451  namespace LinuxSampler {
451      }      }
452    
453      void MidiInputPort::DispatchRaw(uint8_t* pData, int32_t FragmentPos) {      void MidiInputPort::DispatchRaw(uint8_t* pData, int32_t FragmentPos) {
454            pData = handleRunningStatus(pData);
455            
456          uint8_t channel = pData[0] & 0x0f;          uint8_t channel = pData[0] & 0x0f;
457          switch (pData[0] & 0xf0) {          switch (pData[0] & 0xf0) {
458          case 0x80:          case 0x80:
# Line 429  namespace LinuxSampler { Line 484  namespace LinuxSampler {
484              break;              break;
485          }          }
486      }      }
487        
488        void MidiInputPort::SetNoteOnVelocityFilter(const std::vector<uint8_t>& filter) {
489            if (filter.size() != 128 && filter.size() != 0)
490                throw MidiInputException("Note on velocity filter must be either of size 128 or 0");
491            
492            // check the value range of the filter
493            if (!filter.empty())
494                for (int i = 0; i < 128; i++)
495                    if (filter[i] > 127)
496                        throw MidiInputException("Invalid note on velocity filter, values must be in range 0 .. 127");
497            
498            // apply new filter ...
499            LockGuard lock(noteOnVelocityFilterMutex);
500            // double buffer ... double work ...
501            {
502                std::vector<uint8_t>& config =
503                    noteOnVelocityFilter.GetConfigForUpdate();
504                config = filter;
505            }
506            {
507                std::vector<uint8_t>& config =
508                    noteOnVelocityFilter.SwitchConfig();
509                config = filter;
510            }
511        }
512    
513      void MidiInputPort::Connect(EngineChannel* pEngineChannel, midi_chan_t MidiChannel) {      void MidiInputPort::Connect(EngineChannel* pEngineChannel, midi_chan_t MidiChannel) {
514          if (MidiChannel < 0 || MidiChannel > 16)          if (MidiChannel < 0 || MidiChannel > 16)
515              throw MidiInputException("MIDI channel index out of bounds");              throw MidiInputException("MIDI channel index out of bounds");
516    
517          // first check if desired connection is already established          // first check if desired connection is already established
518          MidiChannelMapMutex.Lock();          {
519          MidiChannelMap_t& midiChannelMap = MidiChannelMap.GetConfigForUpdate();              LockGuard lock(MidiChannelMapMutex);
520          bool bAlreadyDone = midiChannelMap[MidiChannel].count(pEngineChannel);              MidiChannelMap_t& midiChannelMap = MidiChannelMap.GetConfigForUpdate();
521          MidiChannelMapMutex.Unlock();              if (midiChannelMap[MidiChannel].count(pEngineChannel)) return;
522          if (bAlreadyDone) return;          }
523    
524          // remove all other connections of that engine channel (if any)          // remove all other connections of that engine channel (if any)
525          Disconnect(pEngineChannel);          Disconnect(pEngineChannel);
526    
527          // register engine channel on the desired MIDI channel          // register engine channel on the desired MIDI channel
528          MidiChannelMapMutex.Lock();          {
529          MidiChannelMap.GetConfigForUpdate()[MidiChannel].insert(pEngineChannel);              LockGuard lock(MidiChannelMapMutex);
530          MidiChannelMap.SwitchConfig()[MidiChannel].insert(pEngineChannel);              MidiChannelMap.GetConfigForUpdate()[MidiChannel].insert(pEngineChannel);
531          MidiChannelMapMutex.Unlock();              MidiChannelMap.SwitchConfig()[MidiChannel].insert(pEngineChannel);
532            }
533    
534          // inform engine channel about this connection          // inform engine channel about this connection
535          pEngineChannel->Connect(this, MidiChannel);          pEngineChannel->Connect(this);
536            if (pEngineChannel->MidiChannel() != MidiChannel)
537                pEngineChannel->SetMidiChannel(MidiChannel);
538    
539          // mark engine channel as changed          // mark engine channel as changed
540          pEngineChannel->StatusChanged(true);          pEngineChannel->StatusChanged(true);
# Line 463  namespace LinuxSampler { Line 546  namespace LinuxSampler {
546          bool bChannelFound = false;          bool bChannelFound = false;
547    
548          // unregister engine channel from all MIDI channels          // unregister engine channel from all MIDI channels
         MidiChannelMapMutex.Lock();  
549          try {          try {
550                LockGuard lock(MidiChannelMapMutex);
551              {              {
552                  MidiChannelMap_t& midiChannelMap = MidiChannelMap.GetConfigForUpdate();                  MidiChannelMap_t& midiChannelMap = MidiChannelMap.GetConfigForUpdate();
553                  for (int i = 0; i <= 16; i++) {                  for (int i = 0; i <= 16; i++) {
# Line 482  namespace LinuxSampler { Line 565  namespace LinuxSampler {
565              }              }
566          }          }
567          catch(...) { /* NOOP */ }          catch(...) { /* NOOP */ }
         MidiChannelMapMutex.Unlock();  
568    
569          // inform engine channel about the disconnection (if there is one)          // inform engine channel about the disconnection (if there is one)
570          if (bChannelFound) pEngineChannel->DisconnectMidiInputPort();          if (bChannelFound) pEngineChannel->Disconnect(this);
571    
572          // mark engine channel as changed          // mark engine channel as changed
573          pEngineChannel->StatusChanged(true);          pEngineChannel->StatusChanged(true);
# Line 505  namespace LinuxSampler { Line 587  namespace LinuxSampler {
587      }      }
588    
589      void MidiInputPort::Connect(VirtualMidiDevice* pDevice) {      void MidiInputPort::Connect(VirtualMidiDevice* pDevice) {
590          virtualMidiDevicesMutex.Lock();          LockGuard lock(virtualMidiDevicesMutex);
591          // double buffer ... double work ...          // double buffer ... double work ...
592          {          {
593              std::vector<VirtualMidiDevice*>& devices =              std::vector<VirtualMidiDevice*>& devices =
# Line 517  namespace LinuxSampler { Line 599  namespace LinuxSampler {
599                  virtualMidiDevices.SwitchConfig();                  virtualMidiDevices.SwitchConfig();
600              devices.push_back(pDevice);              devices.push_back(pDevice);
601          }          }
         virtualMidiDevicesMutex.Unlock();  
602      }      }
603    
604      void MidiInputPort::Disconnect(VirtualMidiDevice* pDevice) {      void MidiInputPort::Disconnect(VirtualMidiDevice* pDevice) {
605          virtualMidiDevicesMutex.Lock();          LockGuard lock(virtualMidiDevicesMutex);
606          // double buffer ... double work ...          // double buffer ... double work ...
607          {          {
608              std::vector<VirtualMidiDevice*>& devices =              std::vector<VirtualMidiDevice*>& devices =
# Line 533  namespace LinuxSampler { Line 614  namespace LinuxSampler {
614                  virtualMidiDevices.SwitchConfig();                  virtualMidiDevices.SwitchConfig();
615              devices.erase(std::find(devices.begin(), devices.end(), pDevice));              devices.erase(std::find(devices.begin(), devices.end(), pDevice));
616          }          }
617          virtualMidiDevicesMutex.Unlock();      }
618    
619        int MidiInputPort::expectedEventSize(unsigned char byte) {
620            if (!(byte & 0x80) && runningStatusBuf[0])
621                byte = runningStatusBuf[0]; // "running status" mode
622            
623            if (byte < 0x80) return -1; // not a valid status byte
624            if (byte < 0xC0) return 3; // note on/off, note pressure, control change
625            if (byte < 0xE0) return 2; // program change, channel pressure
626            if (byte < 0xF0) return 3; // pitch wheel
627            if (byte == 0xF0) return -1; // sysex message (variable size)
628            if (byte == 0xF1) return 2; // time code per quarter frame
629            if (byte == 0xF2) return 3; // sys. common song position pointer
630            if (byte == 0xF3) return 2; // sys. common song select
631            if (byte == 0xF4) return -1; // sys. common undefined / reserved
632            if (byte == 0xF5) return -1; // sys. common undefined / reserved
633            return 1; // tune request, end of SysEx, system real-time events
634      }      }
635    
636  } // namespace LinuxSampler  } // namespace LinuxSampler

Legend:
Removed from v.2317  
changed lines
  Added in v.2500

  ViewVC Help
Powered by ViewVC