Exemplo n.º 1
0
void LOS::importController(int channel, MidiPort* mport, int n)
{
    MidiInstrument* instr = mport->instrument();
    MidiCtrlValListList* vll = mport->controller();
    iMidiCtrlValList i = vll->find(channel, n);
    if (i != vll->end())
        return; // controller does already exist
    MidiController* ctrl = 0;
    MidiControllerList* mcl = instr->controller();
    // printf("import Ctrl\n");
    for (iMidiController i = mcl->begin(); i != mcl->end(); ++i)
    {
        MidiController* mc = i->second;
        int cn = mc->num();
        // printf("   %x %x\n", n, cn);
        if (cn == n)
        {
            ctrl = mc;
            break;
        }
        // wildcard?
        if (((cn & 0xff) == 0xff) && ((cn & ~0xff) == (n & ~0xff)))
        {
            ctrl = i->second;
            break;
        }
    }
    if (ctrl == 0)
    {
        printf("controller 0x%x not defined for instrument %s, channel %d\n",
                n, instr->iname().toLatin1().constData(), channel);
        // TODO: register default Controller
        //      MidiController* MidiPort::midiController(int num) const
    }
    MidiCtrlValList* newValList = new MidiCtrlValList(n);
    vll->add(channel, newValList);
}
Exemplo n.º 2
0
void MidiDevice::handleSeek()
{
  // If the device is not in use by a port, don't bother it.
  if(_port == -1)
    return;
  
  MidiPort* mp = &MusEGlobal::midiPorts[_port];
  MidiInstrument* instr = mp->instrument();
  MidiCtrlValListList* cll = mp->controller();
  unsigned pos = MusEGlobal::audio->tickPos();
  
  //---------------------------------------------------
  //    Send STOP 
  //---------------------------------------------------
    
  // Don't send if external sync is on. The master, and our sync routing system will take care of that.  
  if(!MusEGlobal::extSyncFlag.value())
  {
    if(mp->syncInfo().MRTOut())
    {
      // Shall we check for device write open flag to see if it's ok to send?...
      //if(!(rwFlags() & 0x1) || !(openFlags() & 1))
      //if(!(openFlags() & 1))
      //  continue;
      mp->sendStop();
    }    
  }

  //---------------------------------------------------
  //    If playing, clear all notes and flush out any
  //     stuck notes which were put directly to the device
  //---------------------------------------------------
  
  if(MusEGlobal::audio->isPlaying()) 
  {
    _playEvents.clear();
    for(iMPEvent i = _stuckNotes.begin(); i != _stuckNotes.end(); ++i) 
    {
      MidiPlayEvent ev(*i);
      ev.setTime(0);
      putEvent(ev);  // For immediate playback try putEvent, putMidiEvent, or sendEvent (for the optimizations).
    }
    _stuckNotes.clear();
  }
  
  //---------------------------------------------------
  //    Send new controller values
  //---------------------------------------------------
    
  // Find channels on this port used in the song...
  bool usedChans[MIDI_CHANNELS];
  int usedChanCount = 0;
  for(int i = 0; i < MIDI_CHANNELS; ++i)
    usedChans[i] = false;
  if(MusEGlobal::song->click() && MusEGlobal::clickPort == _port)
  {
    usedChans[MusEGlobal::clickChan] = true;
    ++usedChanCount;
  }
  bool drum_found = false;
  for(ciMidiTrack imt = MusEGlobal::song->midis()->begin(); imt != MusEGlobal::song->midis()->end(); ++imt)
  {
    //------------------------------------------------------------
    //    While we are at it, flush out any track-related playback stuck notes
    //     (NOT 'live' notes) which were not put directly to the device
    //------------------------------------------------------------
    MPEventList& mel = (*imt)->stuckNotes;
    for(iMPEvent i = mel.begin(), i_next = i; i != mel.end(); i = i_next)
    {
      ++i_next;

      if((*i).port() != _port)
        continue;
      MidiPlayEvent ev(*i);
      ev.setTime(0);
      putEvent(ev); // For immediate playback try putEvent, putMidiEvent, or sendEvent (for the optimizations).
      mel.erase(i);
    }
    
    if((*imt)->type() == MusECore::Track::DRUM)
    {
      if(!drum_found)
      {
        drum_found = true; 
        for(int i = 0; i < DRUM_MAPSIZE; ++i)
        {
          // Default to track port if -1 and track channel if -1.
          int mport = MusEGlobal::drumMap[i].port;
          if(mport == -1)
            mport = (*imt)->outPort();
          int mchan = MusEGlobal::drumMap[i].channel;
          if(mchan == -1)
            mchan = (*imt)->outChannel();
          if(mport != _port || usedChans[mchan])
            continue;
          usedChans[mchan] = true;
          ++usedChanCount;
          if(usedChanCount >= MIDI_CHANNELS)
            break;  // All are used, done searching.
        }
      }
    }
    else
    {
      if((*imt)->outPort() != _port || usedChans[(*imt)->outChannel()])
        continue;
      usedChans[(*imt)->outChannel()] = true;
      ++usedChanCount;
    }

    if(usedChanCount >= MIDI_CHANNELS)
      break;    // All are used. Done searching.
  }   
  
  for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) 
  {
    MidiCtrlValList* vl = ivl->second;
    int chan = ivl->first >> 24;
    if(!usedChans[chan])  // Channel not used in song?
      continue;
    int ctlnum = vl->num();

    // Find the first non-muted value at the given tick...
    bool values_found = false;
    bool found_value = false;
    
    iMidiCtrlVal imcv = vl->lower_bound(pos);
    if(imcv != vl->end() && imcv->first == (int)pos)
    {
      for( ; imcv != vl->end() && imcv->first == (int)pos; ++imcv)
      {
        const Part* p = imcv->second.part;
        if(!p)
          continue;
        // Ignore values that are outside of the part.
        if(pos < p->tick() || pos >= (p->tick() + p->lenTick()))
          continue;
        values_found = true;
        // Ignore if part or track is muted or off.
        if(p->mute())
          continue;
        const Track* track = p->track();
        if(track && (track->isMute() || track->off()))
          continue;
        found_value = true;
        break;
      }
    }
    else
    {
      while(imcv != vl->begin())
      {
        --imcv;
        const Part* p = imcv->second.part;
        if(!p)
          continue;
        // Ignore values that are outside of the part.
        unsigned t = imcv->first;
        if(t < p->tick() || t >= (p->tick() + p->lenTick()))
          continue;
        values_found = true;
        // Ignore if part or track is muted or off.
        if(p->mute())
          continue;
        const Track* track = p->track();
        if(track && (track->isMute() || track->off()))
          continue;
        found_value = true;
        break;
      }
    }

    if(found_value)
    {
      // Don't bother sending any sustain values if not playing. Just set the hw state.
      if(ctlnum == CTRL_SUSTAIN && !MusEGlobal::audio->isPlaying())
        mp->setHwCtrlState(chan, CTRL_SUSTAIN, imcv->second.val);
      else
        // Use sendEvent to get the optimizations and limiting. But force if there's a value at this exact position.
        // NOTE: Why again was this forced? There was a reason. Think it was RJ in response to bug rep, then I modded.
        // A reason not to force: If a straight line is drawn on graph, multiple identical events are stored
        //  (which must be allowed). So seeking through them here sends them all redundantly, not good. // REMOVE Tim.
        mp->sendEvent(MidiPlayEvent(0, _port, chan, ME_CONTROLLER, ctlnum, imcv->second.val), false); //, imcv->first == pos);
        //mp->sendEvent(MidiPlayEvent(0, _port, chan, ME_CONTROLLER, ctlnum, imcv->second.val), pos == 0 || imcv->first == pos);
    }

    // Either no value was found, or they were outside parts, or pos is in the unknown area before the first value.
    // Send instrument default initial values.  NOT for syntis. Use midiState and/or initParams for that. 
    //if((imcv == vl->end() || !done) && !MusEGlobal::song->record() && instr && !isSynti()) 
    // Hmm, without refinement we can only do this at position 0, due to possible 'skipped' values outside parts, above.
    if(!values_found && MusEGlobal::config.midiSendCtlDefaults && !MusEGlobal::song->record() && pos == 0 && instr && !isSynti())
    {
      MidiControllerList* mcl = instr->controller();
      ciMidiController imc = mcl->find(vl->num());
      if(imc != mcl->end())
      {
        MidiController* mc = imc->second;
        if(mc->initVal() != CTRL_VAL_UNKNOWN)
          // Use sendEvent to get the optimizations and limiting. No force sending. Note the addition of bias.
          mp->sendEvent(MidiPlayEvent(0, _port, chan, ME_CONTROLLER, ctlnum, mc->initVal() + mc->bias()), false);
      }
    }
  }
  
  //---------------------------------------------------
  //    reset sustain
  //---------------------------------------------------
  
  for(int ch = 0; ch < MIDI_CHANNELS; ++ch) 
  {
    if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) 
    {
      const MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0);
      putEvent(ev);
    }
  }
  
  //---------------------------------------------------
  //    Send STOP and "set song position pointer"
  //---------------------------------------------------
    
  // Don't send if external sync is on. The master, and our sync routing system will take care of that.  
  if(!MusEGlobal::extSyncFlag.value())
  {
    if(mp->syncInfo().MRTOut())
    {
      //mp->sendStop();   // Moved above
      int beat = (pos * 4) / MusEGlobal::config.division;
      mp->sendSongpos(beat);
    }    
  }
}