diff options
-rw-r--r-- | muse/ChangeLog | 3 | ||||
-rw-r--r-- | muse/muse/driver/audiodev.h | 5 | ||||
-rw-r--r-- | muse/muse/driver/dummyaudio.cpp | 9 | ||||
-rw-r--r-- | muse/muse/driver/jack.cpp | 156 | ||||
-rw-r--r-- | muse/muse/driver/jackaudio.h | 10 | ||||
-rw-r--r-- | muse/muse/driver/jackmidi.cpp | 126 |
6 files changed, 296 insertions, 13 deletions
diff --git a/muse/ChangeLog b/muse/ChangeLog index 2a796b0d..5cf866d1 100644 --- a/muse/ChangeLog +++ b/muse/ChangeLog @@ -1,3 +1,6 @@ +01.02.2010 + * Fixed: Jack midi output: Stuck or missing notes. (T356) + - Must only be one jack_midi_event_reserve() per event in handle_jack_midi_out_events(). 31.01.2010 * Changed: Midi sync window: Clock is now seperated from other real time commands (play/stop etc). (T356) 31.01.2010 diff --git a/muse/muse/driver/audiodev.h b/muse/muse/driver/audiodev.h index d3dfda10..437b1eee 100644 --- a/muse/muse/driver/audiodev.h +++ b/muse/muse/driver/audiodev.h @@ -12,6 +12,8 @@ #include <qstring.h> #include <list> +class MidiPlayEvent; + //--------------------------------------------------------- // AudioDevice //--------------------------------------------------------- @@ -27,6 +29,7 @@ class AudioDevice { virtual void stop () = 0; virtual int framePos() const = 0; + virtual unsigned frameTime() const = 0; virtual float* getBuffer(void* port, unsigned long nframes) = 0; @@ -54,6 +57,8 @@ class AudioDevice { virtual void setFreewheel(bool f) = 0; virtual void graphChanged() {} virtual int setMaster(bool f) = 0; + + virtual bool putEvent(int port, const MidiPlayEvent&) = 0; }; #endif diff --git a/muse/muse/driver/dummyaudio.cpp b/muse/muse/driver/dummyaudio.cpp index 5007485c..89fbc064 100644 --- a/muse/muse/driver/dummyaudio.cpp +++ b/muse/muse/driver/dummyaudio.cpp @@ -7,6 +7,7 @@ #include <stdio.h> #include <stdlib.h> +#include <math.h> #include <errno.h> #include <stdarg.h> #include <pthread.h> @@ -20,6 +21,9 @@ #include "driver/alsatimer.h" #include "pos.h" #include "gconfig.h" +#include "utils.h" + +class MidiPlayEvent; #define DEBUG_DUMMY 0 //--------------------------------------------------------- @@ -113,6 +117,9 @@ class DummyAudioDevice : public AudioDevice { printf("DummyAudioDevice::getCurFrame %d\n", _framePos); return _framePos; } + virtual unsigned frameTime() const { + return lrint(curTime() * sampleRate); + } virtual bool isRealtime() { return realtimeFlag; } //virtual int realtimePriority() const { return 40; } virtual int realtimePriority() const { return _realTimePriority; } @@ -160,6 +167,8 @@ class DummyAudioDevice : public AudioDevice { } virtual void setFreewheel(bool) {} void setRealTime() { realtimeFlag = true; } + + virtual bool putEvent(int port, const MidiPlayEvent&) { }; }; DummyAudioDevice* dummyAudio = 0; diff --git a/muse/muse/driver/jack.cpp b/muse/muse/driver/jack.cpp index 4674f460..41092cea 100644 --- a/muse/muse/driver/jack.cpp +++ b/muse/muse/driver/jack.cpp @@ -12,6 +12,7 @@ //#include <time.h> #include <unistd.h> #include <jack/midiport.h> +#include <string.h> #include "audio.h" #include "globals.h" @@ -23,6 +24,9 @@ #include "sync.h" #include "utils.h" +#include "midi.h" +#include "mpevent.h" + #include "jackmidi.h" @@ -48,6 +52,10 @@ 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]; +// p3.3.32 +//jack_port_t *jackMidiInPort = 0; +//jack_port_t *jackMidiOutPort = 0; + JackAudioDevice* jackAudio; //--------------------------------------------------------- @@ -181,7 +189,8 @@ void handle_jack_midi_out_events(jack_nframes_t frames) void *port_buf; int i,j,n,x; - for(i = 0; i < JACK_MIDI_CHANNELS; i++){ + //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); @@ -207,26 +216,54 @@ void handle_jack_midi_out_events(jack_nframes_t frames) /* 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' */ + // 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*) +//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) @@ -397,6 +434,7 @@ static void noJackError(const char* /* s */) JackAudioDevice::JackAudioDevice(jack_client_t* cl, char * name) : AudioDevice() { + _frameCounter = 0; //JackAudioDevice::jackStarted=false; strcpy(jackRegisteredName, name); _client = cl; @@ -527,6 +565,28 @@ bool initJackAudio() fprintf(stderr, "WARNING NO muse-jack midi connection\n"); } + + /* + 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); + } + */ + + if (client) { audioDevice = jackAudio; return false; @@ -1406,3 +1466,91 @@ int JackAudioDevice::setMaster(bool f) } return r; } + + +//--------------------------------------------------------- +// putEvent +// return true if successful +//--------------------------------------------------------- + +//void JackAudioDevice::putEvent(Port port, const MidiEvent& e) +bool JackAudioDevice::putEvent(int port, const MidiPlayEvent& e) + { + if(port >= JACK_MIDI_CHANNELS) + return false; + + //if (midiOutputTrace) { + // printf("MidiOut<%s>: jackMidi: ", portName(port).toLatin1().data()); + // e.dump(); + // } + + //void* pb = jack_port_get_buffer(port.jackPort(), segmentSize); + void* pb = jack_port_get_buffer(midi_port_out[port], segmentSize); + + int ft = e.time() - _frameCounter; + + if (ft < 0) + ft = 0; + if (ft >= (int)segmentSize) { + printf("JackAudio::putEvent: time out of range %d(seg=%d)\n", ft, segmentSize); + if (ft > (int)segmentSize) + ft = segmentSize - 1; + } + switch(e.type()) { + case ME_NOTEON: + case ME_NOTEOFF: + case ME_POLYAFTER: + case ME_CONTROLLER: + case ME_PITCHBEND: + { + unsigned char* p = jack_midi_event_reserve(pb, ft, 3); + if (p == 0) { + fprintf(stderr, "JackMidi: buffer overflow, event lost\n"); + return false; + } + p[0] = e.type() | e.channel(); + p[1] = e.dataA(); + p[2] = e.dataB(); + } + break; + + case ME_PROGRAM: + case ME_AFTERTOUCH: + { + unsigned char* p = jack_midi_event_reserve(pb, ft, 2); + if (p == 0) { + fprintf(stderr, "JackMidi: buffer overflow, event lost\n"); + return false; + } + p[0] = e.type() | e.channel(); + p[1] = e.dataA(); + } + break; + case ME_SYSEX: + { + const unsigned char* data = e.data(); + int len = e.len(); + unsigned char* p = jack_midi_event_reserve(pb, ft, len+2); + if (p == 0) { + fprintf(stderr, "JackMidi: buffer overflow, event lost\n"); + return false; + } + p[0] = 0xf0; + p[len+1] = 0xf7; + memcpy(p+1, data, len); + } + break; + case ME_SONGPOS: + case ME_CLOCK: + case ME_START: + case ME_CONTINUE: + case ME_STOP: + printf("JackMidi: event type %x not supported\n", e.type()); + return false; + break; + } + + return true; + } + + diff --git a/muse/muse/driver/jackaudio.h b/muse/muse/driver/jackaudio.h index 7a73eaf5..76753eec 100644 --- a/muse/muse/driver/jackaudio.h +++ b/muse/muse/driver/jackaudio.h @@ -11,6 +11,8 @@ #include <jack/jack.h> #include "audiodev.h" +class MidiPlayEvent; + //--------------------------------------------------------- // JackAudioDevice //--------------------------------------------------------- @@ -26,6 +28,10 @@ class JackAudioDevice : public AudioDevice { char jackRegisteredName[8]; int dummyState; int dummyPos; + // Free-running frame counter incremented always in process. + jack_nframes_t _frameCounter; + + static int processAudio(jack_nframes_t frames, void*); public: JackAudioDevice(jack_client_t* cl, char * jack_id_string); @@ -38,6 +44,7 @@ class JackAudioDevice : public AudioDevice { virtual bool dummySync(int state); // Artificial sync when not using Jack transport. virtual int framePos() const; + virtual unsigned frameTime() const { return _frameCounter; } virtual float* getBuffer(void* port, unsigned long nframes) { return (float*)jack_port_get_buffer((jack_port_t*)port, nframes); @@ -72,6 +79,9 @@ class JackAudioDevice : public AudioDevice { void graphChanged(); virtual int setMaster(bool f); + // Port is not midi port, it is the port(s) created for MusE. + virtual bool putEvent(int port, const MidiPlayEvent&); + //static bool jackStarted; }; diff --git a/muse/muse/driver/jackmidi.cpp b/muse/muse/driver/jackmidi.cpp index fd9c87ce..b4e88683 100644 --- a/muse/muse/driver/jackmidi.cpp +++ b/muse/muse/driver/jackmidi.cpp @@ -95,17 +95,17 @@ bool MidiJackDevice::putMidiEvent(const MidiPlayEvent& event) if(channel >= JACK_MIDI_CHANNELS) return false; - /* buffer up events, because jack eats them in chunks, if - * the buffer is full, there isn't so much to do, than - * drop the event - */ + // buffer up events, because jack eats them in chunks, if + // the buffer is full, there isn't so much to do, than + // drop the event + give = jack_midi_out_data[channel].give; if(jack_midi_out_data[channel].buffer[give*4+3]){ fprintf(stderr, "WARNING: muse-to-jack midi-buffer is full, channel=%u\n", channel); return false; } - /* copy event(note-on etc..), pitch and volume */ - /* see http://www.midi.org/techspecs/midimessages.php */ + // copy event(note-on etc..), pitch and volume + // see http://www.midi.org/techspecs/midimessages.php switch(event.type()){ case ME_NOTEOFF: jack_midi_out_data[channel].buffer[give*4+0] = 0x80; @@ -129,7 +129,7 @@ bool MidiJackDevice::putMidiEvent(const MidiPlayEvent& event) break; case ME_PITCHBEND: jack_midi_out_data[channel].buffer[give*4+0] = 0xE0; - /* convert muse pitch-bend to midi standard */ + // convert muse pitch-bend to midi standard x = 0x2000 + event.dataA(); jack_midi_out_data[channel].buffer[give*4+1] = x & 0x7f; jack_midi_out_data[channel].buffer[give*4+2] = (x >> 8) & 0x7f; @@ -138,14 +138,122 @@ bool MidiJackDevice::putMidiEvent(const MidiPlayEvent& event) fprintf(stderr, "jack-midi-out %u WARNING: unknown event %x\n", channel, event.type()); return false; } - jack_midi_out_data[channel].buffer[give*4+3] = 1; /* mark state of this slot */ - /* finally increase give position */ + jack_midi_out_data[channel].buffer[give*4+3] = 1; // mark state of this slot + // finally increase give position give++; if(give >= JACK_MIDI_BUFFER_SIZE){ give = 0; } jack_midi_out_data[channel].give = give; return false; + + /* + // + // NOTICE: Only one MusE port (port 0) is supported for now ! MusE has no mechanism to create + // or select other MusE ports. MusE ALSA midi only creates one port as well. + // + const int museport = 0; + if(event.type() == ME_CONTROLLER) + { + int a = event.dataA(); + int b = event.dataB(); + int chn = event.channel(); + unsigned t = event.time(); + + if(a == CTRL_PITCH) + { + int v = b + 8192; + audioDriver->putEvent(museport, MidiPlayEvent(t, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f)); + } + else if (a == CTRL_PROGRAM) + { + // don't output program changes for GM drum channel + //if (!(song->mtype() == MT_GM && chn == 9)) { + int hb = (b >> 16) & 0xff; + int lb = (b >> 8) & 0xff; + int pr = b & 0x7f; + if (hb != 0xff) + audioDriver->putEvent(museport, MidiPlayEvent(t, chn, ME_CONTROLLER, CTRL_HBANK, hb)); + if (lb != 0xff) + audioDriver->putEvent(museport, MidiPlayEvent(t+1, chn, ME_CONTROLLER, CTRL_LBANK, lb)); + audioDriver->putEvent(museport, MidiPlayEvent(t+2, chn, ME_PROGRAM, pr, 0)); + // } + } + else if (a == CTRL_MASTER_VOLUME) + { + unsigned char sysex[] = { + 0x7f, 0x7f, 0x04, 0x01, 0x00, 0x00 + }; + sysex[1] = deviceId(); + sysex[4] = b & 0x7f; + sysex[5] = (b >> 7) & 0x7f; + audioDriver->putEvent(museport, MidiPlayEvent(t, ME_SYSEX, sysex, 6)); + } + else if (a < CTRL_14_OFFSET) + { // 7 Bit Controller + audioDriver->putEvent(museport, event); + } + else if (a < CTRL_RPN_OFFSET) + { // 14 bit high resolution controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + int dataH = (b >> 7) & 0x7f; + int dataL = b & 0x7f; + audioDriver->putEvent(museport, MidiPlayEvent(t, chn, ME_CONTROLLER, ctrlH, dataH)); + audioDriver->putEvent(museport, MidiPlayEvent(t+1, chn, ME_CONTROLLER, ctrlL, dataL)); + } + else if (a < CTRL_NRPN_OFFSET) + { // RPN 7-Bit Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + audioDriver->putEvent(museport, MidiPlayEvent(t, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH)); + audioDriver->putEvent(museport, MidiPlayEvent(t+1, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL)); + audioDriver->putEvent(museport, MidiPlayEvent(t+2, chn, ME_CONTROLLER, CTRL_HDATA, b)); + } + else if (a < CTRL_RPN14_OFFSET) + { // NRPN 7-Bit Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + audioDriver->putEvent(museport, MidiPlayEvent(t, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH)); + audioDriver->putEvent(museport, MidiPlayEvent(t+1, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL)); + audioDriver->putEvent(museport, MidiPlayEvent(t+2, chn, ME_CONTROLLER, CTRL_HDATA, b)); + } + else if (a < CTRL_NRPN14_OFFSET) + { // RPN14 Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + int dataH = (b >> 7) & 0x7f; + int dataL = b & 0x7f; + audioDriver->putEvent(museport, MidiPlayEvent(t, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH)); + audioDriver->putEvent(museport, MidiPlayEvent(t+1, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL)); + audioDriver->putEvent(museport, MidiPlayEvent(t+2, chn, ME_CONTROLLER, CTRL_HDATA, dataH)); + audioDriver->putEvent(museport, MidiPlayEvent(t+3, chn, ME_CONTROLLER, CTRL_LDATA, dataL)); + } + else if (a < CTRL_NONE_OFFSET) + { // NRPN14 Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + int dataH = (b >> 7) & 0x7f; + int dataL = b & 0x7f; + audioDriver->putEvent(museport, MidiPlayEvent(t, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH)); + audioDriver->putEvent(museport, MidiPlayEvent(t+1, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL)); + audioDriver->putEvent(museport, MidiPlayEvent(t+2, chn, ME_CONTROLLER, CTRL_HDATA, dataH)); + audioDriver->putEvent(museport, MidiPlayEvent(t+3, chn, ME_CONTROLLER, CTRL_LDATA, dataL)); + } + else + { + printf("MidiJackDevice::putMidiEvent: unknown controller type 0x%x\n", a); + } + } + else + { + audioDriver->putEvent(museport, event); + } + + // Just return OK for now. + return false; + */ + } //--------------------------------------------------------- |