diff options
author | Florian Jung <flo@windfisch.org> | 2012-03-15 18:21:23 +0000 |
---|---|---|
committer | Florian Jung <flo@windfisch.org> | 2012-03-15 18:21:23 +0000 |
commit | 27b7bf6815cda7abb67026c37b3e44daee1803cb (patch) | |
tree | 0b9d1c0bc84ac7ff8032e707f2b5fb4e0aaabb5c /muse2/muse/driver | |
parent | 2d6f113a10eb485694e20a78500f650776d701e3 (diff) |
merged with trunk
Diffstat (limited to 'muse2/muse/driver')
-rw-r--r-- | muse2/muse/driver/alsamidi.cpp | 373 | ||||
-rw-r--r-- | muse2/muse/driver/alsamidi.h | 14 | ||||
-rw-r--r-- | muse2/muse/driver/alsatimer.cpp | 2 | ||||
-rw-r--r-- | muse2/muse/driver/jack.cpp | 720 | ||||
-rw-r--r-- | muse2/muse/driver/jackaudio.h | 12 | ||||
-rw-r--r-- | muse2/muse/driver/jackmidi.cpp | 444 | ||||
-rw-r--r-- | muse2/muse/driver/jackmidi.h | 4 | ||||
-rw-r--r-- | muse2/muse/driver/rtctimer.cpp | 4 | ||||
-rw-r--r-- | muse2/muse/driver/rtctimer.h | 22 |
9 files changed, 729 insertions, 866 deletions
diff --git a/muse2/muse/driver/alsamidi.cpp b/muse2/muse/driver/alsamidi.cpp index 740de1fb..08f5345f 100644 --- a/muse2/muse/driver/alsamidi.cpp +++ b/muse2/muse/driver/alsamidi.cpp @@ -38,6 +38,7 @@ #include "xml.h" #include "part.h" #include "gconfig.h" +#include "track.h" #include <QApplication> @@ -58,8 +59,6 @@ MidiAlsaDevice::MidiAlsaDevice(const snd_seq_addr_t& a, const QString& n) : MidiDevice(n) { adr = a; - stopPending = false; - seekPending = false; init(); } @@ -285,7 +284,7 @@ void MidiAlsaDevice::writeRouting(int level, Xml& xml) const bool MidiAlsaDevice::putMidiEvent(const MidiPlayEvent& e) { if (MusEGlobal::midiOutputTrace) { - printf("MidiOut: midiAlsa: "); + printf("MidiOut: Alsa: <%s>: ", name().toLatin1().constData()); e.dump(); } int chn = e.channel(); @@ -309,6 +308,19 @@ bool MidiAlsaDevice::putMidiEvent(const MidiPlayEvent& e) snd_seq_ev_set_pgmchange(&event, chn, a); break; case ME_CONTROLLER: + { + if(a == CTRL_PROGRAM) + { + snd_seq_ev_set_pgmchange(&event, chn, b); + break; + } + else if(a == CTRL_PITCH) + { + snd_seq_ev_set_pitchbend(&event, chn, b); + break; + } + } + #if 1 snd_seq_ev_set_controller(&event, chn, a, b); #else @@ -453,77 +465,160 @@ bool MidiAlsaDevice::putEvent(snd_seq_event_t* event) // Called from ALSA midi sequencer thread only. //--------------------------------------------------------- -#if 0 -void MidiAlsaDevice::processMidi() -{ - processStuckNotes(); - if (_playEvents.empty()) - return; - int port = midiPort(); - MidiPort* mp = port != -1 ? &MusEGlobal::midiPorts[port] : 0; - unsigned curFrame = MusEGlobal::audio->curFrame(); - int tickpos = MusEGlobal::audio->tickPos(); - bool extsync = MusEGlobal::extSyncFlag.value(); - //int frameOffset = getFrameOffset(); - //int nextTick = MusEGlobal::audio->nextTick(); - - // Play all events up to current frame. - iMPEvent i = _playEvents.begin(); - for (; i != _playEvents.end(); ++i) { - if (i->time() > (extsync ? tickpos : curFrame)) // p3.3.25 Check: Should be nextTickPos? p4.0.34 - break; - if(mp){ - if (mp->sendEvent(*i)) - break; - } - else - if(putMidiEvent(*i)) - break; - } - _playEvents.erase(_playEvents.begin(), i); -} - -#else void MidiAlsaDevice::processMidi() { - bool stop = stopPending; // Snapshots - bool seek = seekPending; // - seekPending = stopPending = false; + //bool stop = stopPending; // Snapshots + //bool seek = seekPending; // + //seekPending = stopPending = false; // Transfer the stuck notes FIFO to the play events list. // FIXME It would be faster to have MidiAlsaDevice automatically add the stuck note so that only // one FIFO would be needed. But that requires passing an extra 'tick' and 'off velocity' in // addScheduledEvent, which felt too weird. - while(!stuckNotesFifo.isEmpty()) - _stuckNotes.add(stuckNotesFifo.get()); + //while(!stuckNotesFifo.isEmpty()) + // _stuckNotes.add(stuckNotesFifo.get()); - bool extsync = MusEGlobal::extSyncFlag.value(); //int frameOffset = getFrameOffset(); //int nextTick = MusEGlobal::audio->nextTick(); - // We're in the ALSA midi thread. MusEGlobal::audio->isPlaying() might not be true during seek right now. - //if(stop || (seek && MusEGlobal::audio->isPlaying())) - if(stop || seek) + //bool is_playing = MusEGlobal::audio->isPlaying(); + // We're in the ALSA midi thread. audio->isPlaying() might not be true during seek right now. Include START_PLAY state... + //bool is_playing = MusEGlobal::audio->isPlaying() || MusEGlobal::audio->isStarting(); // TODO Check this. It includes LOOP1 and LOOP2 besides PLAY. + int pos = MusEGlobal::audio->tickPos(); + int port = midiPort(); + MidiPort* mp = port == -1 ? 0 : &MusEGlobal::midiPorts[port]; + bool ext_sync = MusEGlobal::extSyncFlag.value(); + + /* + if(mp) { - //--------------------------------------------------- - // Clear all notes and handle stuck notes - //--------------------------------------------------- - playEventFifo.clear(); + MidiSyncInfo& si = mp->syncInfo(); + if(stop) + { + // Don't send if external sync is on. The master, and our sync routing system will take care of that. + if(!ext_sync) + { + // Shall we check open flags? + //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) + //if(!(dev->openFlags() & 1)) + // return; + + // Send MMC stop... + if(si.MMCOut()) + { + unsigned char msg[mmcStopMsgLen]; + memcpy(msg, mmcStopMsg, mmcStopMsgLen); + msg[1] = si.idOut(); + putMidiEvent(MidiPlayEvent(0, 0, ME_SYSEX, msg, mmcStopMsgLen)); + } + + // Send midi stop... + if(si.MRTOut()) + { + putMidiEvent(MidiPlayEvent(0, 0, 0, ME_STOP, 0, 0)); + // Added check of option send continue not start. p3.3.31 + // Hmm, is this required? Seems to make other devices unhappy. + // (Could try now that this is in MidiDevice. p4.0.22 ) + //if(!si.sendContNotStart()) + // mp->sendSongpos(MusEGlobal::audio->tickPos() * 4 / config.division); + } + } + } + + if(seek) + { + // Don't send if external sync is on. The master, and our sync routing system will take care of that. + if(!ext_sync) + { + // Send midi stop and song position pointer... + if(si.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; + putMidiEvent(MidiPlayEvent(0, 0, 0, ME_STOP, 0, 0)); + // Hm, try sending these after stuck notes below... + //putMidiEvent(MidiPlayEvent(0, 0, 0, ME_SONGPOS, beat, 0)); + //if(is_playing) + // putMidiEvent(MidiPlayEvent(0, 0, 0, ME_CONTINUE, 0, 0)); + } + } + } + } + */ + + /* + if(stop || (seek && is_playing)) + { + // Clear all notes and handle stuck notes... + //playEventFifo.clear(); _playEvents.clear(); - //printf("transferring stuck notes\n"); for(iMPEvent i = _stuckNotes.begin(); i != _stuckNotes.end(); ++i) { - //printf(" stuck note\n"); MidiPlayEvent ev = *i; ev.setTime(0); - _playEvents.add(ev); + //_playEvents.add(ev); + putMidiEvent(ev); // Play immediately. } _stuckNotes.clear(); } - else + */ + + /* + if(mp) + { + MidiSyncInfo& si = mp->syncInfo(); + // Try sending these now after stuck notes above... + if(stop || seek) + { + // Reset sustain. + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) + putMidiEvent(MidiPlayEvent(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0)); + } + if(seek) + { + // Send new song position. + if(!ext_sync && si.MRTOut()) + { + int beat = (pos * 4) / MusEGlobal::config.division; + putMidiEvent(MidiPlayEvent(0, 0, 0, ME_SONGPOS, beat, 0)); + } + // Send new controller values. + MidiCtrlValListList* cll = mp->controller(); + for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) + { + MidiCtrlValList* vl = ivl->second; + iMidiCtrlVal imcv = vl->iValue(pos); + if(imcv != vl->end()) { + Part* p = imcv->second.part; + // Don't send if part or track is muted or off. + if(!p || p->mute()) + continue; + Track* track = p->track(); + if(track && (track->isMute() || track->off())) + continue; + unsigned t = (unsigned)imcv->first; + // Do not add values that are outside of the part. + if(t >= p->tick() && t < (p->tick() + p->lenTick())) + // Use sendEvent to get the optimizations and limiting. But force if there's a value at this exact position. + mp->sendEvent(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val), imcv->first == pos); + } + } + + // Send continue. + // REMOVE Tim. This is redundant and too early - Audio::startRolling already properly sends it when sync ready. + //if(is_playing && !ext_sync && si.MRTOut()) + // putMidiEvent(MidiPlayEvent(0, 0, 0, ME_CONTINUE, 0, 0)); + } + } + */ + + //if(!(stop || (seek && is_playing))) { // Transfer the play events FIFO to the play events list. - while(!playEventFifo.isEmpty()) - _playEvents.add(playEventFifo.get()); + //while(!playEventFifo.isEmpty()) + // _playEvents.add(playEventFifo.get()); /* TODO Handle these more directly than putting them into play events list. //if(MusEGlobal::audio->isPlaying()) @@ -545,53 +640,18 @@ void MidiAlsaDevice::processMidi() processStuckNotes(); } - /* Instead, done immediately in handleStop and handleSeek using putEvent. - if(stop) - { - // reset sustain... - MidiPort* mp = &MusEGlobal::midiPorts[_port]; - for(int ch = 0; ch < MIDI_CHANNELS; ++ch) - { - if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) - { - //printf("send clear sustain!!!!!!!! port %d ch %d\n", i,ch); - MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); - _playEvents.add(ev); - } - } - } - if(seek) - { - // Send new contoller values... - for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) - { - MidiCtrlValList* vl = ivl->second; - iMidiCtrlVal imcv = vl->iValue(pos); - if(imcv != vl->end()) { - Part* p = imcv->second.part; - unsigned t = (unsigned)imcv->first; - // Do not add values that are outside of the part. - if(p && t >= p->tick() && t < (p->tick() + p->lenTick()) ) - _playEvents.add(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val)); - } - } - } */ - - if (_playEvents.empty()) - return; + if(_playEvents.empty()) + return; - int port = midiPort(); - MidiPort* mp = port != -1 ? &MusEGlobal::midiPorts[port] : 0; unsigned curFrame = MusEGlobal::audio->curFrame(); - int tickpos = MusEGlobal::audio->tickPos(); // Play all events up to current frame. iMPEvent i = _playEvents.begin(); for (; i != _playEvents.end(); ++i) { - if (i->time() > (extsync ? tickpos : curFrame)) // p3.3.25 Check: Should be nextTickPos? p4.0.34 + if (i->time() > (ext_sync ? pos : curFrame)) // p3.3.25 Check: Should be nextTickPos? p4.0.34 break; if(mp){ - if (mp->sendEvent(*i)) + if (mp->sendEvent(*i, true)) // Force the event to be sent. break; } else @@ -601,6 +661,7 @@ void MidiAlsaDevice::processMidi() _playEvents.erase(_playEvents.begin(), i); } +/* //--------------------------------------------------------- // handleStop //--------------------------------------------------------- @@ -614,21 +675,6 @@ void MidiAlsaDevice::handleStop() stopPending = true; // Trigger stop handling in processMidi. //--------------------------------------------------- - // reset sustain - //--------------------------------------------------- - - MidiPort* mp = &MusEGlobal::midiPorts[_port]; - for(int ch = 0; ch < MIDI_CHANNELS; ++ch) - { - if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) - { - //printf("send clear sustain!!!!!!!! port %d ch %d\n", i,ch); - MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); - putMidiEvent(ev); - } - } - - //--------------------------------------------------- // send midi stop //--------------------------------------------------- @@ -646,18 +692,61 @@ void MidiAlsaDevice::handleStop() if(si.MRTOut()) { - // Send STOP mp->sendStop(); - - // Added check of option send continue not start. p3.3.31 - // Hmm, is this required? Seems to make other devices unhappy. - // (Could try now that this is in MidiDevice. p4.0.22 ) + // Added check of option send continue not start. Hmm, is this required? Seems to make other devices unhappy. + // (Could try now that this is in MidiDevice.) //if(!si.sendContNotStart()) // mp->sendSongpos(MusEGlobal::audio->tickPos() * 4 / config.division); } } + + //--------------------------------------------------- + // reset sustain + //--------------------------------------------------- + + MidiPort* mp = &MusEGlobal::midiPorts[_port]; + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) + { + MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); + //putMidiEvent(ev); + putEvent(ev); + // Do sendEvent to get the optimizations - send only on a change of value. + //mp->sendEvent(ev); + } + } + + //--------------------------------------------------- + // send midi 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()) +// { +// // Shall we check open flags? +// //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) +// //if(!(dev->openFlags() & 1)) +// // return; +// +// MidiSyncInfo& si = mp->syncInfo(); +// if(si.MMCOut()) +// mp->sendMMCStop(); +// +// if(si.MRTOut()) +// { +// // Send STOP +// mp->sendStop(); +// // Added check of option send continue not start. Hmm, is this required? Seems to make other devices unhappy. +// // (Could try now that this is in MidiDevice.) +// //if(!si.sendContNotStart()) +// // mp->sendSongpos(MusEGlobal::audio->tickPos() * 4 / config.division); +// } +// } } +*/ +/* //--------------------------------------------------------- // handleSeek //--------------------------------------------------------- @@ -675,7 +764,41 @@ void MidiAlsaDevice::handleSeek() int pos = MusEGlobal::audio->tickPos(); //--------------------------------------------------- - // Send new contoller values + // 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(); + } + } + + //--------------------------------------------------- + // reset sustain + //--------------------------------------------------- + + MidiPort* mp = &MusEGlobal::midiPorts[_port]; + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) + { + MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); + putEvent(ev); + //putMidiEvent(ev); + // Do sendEvent to get the optimizations - send only on a change of value. + //mp->sendEvent(ev); + } + } + + //--------------------------------------------------- + // Send new controller values //--------------------------------------------------- for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) @@ -685,12 +808,18 @@ void MidiAlsaDevice::handleSeek() if(imcv != vl->end()) { Part* p = imcv->second.part; + // Don't send if part or track is muted or off. + if(!p || p->mute()) + continue; + Track* track = p->track(); + if(track && (track->isMute() || track->off())) + continue; unsigned t = (unsigned)imcv->first; // Do not add values that are outside of the part. if(p && t >= p->tick() && t < (p->tick() + p->lenTick()) ) //_playEvents.add(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val)); - // Hmm, play event list for immediate playback? Try putEvent instead. p4.0.34 - putMidiEvent(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val)); + // Use sendEvent to get the optimizations and limiting. But force if there's a value at this exact position. + mp->sendEvent(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val), imcv->first == pos); } } @@ -698,31 +827,23 @@ void MidiAlsaDevice::handleSeek() // 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. p3.3.31 + // 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?... - // This means obey what the user has chosen for read/write in the midi port config dialog, - // which already takes into account whether the device is writable or not. //if(!(rwFlags() & 0x1) || !(openFlags() & 1)) //if(!(openFlags() & 1)) // continue; + //mp->sendStop(); // Moved above. int beat = (pos * 4) / MusEGlobal::config.division; - - //bool isPlaying = (state == PLAY); - bool isPlaying = MusEGlobal::audio->isPlaying(); // TODO Check this it includes LOOP1 and LOOP2 besides PLAY. p4.0.22 - - mp->sendStop(); mp->sendSongpos(beat); - if(isPlaying) - mp->sendContinue(); } } } -#endif +*/ //--------------------------------------------------------- // initMidiAlsa diff --git a/muse2/muse/driver/alsamidi.h b/muse2/muse/driver/alsamidi.h index 2054a7d3..7a1ac1c2 100644 --- a/muse2/muse/driver/alsamidi.h +++ b/muse2/muse/driver/alsamidi.h @@ -45,10 +45,8 @@ class MidiAlsaDevice : public MidiDevice { private: // Special for ALSA midi device: Play event list is processed in the ALSA midi sequencer thread. // Need this FIFO, to decouple from audio thread which adds events to the list. - MidiFifo playEventFifo; - MidiFifo stuckNotesFifo; - volatile bool stopPending; - volatile bool seekPending; + //MidiFifo playEventFifo; + //MidiFifo stuckNotesFifo; virtual QString open(); virtual void close(); @@ -69,13 +67,13 @@ class MidiAlsaDevice : public MidiDevice { virtual void writeRouting(int, Xml&) const; virtual inline int deviceType() const { return ALSA_MIDI; } // Schedule an event for playback. Returns false if event cannot be delivered. - virtual bool addScheduledEvent(const MidiPlayEvent& ev) { return !playEventFifo.put(ev); } + //virtual bool addScheduledEvent(const MidiPlayEvent& ev) { return !playEventFifo.put(ev); } // Add a stuck note. Returns false if event cannot be delivered. - virtual bool addStuckNote(const MidiPlayEvent& ev) { return !stuckNotesFifo.put(ev); } + //virtual bool addStuckNote(const MidiPlayEvent& ev) { return !stuckNotesFifo.put(ev); } // Play all events up to current frame. virtual void processMidi(); - virtual void handleStop(); - virtual void handleSeek(); + //virtual void handleStop(); + //virtual void handleSeek(); }; extern bool initMidiAlsa(); diff --git a/muse2/muse/driver/alsatimer.cpp b/muse2/muse/driver/alsatimer.cpp index ee72d679..20f7ab88 100644 --- a/muse2/muse/driver/alsatimer.cpp +++ b/muse2/muse/driver/alsatimer.cpp @@ -173,7 +173,7 @@ namespace MusECore { (long int)((1000000000.0 / snd_timer_info_get_resolution(info)) / snd_timer_params_get_ticks(params))); } - return 0; + return (long int)((1000000000.0 / snd_timer_info_get_resolution(info)) / snd_timer_params_get_ticks(params)); } actFreq = (1000000000 / snd_timer_info_get_resolution(info)) / setTick; if (actFreq != freq) { diff --git a/muse2/muse/driver/jack.cpp b/muse2/muse/driver/jack.cpp index 421152a7..a52410e9 100644 --- a/muse2/muse/driver/jack.cpp +++ b/muse2/muse/driver/jack.cpp @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: jack.cpp,v 1.30.2.17 2009/12/20 05:00:35 terminator356 Exp $ // (C) Copyright 2002 Werner Schweer (ws@seh.de) +// (C) Copyright 2012 Tim E. Real (terminator356 on sourceforge.net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -82,15 +83,6 @@ bool checkAudioDevice() namespace MusECore { -//extern int jackmidi_pi[2]; -//extern int jackmidi_po[2]; - -//jack_port_t *midi_port_in[JACK_MIDI_CHANNELS]; -//jack_port_t *midi_port_out[JACK_MIDI_CHANNELS]; - -//muse_jack_midi_buffer jack_midi_out_data[JACK_MIDI_CHANNELS]; -//muse_jack_midi_buffer jack_midi_in_data[JACK_MIDI_CHANNELS]; - JackAudioDevice* jackAudio; //--------------------------------------------------------- @@ -109,38 +101,9 @@ inline bool checkJackClient(jack_client_t* _client) // jack_thread_init //--------------------------------------------------------- -static void jack_thread_init (void* ) // data +static void jack_thread_init (void* ) { MusEGlobal::doSetuid(); - /* - if (jackAudio->isRealtime()) { - struct sched_param rt_param; - int rv; - memset(&rt_param, 0, sizeof(sched_param)); - int type; - rv = pthread_getschedparam(pthread_self(), &type, &rt_param); - if (rv != 0) - perror("get scheduler parameter"); - if (type != SCHED_FIFO) { - fprintf(stderr, "JACK thread not running SCHED_FIFO, try to set...\n"); - - memset(&rt_param, 0, sizeof(sched_param)); - rt_param.sched_priority = 1; - rv = pthread_setschedparam(pthread_self(), SCHED_FIFO, &rt_param); - if (rv != 0) - perror("set realtime scheduler"); - memset(&rt_param, 0, sizeof(sched_param)); - rv = pthread_getschedparam(pthread_self(), &type, &rt_param); - if (rv != 0) - perror("get scheduler parameter"); - if (type != SCHED_FIFO) - fprintf(stderr, "JACK still not running FIFO !?!\n" - "======reliable RT operation not possible!!======\n"); - else - fprintf(stderr, "JACK thread succesfully set to SCHED_FIFO\n"); - } - } - */ #ifdef VST_SUPPORT if (loadVST) fst_adopt_thread(); @@ -148,161 +111,78 @@ static void jack_thread_init (void* ) // data MusEGlobal::undoSetuid(); } -/* -//--------------------------------------------------------- -// processAudio + Midi -// JACK callback -//--------------------------------------------------------- -void -print_triplet(unsigned char *data) -{ - int a,b,c; - a = b = c = 0; - memcpy(&a, data, 1); - memcpy(&b, data+1, 1); - memcpy(&c, data+2, 1); - fprintf(stderr, "%x,%x,%x", a, b, c); -} -*/ - -/* -void handle_jack_midi_in_events(jack_nframes_t frames) -{ - char buf = 0; - int i,j; - jack_midi_event_t midi_event; - unsigned char t,n,v; - - for(j = 0; j < JACK_MIDI_CHANNELS; j++){ - void *midi_buffer_in = jack_port_get_buffer(midi_port_in[j], frames); - int event_count = jack_midi_get_event_count(midi_buffer_in); - - for(i = 0; i < event_count; i++){ - jack_midi_event_get(&midi_event, midi_buffer_in, i); - t = midi_event.buffer[0]; - n = midi_event.buffer[1]; - v = midi_event.buffer[2]; - if(((*(midi_event.buffer) & 0xf0)) == 0x90){ - fprintf(stderr, "jack-midi-in-event: ON_ time=%d %u ", midi_event.time, - midi_event.size); - print_triplet(midi_event.buffer); - fprintf(stderr, "\n"); - }else if(((*(midi_event.buffer)) & 0xf0) == 0x80){ - fprintf(stderr, "jack-midi-in-event: OFF time=%d %u ", midi_event.time, - midi_event.size); - print_triplet(midi_event.buffer); - fprintf(stderr, "\n"); - }else{ - fprintf(stderr, "jack-midi-in-event: ??? time=%d %u ", midi_event.time, - midi_event.size); - print_triplet(midi_event.buffer); - fprintf(stderr, "\n"); - } - jack_midi_in_data[j].buffer[0] = t; - jack_midi_in_data[j].buffer[1] = n; - jack_midi_in_data[j].buffer[2] = v; - jack_midi_in_data[j].buffer[3] = 1; - fprintf(stderr, "handle_jack_midi_in_events() w\n"); - write(jackmidi_pi[1], &buf, 1); - fprintf(stderr, "handle_jack_midi_in_events() wd\n"); - } - } -} - -void handle_jack_midi_out_events(jack_nframes_t frames) -{ - unsigned char *data; - void *port_buf; - int i,j,n,x; - - //for(i = 0; i < JACK_MIDI_CHANNELS; i++){ - for(i = 0; i < JACK_MIDI_CHANNELS; ++i){ - // jack-midi-clear any old events - while(jack_midi_out_data[i].buffer[jack_midi_out_data[i].take*4+3] == 2){ - port_buf = jack_port_get_buffer(midi_port_out[i], frames); - jack_midi_clear_buffer(port_buf); - jack_midi_out_data[i].buffer[jack_midi_out_data[i].take*4+3] = 0; - // point the take to the next slot - jack_midi_out_data[i].take++; - if(jack_midi_out_data[i].take >= JACK_MIDI_BUFFER_SIZE){ - jack_midi_out_data[i].take = 0; - } - } - // check if any incoming midi-events from muse - if(jack_midi_out_data[i].give != jack_midi_out_data[i].take){ - - if(jack_midi_out_data[i].give > jack_midi_out_data[i].take){ - n = jack_midi_out_data[i].give - jack_midi_out_data[i].take; - }else{ - n = jack_midi_out_data[i].give + - (JACK_MIDI_BUFFER_SIZE - jack_midi_out_data[i].take); - } - port_buf = jack_port_get_buffer(midi_port_out[i], frames); - jack_midi_clear_buffer(port_buf); - // FIX: midi events has different sizes, compare note-on to - // program-change. We should first walk over the events - // counting the size. - //data = jack_midi_event_reserve(port_buf, 0, n*3); - //x = jack_midi_out_data[i].take; - //for(j = 0; j < n; j++){ - // data[j*3+0] = jack_midi_out_data[i].buffer[x*4+0]; - // data[j*3+1] = jack_midi_out_data[i].buffer[x*4+1]; - // data[j*3+2] = jack_midi_out_data[i].buffer[x*4+2]; - // after having copied the buffer over to the jack-buffer, - // mark the muses midi-out buffer as 'need-cleaning' - // jack_midi_out_data[i].buffer[x*4+3] = 2; - // x++; - // if(x >= JACK_MIDI_BUFFER_SIZE){ - // x = 0; - // } - //} - - x = jack_midi_out_data[i].take; - for(j = 0; j < n; ++j) - { - data = jack_midi_event_reserve(port_buf, 0, 3); - if(data == 0) - { - fprintf(stderr, "handle_jack_midi_out_events: buffer overflow, event lost\n"); - // Can do no more processing. Just return. - return; - } - data[0] = jack_midi_out_data[i].buffer[x*4+0]; - data[1] = jack_midi_out_data[i].buffer[x*4+1]; - data[2] = jack_midi_out_data[i].buffer[x*4+2]; - // after having copied the buffer over to the jack-buffer, - // mark the muses midi-out buffer as 'need-cleaning' - jack_midi_out_data[i].buffer[x*4+3] = 2; - x++; - if(x >= JACK_MIDI_BUFFER_SIZE){ - x = 0; - } - } - - } - } -} -*/ - -//static int processAudio(jack_nframes_t frames, void*) int JackAudioDevice::processAudio(jack_nframes_t frames, void*) { - jackAudio->_frameCounter += frames; - -/// handle_jack_midi_in_events(frames); -/// handle_jack_midi_out_events(frames); - -// if (JACK_DEBUG) -// printf("processAudio - >>>>\n"); + int state_pending = jackAudio->_dummyStatePending; // Snapshots. + int pos_pending = jackAudio->_dummyPosPending; // + jackAudio->_dummyStatePending = -1; // Reset. + jackAudio->_dummyPosPending = -1; // + + jackAudio->_frameCounter += frames; MusEGlobal::segmentSize = frames; + if (MusEGlobal::audio->isRunning()) - MusEGlobal::audio->process((unsigned long)frames); + { + // Are we not using Jack transport? + if(!MusEGlobal::useJackTransport.value()) + { + // STOP -> STOP, STOP -> START_PLAY, PLAY -> START_PLAY all count as 'syncing'. + if(((jackAudio->dummyState == Audio::STOP || jackAudio->dummyState == Audio::PLAY) && state_pending == Audio::START_PLAY) + || (jackAudio->dummyState == Audio::STOP && state_pending == Audio::STOP) ) + { + jackAudio->_syncTimeout = (float)frames / (float)MusEGlobal::sampleRate; // (Re)start the timeout counter... + if(pos_pending != -1) + jackAudio->dummyPos = pos_pending; // Set the new dummy position. + if((jackAudio->dummyState == Audio::STOP || jackAudio->dummyState == Audio::PLAY) && state_pending == Audio::START_PLAY) + jackAudio->dummyState = Audio::START_PLAY; + } + else // All other states such as START_PLAY -> STOP, PLAY -> STOP. + if(state_pending != -1 && state_pending != jackAudio->dummyState) + { + jackAudio->_syncTimeout = 0.0; // Reset. + jackAudio->dummyState = state_pending; + } + + // Is the sync timeout counter running? + if(jackAudio->_syncTimeout > 0.0) + { + //printf("Jack processAudio dummy sync: state:%d pending:%d\n", jackAudio->dummyState, state_pending); + // Is MusE audio ready to roll? + if(MusEGlobal::audio->sync(jackAudio->dummyState, jackAudio->dummyPos)) + { + jackAudio->_syncTimeout = 0.0; // Reset. + // We're ready. Switch to PLAY state. + if(jackAudio->dummyState == Audio::START_PLAY) + jackAudio->dummyState = Audio::PLAY; + } + else + { + jackAudio->_syncTimeout += (float)frames / (float)MusEGlobal::sampleRate; + if(jackAudio->_syncTimeout > 5.0) // TODO: Make this timeout a 'settings' option so it can be applied both to Jack and here. + { + if (MusEGlobal::debugMsg) + puts("Jack dummy sync timeout! Starting anyway...\n"); + jackAudio->_syncTimeout = 0.0; // Reset. + // We're not ready, but no time left - gotta roll anyway. Switch to PLAY state, similar to how Jack is supposed to work. + if(jackAudio->dummyState == Audio::START_PLAY) + { + jackAudio->dummyState = Audio::PLAY; + // Docs say sync will be called with Rolling state when timeout expires. + MusEGlobal::audio->sync(jackAudio->dummyState, jackAudio->dummyPos); + } + } + } + } + } + + //if(jackAudio->getState() != Audio::START_PLAY) // Don't process while we're syncing. TODO: May need to deliver silence in process! + MusEGlobal::audio->process((unsigned long)frames); + } else { if (MusEGlobal::debugMsg) puts("jack calling when audio is disconnected!\n"); } -// if (JACK_DEBUG) -// printf("processAudio - <<<<\n"); + return 0; } @@ -344,13 +224,9 @@ static int processSync(jack_transport_state_t state, jack_position_t* pos, void* } unsigned frame = pos->frame; - //printf("processSync valid:%d frame:%d\n", pos->valid, frame); - - // p3.3.23 - //printf("Jack processSync() before MusEGlobal::audio->sync frame:%d\n", frame); //return MusEGlobal::audio->sync(audioState, frame); int rv = MusEGlobal::audio->sync(audioState, frame); - //printf("Jack processSync() after MusEGlobal::audio->sync frame:%d\n", frame); + //printf("Jack processSync() after MusEGlobal::audio->sync frame:%d\n", frame); return rv; } @@ -366,7 +242,6 @@ static void timebase_callback(jack_transport_state_t /* state */, { //printf("Jack timebase_callback pos->frame:%u MusEGlobal::audio->tickPos:%d MusEGlobal::song->cpos:%d\n", pos->frame, MusEGlobal::audio->tickPos(), MusEGlobal::song->cpos()); - // p3.3.27 //Pos p(pos->frame, false); Pos p(MusEGlobal::extSyncFlag.value() ? MusEGlobal::audio->tickPos() : pos->frame, MusEGlobal::extSyncFlag.value() ? true : false); // Can't use song pos - it is only updated every (slow) GUI heartbeat ! @@ -382,7 +257,6 @@ static void timebase_callback(jack_transport_state_t /* state */, // dummy: // - // p3.3.26 //pos->beats_per_bar = 4; //pos->beat_type = 4; //pos->ticks_per_beat = 384; @@ -472,20 +346,7 @@ JackAudioDevice::~JackAudioDevice() if (JACK_DEBUG) printf("~JackAudioDevice()\n"); if (_client) { - - /* - // p3.3.35 - for(int i = 0; i < JACK_MIDI_CHANNELS; i++) - { - if(midi_port_in[i]) - jack_port_unregister(_client, midi_port_in[i]); - if(midi_port_out[i]) - jack_port_unregister(_client, midi_port_out[i]); - } - */ - if (jack_client_close(_client)) { - //error->logError("jack_client_close() failed: %s\n", strerror(errno)); fprintf(stderr,"jack_client_close() failed: %s\n", strerror(errno)); } } @@ -518,32 +379,6 @@ int JackAudioDevice::realtimePriority() const return param.sched_priority; } -/* -//--------------------------------------------------------- -// getJackName() -//--------------------------------------------------------- - -char* JackAudioDevice::getJackName() - { - return jackRegisteredName; - } -*/ - -/* -//--------------------------------------------------------- -// clientName() -//--------------------------------------------------------- - -const char* JackAudioDevice::clientName() -{ - //if(_client) - // return jack_get_client_name(_client); - //else - // return "MusE"; - return jackRegisteredName; -} -*/ - //--------------------------------------------------------- // initJackAudio // return true if JACK not found @@ -551,15 +386,6 @@ const char* JackAudioDevice::clientName() bool initJackAudio() { - /* - // p3.3.35 - for(int i = 0; i < JACK_MIDI_CHANNELS; i++) - { - midi_port_in[i] = 0; - midi_port_out[i] = 0; - } - */ - if (JACK_DEBUG) printf("initJackAudio()\n"); if (MusEGlobal::debugMsg) { @@ -596,7 +422,7 @@ bool initJackAudio() if (status & JackVersionError) printf("jack server has wrong version\n"); printf("cannot create jack client\n"); - MusEGlobal::undoSetuid(); // p3.3.51 + MusEGlobal::undoSetuid(); return true; } @@ -616,39 +442,6 @@ bool initJackAudio() } MusEGlobal::undoSetuid(); - /* - // setup midi input/output - //memset(jack_midi_out_data, 0, JACK_MIDI_CHANNELS * sizeof(muse_jack_midi_buffer)); - //memset(jack_midi_in_data, 0, JACK_MIDI_CHANNELS * sizeof(muse_jack_midi_buffer)); - if(client){ - for(i = 0; i < JACK_MIDI_CHANNELS; i++) - { - char buf[80]; - snprintf(buf, 80, "muse-jack-midi-in-%d", i+1); - midi_port_in[i] = jack_port_register(client, buf, - JACK_DEFAULT_MIDI_TYPE, - JackPortIsInput, 0); - if(midi_port_in[i] == NULL){ - fprintf(stderr, "failed to register jack-midi-in\n"); - exit(-1); - } - snprintf(buf, 80, "muse-jack-midi-out-%d", i+1); - midi_port_out[i] = jack_port_register(client, buf, - JACK_DEFAULT_MIDI_TYPE, - JackPortIsOutput, 0); - if(midi_port_out == NULL) - { - fprintf(stderr, "failed to register jack-midi-out\n"); - exit(-1); - } - } - } - else - { - fprintf(stderr, "WARNING NO muse-jack midi connection\n"); - } - */ - if (client) { MusEGlobal::audioDevice = jackAudio; jackAudio->scanMidiPorts(); @@ -730,8 +523,8 @@ void JackAudioDevice::connectJackMidiPorts() //void* port = md->clientPort(); if(md->rwFlags() & 1) { - void* port = md->outClientPort(); // p3.3.55 - if(port) // + void* port = md->outClientPort(); + if(port) { RouteList* rl = md->outRoutes(); for (ciRoute r = rl->begin(); r != rl->end(); ++r) @@ -743,12 +536,10 @@ void JackAudioDevice::connectJackMidiPorts() } } - // else // p3.3.55 Removed - if(md->rwFlags() & 2) { - void* port = md->inClientPort(); // p3.3.55 - if(port) // + void* port = md->inClientPort(); + if(port) { RouteList* rl = md->inRoutes(); for (ciRoute r = rl->begin(); r != rl->end(); ++r) @@ -760,117 +551,8 @@ void JackAudioDevice::connectJackMidiPorts() } } } - - - /* - const char* type = JACK_DEFAULT_MIDI_TYPE; - const char** ports = jack_get_ports(_client, 0, type, 0); - for (const char** p = ports; p && *p; ++p) - { - jack_port_t* port = jack_port_by_name(_client, *p); - if(!port) - continue; - // Ignore our own client ports. - if(jack_port_is_mine(_client, port)) - { - if(MusEGlobal::debugMsg) - printf(" ignoring own port: %s\n", *p); - continue; - } - int nsz = jack_port_name_size(); - char buffer[nsz]; - strncpy(buffer, *p, nsz); - // Ignore the MusE Jack port. - //if(strncmp(buffer, "MusE", 4) == 0) - // continue; - - if(MusEGlobal::debugMsg) - printf(" found port: %s ", buffer); - - // If there are aliases for this port, use the first one - much better for identifying. - //char a1[nsz]; - char a2[nsz]; - char* aliases[2]; - //aliases[0] = a1; - aliases[0] = buffer; - aliases[1] = a2; - // To disable aliases, just rem this line. - jack_port_get_aliases(port, aliases); - //int na = jack_port_get_aliases(port, aliases); - //char* namep = (na >= 1) ? aliases[0] : buffer; - char* namep = aliases[0]; - - if(MusEGlobal::debugMsg) - printf("alias: %s\n", aliases[0]); - - //int flags = 0; - int pf = jack_port_flags(port); - // If Jack port can send data to us... - //if(pf & JackPortIsOutput) - // Mark as input capable. - // flags |= 2; - // If Jack port can receive data from us... - //if(pf & JackPortIsInput) - // Mark as output capable. - // flags |= 1; - - //JackPort jp(0, QString(buffer), flags); - //portList.append(jp); - - QString name(namep); - - if(JACK_DEBUG) - printf("JackAudioDevice::graphChanged %s\n", name.toLatin1()); - - for(iMidiDevice imd = MusEGlobal::midiDevices.begin(); imd != MusEGlobal::midiDevices.end(); ++imd) - { - // Is it a Jack midi device? - MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(*imd); - if(!mjd) - continue; - - //if(dev->name() != name) - // continue; - - // Is this port the one created for the Jack midi device? - if(!mjd->clientJackPort() || (mjd->clientJackPort() != port)) - continue; - - jack_port_t* devport = jack_port_by_name(_client, mjd->name().toLatin1()); - if(!devport) - continue; - - int ofl = mjd->openFlags(); - - if(JACK_DEBUG) - printf("JackAudioDevice::graphChanged found MidiJackDevice:%s\n", mjd->name().toLatin1()); - - // Note docs say it can't be both input and output. src, dest - // If Jack port can receive data from us and we actually want to... - if((pf & JackPortIsOutput) && (ofl & 1)) - { - if(JACK_DEBUG) - printf("JackAudioDevice::graphChanged connecting MusE output\n"); - MusEGlobal::audioDevice->connect(port, devport); - } - else - // If Jack port can send data to us and we actually want it... - if((pf & JackPortIsInput) && (ofl & 2)) - { - if(JACK_DEBUG) - printf("JackAudioDevice::graphChanged connecting MusE input\n"); - MusEGlobal::audioDevice->connect(devport, port); - } - - break; - } - } - - if(ports) - free(ports); - - */ } + //--------------------------------------------------------- // client_registration_callback //--------------------------------------------------------- @@ -1009,9 +691,6 @@ void JackAudioDevice::graphChanged() ++pn; } - // p3.3.37 - //delete ports; - //free(ports); jack_free(ports); // p4.0.29 ports = NULL; @@ -1100,9 +779,6 @@ void JackAudioDevice::graphChanged() ++pn; } - // p3.3.37 - //delete ports; - //free(ports); jack_free(ports); // p4.0.29 ports = NULL; @@ -1134,12 +810,9 @@ void JackAudioDevice::graphChanged() if(md->rwFlags() & 1) // Writable { - // p3.3.55 jack_port_t* port = (jack_port_t*)md->outClientPort(); if(port != 0) { - //printf("graphChanged() valid out client port\n"); // p3.3.55 - const char** ports = jack_port_get_all_connections(_client, port); RouteList* rl = md->outRoutes(); @@ -1160,7 +833,7 @@ void JackAudioDevice::graphChanged() //if (irl->channel != channel) // continue; QString name = irl->name(); - //name += QString(JACK_MIDI_OUT_PORT_SUFFIX); // p3.3.55 + //name += QString(JACK_MIDI_OUT_PORT_SUFFIX); QByteArray ba = name.toLatin1(); const char* portName = ba.constData(); bool found = false; @@ -1224,9 +897,6 @@ void JackAudioDevice::graphChanged() ++pn; } - // p3.3.55 - // Done with ports. Free them. - //free(ports); jack_free(ports); // p4.0.29 } } @@ -1239,11 +909,9 @@ void JackAudioDevice::graphChanged() if(md->rwFlags() & 2) // Readable { - // p3.3.55 jack_port_t* port = (jack_port_t*)md->inClientPort(); if(port != 0) { - //printf("graphChanged() valid in client port\n"); // p3.3.55 const char** ports = jack_port_get_all_connections(_client, port); RouteList* rl = md->inRoutes(); @@ -1326,20 +994,11 @@ void JackAudioDevice::graphChanged() } ++pn; } - // p3.3.55 - // Done with ports. Free them. - //free(ports); + jack_free(ports); // p4.0.29 } } } - - // p3.3.55 Removed. - //if(ports) - // Done with ports. Free them. - //delete ports; - // free(ports); - //ports = NULL; } } @@ -1360,18 +1019,12 @@ void JackAudioDevice::registerClient() if(!checkJackClient(_client)) return; jack_set_process_callback(_client, processAudio, 0); jack_set_sync_callback(_client, processSync, 0); - // FIXME: FIXME: - // Added by Tim. p3.3.20 - // Did not help. Seek during play: Jack keeps switching to STOP state after about 1-2 seconds timeout if sync is holding it up. - // Nothing in MusE seems to be telling it to stop. - // NOTE: Update: It was a bug in QJackCtl. Fixed now. //jack_set_sync_timeout(_client, 5000000); // Change default 2 to 5 second sync timeout because prefetch may be very slow esp. with resampling ! jack_on_shutdown(_client, processShutdown, 0); jack_set_buffer_size_callback(_client, bufsize_callback, 0); jack_set_sample_rate_callback(_client, srate_callback, 0); jack_set_port_registration_callback(_client, registration_callback, 0); - // p3.3.37 jack_set_client_registration_callback(_client, client_registration_callback, 0); jack_set_port_connect_callback(_client, port_connect_callback, 0); @@ -1384,7 +1037,6 @@ void JackAudioDevice::registerClient() // registerInPort //--------------------------------------------------------- -//void* JackAudioDevice::registerInPort(const char* name) void* JackAudioDevice::registerInPort(const char* name, bool midi) { if (JACK_DEBUG) @@ -1401,7 +1053,6 @@ void* JackAudioDevice::registerInPort(const char* name, bool midi) // registerOutPort //--------------------------------------------------------- -//void* JackAudioDevice::registerOutPort(const char* name) void* JackAudioDevice::registerOutPort(const char* name, bool midi) { if (JACK_DEBUG) @@ -1452,7 +1103,7 @@ void JackAudioDevice::disconnect(void* src, void* dst) if (JACK_DEBUG) printf("JackAudioDevice::disconnect()\n"); if(!checkJackClient(_client)) return; - if(!src || !dst) // p3.3.55 + if(!src || !dst) return; const char* sn = jack_port_name((jack_port_t*) src); const char* dn = jack_port_name((jack_port_t*) dst); @@ -1478,7 +1129,6 @@ void JackAudioDevice::disconnect(void* src, void* dst) // start //--------------------------------------------------------- -//void JackAudioDevice::start() void JackAudioDevice::start(int /*priority*/) { if (JACK_DEBUG) @@ -1488,7 +1138,7 @@ void JackAudioDevice::start(int /*priority*/) MusEGlobal::doSetuid(); if (jack_activate(_client)) { - MusEGlobal::undoSetuid(); // p3.3.51 + MusEGlobal::undoSetuid(); fprintf (stderr, "JACK: cannot activate client\n"); exit(-1); } @@ -1530,7 +1180,6 @@ void JackAudioDevice::start(int /*priority*/) } } - // p3.3.37 // Connect the Jack midi client ports to device ports. connectJackMidiPorts(); @@ -1723,9 +1372,6 @@ void JackAudioDevice::getJackPorts(const char** ports, std::list<QString>& name_ name_list.push_back(qname); } - // p3.3.37 - //if(ports) - //free(ports); // jack_free(ports); // p4.0.29 //return clientList; @@ -1744,61 +1390,6 @@ std::list<QString> JackAudioDevice::outputPorts(bool midi, int aliases) const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; const char** ports = jack_get_ports(_client, 0, type, JackPortIsOutput); - /* - QString qname; - for (const char** p = ports; p && *p; ++p) { - jack_port_t* port = jack_port_by_name(_client, *p); - //int flags = jack_port_flags(port); - //if (!(flags & JackPortIsOutput)) - // continue; - //char buffer[128]; - - int nsz = jack_port_name_size(); - char buffer[nsz]; - - strncpy(buffer, *p, nsz); - //if (strncmp(buffer, "MusE", 4) == 0) - //{ - // if(MusEGlobal::debugMsg) - // printf("JackAudioDevice::outputPorts ignoring own MusE port: %s\n", *p); - // continue; - //} - - // Ignore our own client ports. - if(jack_port_is_mine(_client, port)) - { - if(MusEGlobal::debugMsg) - printf("JackAudioDevice::outputPorts ignoring own port: %s\n", *p); - continue; - } - - // p3.3.38 - if((aliases == 0) || (aliases == 1)) - { - //char a1[nsz]; - char a2[nsz]; - char* al[2]; - //aliases[0] = a1; - al[0] = buffer; - al[1] = a2; - int na = jack_port_get_aliases(port, al); - int a = aliases; - if(a >= na) - { - a = na; - if(a > 0) - a--; - } - qname = QString(al[a]); - } - else - qname = QString(buffer); - - //clientList.push_back(QString(buffer)); - clientList.push_back(qname); - } - */ - if(ports) { getJackPorts(ports, clientList, midi, true, aliases); // Get physical ports first. @@ -1823,61 +1414,6 @@ std::list<QString> JackAudioDevice::inputPorts(bool midi, int aliases) const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; const char** ports = jack_get_ports(_client, 0, type, JackPortIsInput); - /* - QString qname; - for (const char** p = ports; p && *p; ++p) { - jack_port_t* port = jack_port_by_name(_client, *p); - //int flags = jack_port_flags(port); - //if (!(flags & JackPortIsInput)) - // continue; - //char buffer[128]; - - int nsz = jack_port_name_size(); - char buffer[nsz]; - - strncpy(buffer, *p, nsz); - //if (strncmp(buffer, "MusE", 4) == 0) - //{ - // if(MusEGlobal::debugMsg) - // printf("JackAudioDevice::inputPorts ignoring own MusE port: %s\n", *p); - // continue; - //} - - // Ignore our own client ports. - if(jack_port_is_mine(_client, port)) - { - if(MusEGlobal::debugMsg) - printf("JackAudioDevice::inputPorts ignoring own port: %s\n", *p); - continue; - } - - // p3.3.38 - if((aliases == 0) || (aliases == 1)) - { - //char a1[nsz]; - char a2[nsz]; - char* al[2]; - //aliases[0] = a1; - al[0] = buffer; - al[1] = a2; - int na = jack_port_get_aliases(port, al); - int a = aliases; - if(a >= na) - { - a = na; - if(a > 0) - a--; - } - qname = QString(al[a]); - } - else - qname = QString(buffer); - - //clientList.push_back(QString(buffer)); - clientList.push_back(qname); - } - */ - if(ports) { getJackPorts(ports, clientList, midi, true, aliases); // Get physical ports first. @@ -1979,37 +1515,6 @@ void JackAudioDevice::setFreewheel(bool f) } //--------------------------------------------------------- -// dummySync -//--------------------------------------------------------- - -bool JackAudioDevice::dummySync(int state) -{ - // Roughly segment time length. - //timespec ts = { 0, (1000000000 * MusEGlobal::segmentSize) / MusEGlobal::sampleRate }; // In nanoseconds. - unsigned int sl = (1000000 * MusEGlobal::segmentSize) / MusEGlobal::sampleRate; // In microseconds. - - double ct = curTime(); - // Wait for a default maximum of 5 seconds. - // Similar to how Jack is supposed to wait a default of 2 seconds for slow clients. - // TODO: Make this timeout a 'settings' option so it can be applied both to Jack and here. - while((curTime() - ct) < 5.0) - { - // Is MusE audio ready to roll? - if(MusEGlobal::audio->sync(state, dummyPos)) - return true; - - // Not ready. Wait a 'segment', try again... - //nanosleep(&ts, NULL); - usleep(sl); // usleep is supposed to be obsolete! - } - - //if(JACK_DEBUG) - printf("JackAudioDevice::dummySync Sync timeout - audio not ready!\n"); - - return false; -} - -//--------------------------------------------------------- // startTransport //--------------------------------------------------------- @@ -2022,19 +1527,7 @@ void JackAudioDevice::startTransport() // as if processSync was called. if(!MusEGlobal::useJackTransport.value()) { - //dummyState = Audio::START_PLAY; - - // Is MusE audio ready to roll? - //if(dummySync(dummyState)) - if(dummySync(Audio::START_PLAY)) - { - // MusE audio is ready to roll. Let's play. - dummyState = Audio::PLAY; - return; - } - - // Ready or not, we gotta roll. Similar to how Jack is supposed to roll anyway. - dummyState = Audio::PLAY; + _dummyStatePending = Audio::START_PLAY; return; } @@ -2052,11 +1545,9 @@ void JackAudioDevice::stopTransport() if (JACK_DEBUG) printf("JackAudioDevice::stopTransport()\n"); - dummyState = Audio::STOP; - if(!MusEGlobal::useJackTransport.value()) { - //dummyState = Audio::STOP; + _dummyStatePending = Audio::STOP; return; } @@ -2077,32 +1568,11 @@ void JackAudioDevice::seekTransport(unsigned frame) if (JACK_DEBUG) printf("JackAudioDevice::seekTransport() frame:%d\n", frame); - dummyPos = frame; if(!MusEGlobal::useJackTransport.value()) { - // If we're not using Jack's transport, just pass the current state and new frame along - // as if processSync was called. - //dummyPos = frame; - int tempState = dummyState; - //dummyState = Audio::START_PLAY; - - // Is MusE audio ready yet? - //MusEGlobal::audio->sync(dummyState, dummyPos); - //if(dummySync(dummyState)) - if(dummySync(Audio::START_PLAY)) - { - dummyState = tempState; - return; - } - - // Not ready, resume previous state anyway. - // FIXME: Observed: Seek during play: Jack transport STOPs on timeout. - // Docs say when starting play, transport will roll anyway, ready or not (observed), - // but don't mention what should happen on seek during play. - // And setting the slow-sync timeout doesn't seem to do anything! - // NOTE: Update: It was a bug with QJackCtl. Fixed now. - //dummyState = tempState; - dummyState = Audio::STOP; + _dummyPosPending = frame; + // STOP -> STOP means seek in stop mode. PLAY -> START_PLAY means seek in play mode. + _dummyStatePending = (dummyState == Audio::STOP ? Audio::STOP : Audio::START_PLAY); return; } @@ -2120,28 +1590,11 @@ void JackAudioDevice::seekTransport(const Pos &p) if (JACK_DEBUG) printf("JackAudioDevice::seekTransport() frame:%d\n", p.frame()); - dummyPos = p.frame(); if(!MusEGlobal::useJackTransport.value()) { - // If we're not using Jack's transport, just pass the current state and new frame along - // as if processSync was called. - //dummyPos = p.frame(); - int tempState = dummyState; - //dummyState = Audio::START_PLAY; - - // Is MusE audio ready yet? - //MusEGlobal::audio->sync(dummyState, dummyPos); - //if(dummySync(dummyState)) - if(dummySync(Audio::START_PLAY)) - { - dummyState = tempState; - return; - } - - // Not ready, resume previous state anyway. - // FIXME: See fixme in other seekTransport... - //dummyState = tempState; - dummyState = Audio::STOP; + _dummyPosPending = p.frame(); + // STOP -> STOP means seek in stop mode. PLAY -> START_PLAY means seek in play mode. + _dummyStatePending = (dummyState == Audio::STOP ? Audio::STOP : Audio::START_PLAY); return; } @@ -2380,8 +1833,7 @@ void exitJackAudio() if (JACK_DEBUG) printf("exitJackAudio() after delete jackAudio\n"); - // Added by Tim. p3.3.14 - MusEGlobal::audioDevice = NULL; + MusEGlobal::audioDevice = NULL; // By Tim } } // namespace MusECore diff --git a/muse2/muse/driver/jackaudio.h b/muse2/muse/driver/jackaudio.h index c4d37db9..838a20df 100644 --- a/muse2/muse/driver/jackaudio.h +++ b/muse2/muse/driver/jackaudio.h @@ -40,13 +40,16 @@ class MidiPlayEvent; class JackAudioDevice : public AudioDevice { jack_client_t* _client; - double sampleTime; - int samplePos; + //double sampleTime; + //int samplePos; + float _syncTimeout; jack_transport_state_t transportState; jack_position_t pos; char jackRegisteredName[16]; int dummyState; int dummyPos; + volatile int _dummyStatePending; + volatile int _dummyPosPending; // Free-running frame counter incremented always in process. jack_nframes_t _frameCounter; @@ -58,14 +61,13 @@ class JackAudioDevice : public AudioDevice { virtual ~JackAudioDevice(); virtual void nullify_client() { _client = 0; } - virtual inline int deviceType() const { return JACK_AUDIO; } // p3.3.52 + virtual inline int deviceType() const { return JACK_AUDIO; } void scanMidiPorts(); //virtual void start(); virtual void start(int); virtual void stop (); - virtual bool dummySync(int state); // Artificial sync when not using Jack transport. virtual int framePos() const; virtual unsigned frameTime() const { return _frameCounter; } @@ -80,8 +82,6 @@ class JackAudioDevice : public AudioDevice { virtual void registerClient(); virtual const char* clientName() { return jackRegisteredName; } - //virtual void* registerOutPort(const char* name); - //virtual void* registerInPort(const char* name); virtual void* registerOutPort(const char* /*name*/, bool /*midi*/); virtual void* registerInPort(const char* /*name*/, bool /*midi*/); diff --git a/muse2/muse/driver/jackmidi.cpp b/muse2/muse/driver/jackmidi.cpp index 7a12b92d..706fa269 100644 --- a/muse2/muse/driver/jackmidi.cpp +++ b/muse2/muse/driver/jackmidi.cpp @@ -24,6 +24,7 @@ #include <QString> #include <stdio.h> +#include <string.h> #include <jack/jack.h> //#include <jack/midiport.h> @@ -43,6 +44,8 @@ #include "../mplugins/midiitransform.h" #include "../mplugins/mitplugin.h" #include "xml.h" +#include "gconfig.h" +#include "track.h" // Turn on debug messages. //#define JACK_MIDI_DEBUG @@ -336,10 +339,10 @@ void MidiJackDevice::recordEvent(MidiRecordEvent& event) event.setLoopNum(MusEGlobal::audio->loopCount()); if (MusEGlobal::midiInputTrace) { - printf("Jack MidiInput: "); + printf("MidiIn Jack: <%s>: ", name().toLatin1().constData()); event.dump(); } - + int typ = event.type(); if(_port != -1) @@ -431,7 +434,8 @@ void MidiJackDevice::eventReceived(jack_midi_event_t* ev) { MidiRecordEvent event; event.setB(0); - + event.setPort(_port); + // NOTE: From muse_qt4_evolution. Not done here in Muse-2 (yet). // move all events 2*MusEGlobal::segmentSize into the future to get // jitterfree playback @@ -452,6 +456,7 @@ void MidiJackDevice::eventReceived(jack_midi_event_t* ev) int a = *(ev->buffer + 1) & 0x7f; int b = *(ev->buffer + 2) & 0x7f; event.setType(type); + switch(type) { case ME_NOTEON: case ME_NOTEOFF: @@ -494,7 +499,7 @@ void MidiJackDevice::eventReceived(jack_midi_event_t* ev) return; case ME_SONGPOS: if(_port != -1) - MusEGlobal::midiSeq->setSongPosition(_port, *(ev->buffer + 1) | (*(ev->buffer + 2) >> 2 )); // LSB then MSB + MusEGlobal::midiSeq->setSongPosition(_port, *(ev->buffer + 1) | (*(ev->buffer + 2) << 7 )); // LSB then MSB return; //case ME_SONGSEL: //case ME_TUNE_REQ: @@ -525,11 +530,6 @@ void MidiJackDevice::eventReceived(jack_midi_event_t* ev) return; } - if (MusEGlobal::midiInputTrace) { - printf("MidiInput<%s>: ", name().toLatin1().constData()); - event.dump(); - } - #ifdef JACK_MIDI_DEBUG printf("MidiJackDevice::eventReceived time:%d type:%d ch:%d A:%d B:%d\n", event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); #endif @@ -600,14 +600,6 @@ bool MidiJackDevice::queueEvent(const MidiPlayEvent& e) //if(port >= JACK_MIDI_CHANNELS) // return false; - //if (midiOutputTrace) { - // printf("MidiOut<%s>: jackMidi: ", portName(port).toLatin1().constData()); - // e.dump(); - // } - - //if(MusEGlobal::debugMsg) - // printf("MidiJackDevice::queueEvent\n"); - if(!_out_client_jackport) return false; void* pb = jack_port_get_buffer(_out_client_jackport, MusEGlobal::segmentSize); @@ -626,9 +618,14 @@ bool MidiJackDevice::queueEvent(const MidiPlayEvent& e) } #ifdef JACK_MIDI_DEBUG - printf("MidiJackDevice::queueEvent time:%d type:%d ch:%d A:%d B:%d\n", e.time(), e.type(), e.channel(), e.dataA(), e.dataB()); + printf("MidiJackDevice::queueEvent pos:%d fo:%d ft:%d time:%d type:%d ch:%d A:%d B:%d\n", pos, frameOffset, ft, e.time(), e.type(), e.channel(), e.dataA(), e.dataB()); #endif + if (MusEGlobal::midiOutputTrace) { + printf("MidiOut: Jack: <%s>: ", name().toLatin1().constData()); + e.dump(); + } + switch(e.type()) { case ME_NOTEON: case ME_NOTEOFF: @@ -698,14 +695,47 @@ bool MidiJackDevice::queueEvent(const MidiPlayEvent& e) } break; case ME_SONGPOS: + { + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::queueEvent songpos %d\n", e.dataA()); + #endif + + unsigned char* p = jack_midi_event_reserve(pb, ft, 3); + if (p == 0) { + #ifdef JACK_MIDI_DEBUG + fprintf(stderr, "MidiJackDevice::queueEvent songpos: buffer overflow, stopping until next cycle\n"); + #endif + return false; + } + int pos = e.dataA(); + p[0] = e.type(); + p[1] = pos & 0x7f; // LSB + p[2] = (pos >> 7) & 0x7f; // MSB + } + break; case ME_CLOCK: case ME_START: case ME_CONTINUE: case ME_STOP: + { + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::queueEvent realtime %x\n", e.type()); + #endif + + unsigned char* p = jack_midi_event_reserve(pb, ft, 1); + if (p == 0) { + #ifdef JACK_MIDI_DEBUG + fprintf(stderr, "MidiJackDevice::queueEvent realtime: buffer overflow, stopping until next cycle\n"); + #endif + return false; + } + p[0] = e.type(); + } + break; + default: if(MusEGlobal::debugMsg) printf("MidiJackDevice::queueEvent: event type %x not supported\n", e.type()); - //return false; - return true; // Absorb the event. Don't want it hanging around in the list. FIXME: Support these? p4.0.15 Tim. + return true; // Absorb the event. Don't want it hanging around in the list. break; } @@ -757,12 +787,18 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event) // event.time(), event.type(), event.channel(), event.dataA(), event.dataB(), hb, lb, pr); if (hb != 0xff) + { if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HBANK, hb))) return false; // p4.0.15 Inform that processing the event in general failed. Ditto all below... +/// t += 1; + } if (lb != 0xff) - if(!queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LBANK, lb))) + { + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LBANK, lb))) return false; - if(!queueEvent(MidiPlayEvent(t+2, port, chn, ME_PROGRAM, pr, 0))) +/// t += 1; + } + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PROGRAM, pr, 0))) return false; // } @@ -815,13 +851,15 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event) { if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HBANK, hb))) return false; +/// t += 1; } if (lb != 0xff) { - if(!queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LBANK, lb))) + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LBANK, lb))) return false; +/// t += 1; } - if(!queueEvent(MidiPlayEvent(t+2, port, chn, ME_PROGRAM, pr, 0))) + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PROGRAM, pr, 0))) return false; // } @@ -851,7 +889,8 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event) int dataL = b & 0x7f; if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, ctrlH, dataH))) return false; - if(!queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, ctrlL, dataL))) +/// t += 1; + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, ctrlL, dataL))) return false; } else if (a < CTRL_NRPN_OFFSET) @@ -860,19 +899,22 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event) int ctrlL = a & 0x7f; if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH))) return false; - if(!queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL))) +/// t += 1; + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL))) return false; - if(!queueEvent(MidiPlayEvent(t+2, port, chn, ME_CONTROLLER, CTRL_HDATA, b))) +/// t += 1; + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, b))) return false; +/// t += 1; - t += 3; + //t += 3; // Select null parameters so that subsequent data controller events do not upset the last *RPN controller. //sendNullRPNParams(chn, false); if(nvh != 0xff) { if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, nvh & 0x7f))) return false; - t += 1; +/// t += 1; } if(nvl != 0xff) { @@ -887,18 +929,21 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event) int ctrlL = a & 0x7f; if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH))) return false; - if(!queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL))) +/// t += 1; + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL))) return false; - if(!queueEvent(MidiPlayEvent(t+2, port, chn, ME_CONTROLLER, CTRL_HDATA, b))) +/// t += 1; + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, b))) return false; +/// t += 1; - t += 3; + //t += 3; //sendNullRPNParams(chn, true); if(nvh != 0xff) { if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, nvh & 0x7f))) return false; - t += 1; +/// t += 1; } if(nvl != 0xff) { @@ -914,20 +959,24 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event) int dataL = b & 0x7f; if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH))) return false; - if(!queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL))) +/// t += 1; + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL))) return false; - if(!queueEvent(MidiPlayEvent(t+2, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH))) +/// t += 1; + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH))) return false; - if(!queueEvent(MidiPlayEvent(t+3, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL))) +/// t += 1; + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL))) return false; +/// t += 1; - t += 4; + //t += 4; //sendNullRPNParams(chn, false); if(nvh != 0xff) { if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, nvh & 0x7f))) return false; - t += 1; +/// t += 1; } if(nvl != 0xff) { @@ -943,20 +992,24 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event) int dataL = b & 0x7f; if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH))) return false; - if(!queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL))) +/// t += 1; + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL))) return false; - if(!queueEvent(MidiPlayEvent(t+2, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH))) +/// t += 1; + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH))) return false; - if(!queueEvent(MidiPlayEvent(t+3, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL))) +/// t += 1; + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL))) return false; +/// t += 1; - t += 4; + //t += 4; //sendNullRPNParams(chn, true); if(nvh != 0xff) { if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, nvh & 0x7f))) return false; - t += 1; +/// t += 1; } if(nvl != 0xff) { @@ -988,8 +1041,16 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event) void MidiJackDevice::processMidi() { + //bool stop = stopPending; // Snapshots + //bool seek = seekPending; // + //seekPending = stopPending = false; + processStuckNotes(); + // Don't process if the device is not assigned to a port. + //if(_port == -1) + // return; + void* port_buf = 0; if(_out_client_jackport && _writeEnable) { @@ -997,65 +1058,294 @@ void MidiJackDevice::processMidi() jack_midi_clear_buffer(port_buf); } + int port = midiPort(); + MidiPort* mp = port == -1 ? 0 : &MusEGlobal::midiPorts[port]; + + /* + bool is_playing = MusEGlobal::audio->isPlaying(); // TODO Check this. It includes LOOP1 and LOOP2 besides PLAY. + //bool is_playing = MusEGlobal::audio->isPlaying() || MusEGlobal::audio->isStarting(); + int pos = MusEGlobal::audio->tickPos(); + bool ext_sync = MusEGlobal::extSyncFlag.value(); + + if(mp) + { + MidiSyncInfo& si = mp->syncInfo(); + if(stop) + { + // Don't send if external sync is on. The master, and our sync routing system will take care of that. + if(!ext_sync) + { + // Shall we check open flags? + //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) + //if(!(dev->openFlags() & 1)) + // return; + + // Send MMC stop... + if(si.MMCOut()) + { + unsigned char msg[mmcStopMsgLen]; + memcpy(msg, mmcStopMsg, mmcStopMsgLen); + msg[1] = si.idOut(); + putEvent(MidiPlayEvent(0, 0, ME_SYSEX, msg, mmcStopMsgLen)); + } + + // Send midi stop... + if(si.MRTOut()) + { + putEvent(MidiPlayEvent(0, 0, 0, ME_STOP, 0, 0)); + // Added check of option send continue not start. p3.3.31 + // Hmm, is this required? Seems to make other devices unhappy. + // (Could try now that this is in MidiDevice. p4.0.22 ) + //if(!si.sendContNotStart()) + // mp->sendSongpos(MusEGlobal::audio->tickPos() * 4 / config.division); + } + } + } + + if(seek) + { + // Don't send if external sync is on. The master, and our sync routing system will take care of that. + if(!ext_sync) + { + // Send midi stop and song position pointer... + if(si.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; + putEvent(MidiPlayEvent(0, 0, 0, ME_STOP, 0, 0)); + // Hm, try scheduling these for after stuck notes scheduled below... + //putEvent(MidiPlayEvent(0, 0, 0, ME_SONGPOS, beat, 0)); + //if(is_playing) + // putEvent(MidiPlayEvent(0, 0, 0, ME_CONTINUE, 0, 0)); + } + } + } + } + + if(stop || (seek && is_playing)) + { + // Clear all notes and handle stuck notes... + _playEvents.clear(); + for(iMPEvent i = _stuckNotes.begin(); i != _stuckNotes.end(); ++i) + { + MidiPlayEvent ev = *i; + ev.setTime(0); // Schedule immediately. + putEvent(ev); + } + _stuckNotes.clear(); + } + + if(mp) + { + MidiSyncInfo& si = mp->syncInfo(); + // Try scheduling these now for after stuck notes scheduled above... + if(stop || seek) + { + // Reset sustain. + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) + putEvent(MidiPlayEvent(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0)); + } + if(seek) + { + // Send new song position. + if(!ext_sync && si.MRTOut()) + { + int beat = (pos * 4) / MusEGlobal::config.division; + putEvent(MidiPlayEvent(0, 0, 0, ME_SONGPOS, beat, 0)); + } + // Send new controller values. + MidiCtrlValListList* cll = mp->controller(); + for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) + { + MidiCtrlValList* vl = ivl->second; + iMidiCtrlVal imcv = vl->iValue(pos); + if(imcv != vl->end()) { + Part* p = imcv->second.part; + // Don't send if part or track is muted or off. + if(!p || p->mute()) + continue; + Track* track = p->track(); + if(track && (track->isMute() || track->off())) + continue; + unsigned t = (unsigned)imcv->first; + // Do not add values that are outside of the part. + if(t >= p->tick() && t < (p->tick() + p->lenTick())) + // Use sendEvent to get the optimizations and limiting. But force if there's a value at this exact position. + mp->sendEvent(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val), imcv->first == pos); + } + } + // Send continue. + // REMOVE Tim. This is redundant and too early - Audio::startRolling already properly sends it when sync ready. + //if(is_playing && !ext_sync && si.MRTOut()) + // putEvent(MidiPlayEvent(0, 0, 0, ME_CONTINUE, 0, 0)); + } + } + */ + while(!eventFifo.isEmpty()) { MidiPlayEvent e(eventFifo.peek()); + //printf("MidiJackDevice::processMidi FIFO event time:%d type:%d ch:%d A:%d B:%d\n", e.time(), e.type(), e.channel(), e.dataA(), e.dataB()); // Try to process only until full, keep rest for next cycle. If no out client port or no write enable, eat up events. p4.0.15 if(port_buf && !processEvent(e)) return; // Give up. The Jack buffer is full. Nothing left to do. eventFifo.remove(); // Successfully processed event. Remove it from FIFO. } + //if(!(stop || (seek && is_playing))) + // processStuckNotes(); + if(_playEvents.empty()) - { - //printf("MidiJackDevice::processMidi play events empty\n"); return; - } iMPEvent i = _playEvents.begin(); for(; i != _playEvents.end(); ++i) { //printf("MidiJackDevice::processMidi playEvent time:%d type:%d ch:%d A:%d B:%d\n", i->time(), i->type(), i->channel(), i->dataA(), i->dataB()); // Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values. - // Same code as in MidiPort::sendEvent() - if(_port != -1) - { - MidiPort* mp = &MusEGlobal::midiPorts[_port]; - if(i->type() == ME_CONTROLLER) - { - int da = i->dataA(); - int db = i->dataB(); - db = mp->limitValToInstrCtlRange(da, db); - if(!mp->setHwCtrlState(i->channel(), da, db)) - continue; - //mp->setHwCtrlState(i->channel(), da, db); - } - else - if(i->type() == ME_PITCHBEND) - { - //printf("MidiJackDevice::processMidi playEvents ME_PITCHBEND time:%d type:%d ch:%d A:%d B:%d\n", (*i).time(), (*i).type(), (*i).channel(), (*i).dataA(), (*i).dataB()); - int da = mp->limitValToInstrCtlRange(CTRL_PITCH, i->dataA()); - if(!mp->setHwCtrlState(i->channel(), CTRL_PITCH, da)) - continue; - //mp->setHwCtrlState(i->channel(), CTRL_PITCH, da); - //(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f)); - } - else - if(i->type() == ME_PROGRAM) - { - if(!mp->setHwCtrlState(i->channel(), CTRL_PROGRAM, i->dataA())) - continue; - //mp->setHwCtrlState(i->channel(), CTRL_PROGRAM, i->dataA()); - } - } + if(mp && !mp->sendHwCtrlState(*i, true)) // Force the event to be sent. + continue; // Try to process only until full, keep rest for next cycle. If no out client port or no write enable, eat up events. p4.0.15 if(port_buf && !processEvent(*i)) break; } _playEvents.erase(_playEvents.begin(), i); +} + +/* +//--------------------------------------------------------- +// handleStop +//--------------------------------------------------------- + +void MidiJackDevice::handleStop() +{ + // If the device is not in use by a port, don't bother it. + if(_port == -1) + return; + + stopPending = true; // Trigger stop handling in processMidi. +// //--------------------------------------------------- +// // reset sustain +// //--------------------------------------------------- +// +// MidiPort* mp = &MusEGlobal::midiPorts[_port]; +// for(int ch = 0; ch < MIDI_CHANNELS; ++ch) +// { +// if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) +// { +// //printf("send clear sustain!!!!!!!! port %d ch %d\n", i,ch); +// MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); +// putEvent(ev); +// // Do sendEvent to get the optimizations - send only on a change of value. +// //mp->sendEvent(ev); +// } +// } + +// //--------------------------------------------------- +// // send midi 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()) +// { +// // Shall we check open flags? +// //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) +// //if(!(dev->openFlags() & 1)) +// // return; +// +// MidiSyncInfo& si = mp->syncInfo(); +// if(si.MMCOut()) +// mp->sendMMCStop(); +// +// if(si.MRTOut()) +// { +// // Send STOP +// mp->sendStop(); +// +// // Added check of option send continue not start. p3.3.31 +// // Hmm, is this required? Seems to make other devices unhappy. +// // (Could try now that this is in MidiDevice. p4.0.22 ) +// //if(!si.sendContNotStart()) +// // mp->sendSongpos(MusEGlobal::audio->tickPos() * 4 / config.division); +// } +// } +} +*/ + +/* +//--------------------------------------------------------- +// handleSeek +//--------------------------------------------------------- + +void MidiJackDevice::handleSeek() +{ + // If the device is not in use by a port, don't bother it. + if(_port == -1) + return; + + seekPending = true; // Trigger seek handling in processMidi. + + //MidiPort* mp = &MusEGlobal::midiPorts[_port]; + //MidiCtrlValListList* cll = mp->controller(); + //int pos = MusEGlobal::audio->tickPos(); + + //--------------------------------------------------- + // Send new contoller values + //--------------------------------------------------- + +// for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) +// { +// MidiCtrlValList* vl = ivl->second; +// iMidiCtrlVal imcv = vl->iValue(pos); +// if(imcv != vl->end()) +// { +// Part* p = imcv->second.part; +// //printf("MidiAlsaDevice::handleSeek _port:%d ctl:%d num:%d val:%d\n", _port, ivl->first >> 24, vl->num(), imcv->second.val); +// unsigned t = (unsigned)imcv->first; +// // Do not add values that are outside of the part. +// if(p && t >= p->tick() && t < (p->tick() + p->lenTick()) ) +// // Keep this and the section in processMidi() just in case we need to revert... +// //_playEvents.add(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val)); +// // Hmm, play event list for immediate playback? Try putEvent, putMidiEvent, or sendEvent (for the optimizations) instead. +// mp->sendEvent(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val)); +// } +// } + + //--------------------------------------------------- + // 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. p3.3.31 +// if(!MusEGlobal::extSyncFlag.value()) +// { +// if(mp->syncInfo().MRTOut()) +// { +// // Shall we check for device write open flag to see if it's ok to send?... +// // This means obey what the user has chosen for read/write in the midi port config dialog, +// // which already takes into account whether the device is writable or not. +// //if(!(rwFlags() & 0x1) || !(openFlags() & 1)) +// //if(!(openFlags() & 1)) +// // continue; +// +// int beat = (pos * 4) / MusEGlobal::config.division; +// +// //bool isPlaying = (state == PLAY); +// bool isPlaying = MusEGlobal::audio->isPlaying(); // TODO Check this it includes LOOP1 and LOOP2 besides PLAY. p4.0.22 +// +// mp->sendStop(); +// mp->sendSongpos(beat); +// // REMOVE Tim. This is redundant and too early - Audio::startRolling already properly sends it when sync ready. +// //if(isPlaying) +// // mp->sendContinue(); +// } +// } } +*/ //--------------------------------------------------------- // initMidiJack diff --git a/muse2/muse/driver/jackmidi.h b/muse2/muse/driver/jackmidi.h index d2c14190..c8f7f901 100644 --- a/muse2/muse/driver/jackmidi.h +++ b/muse2/muse/driver/jackmidi.h @@ -74,13 +74,15 @@ class MidiJackDevice : public MidiDevice { public: MidiJackDevice(const QString& name); + virtual ~MidiJackDevice(); static MidiDevice* createJackMidiDevice(QString name = "", int rwflags = 3); // 1:Writable 2: Readable 3: Writable + Readable virtual inline int deviceType() const { return JACK_MIDI; } virtual void setName(const QString&); + //virtual void handleStop(); + //virtual void handleSeek(); virtual void processMidi(); - virtual ~MidiJackDevice(); virtual void recordEvent(MidiRecordEvent&); diff --git a/muse2/muse/driver/rtctimer.cpp b/muse2/muse/driver/rtctimer.cpp index 365b8501..c50fadf6 100644 --- a/muse2/muse/driver/rtctimer.cpp +++ b/muse2/muse/driver/rtctimer.cpp @@ -99,9 +99,9 @@ unsigned int RtcTimer::setTimerFreq(unsigned int freq) { int rc = ioctl(timerFd, RTC_IRQP_SET, freq); if (rc == -1) { - fprintf(stderr, "RtcTimer::setTimerFreq(): cannot set tick on /dev/rtc: %s\n", + fprintf(stderr, "RtcTimer::setTimerFreq(): cannot set freq %d on /dev/rtc: %s\n", freq, strerror(errno)); - fprintf(stderr, " precise timer not available\n"); + fprintf(stderr, " precise timer not available, check file permissions and allowed RTC freq (/sys/class/rtc/rtc0/max_user_freq)\n"); return 0; } return freq; diff --git a/muse2/muse/driver/rtctimer.h b/muse2/muse/driver/rtctimer.h index 425ea643..2b1921a2 100644 --- a/muse2/muse/driver/rtctimer.h +++ b/muse2/muse/driver/rtctimer.h @@ -1,12 +1,12 @@ - //========================================================= - // MusE - // Linux Music Editor - // $Id: rtctimer.h,v 1.1.2.3 2005/08/21 18:11:28 spamatica Exp $ - // - // Most code moved from midiseq.cpp - // - // (C) Copyright -2004 Werner Schweer (werner@seh.de) - // (C) Copyright 2004 Robert Jonsson (rj@spamatica.se) +//========================================================= +// MusE +// Linux Music Editor +// $Id: rtctimer.h,v 1.1.2.3 2005/08/21 18:11:28 spamatica Exp $ +// +// Most code moved from midiseq.cpp +// +// (C) Copyright -2004 Werner Schweer (werner@seh.de) +// (C) Copyright 2004 Robert Jonsson (rj@spamatica.se) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -22,7 +22,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // - //========================================================= +//========================================================= #ifndef __RTCTIMER_H__ #define __RTCTIMER_H__ @@ -59,4 +59,4 @@ class RtcTimer : public Timer{ } // namespace MusECore -#endif //__ALSATIMER_H__ +#endif //__RTCTIMER_H__ |