summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim E. Real <termtech@rogers.com>2010-01-28 03:15:12 +0000
committerTim E. Real <termtech@rogers.com>2010-01-28 03:15:12 +0000
commitfdea4486f8c9010d523e61ff040ca3d6711c4943 (patch)
tree9bf286d06c07aa9f180e082bf7f2e723c9d1ad28
parent4811a6644aa7d75519edb37460a1c45493b61bd5 (diff)
Initial Jack Midi support.
-rw-r--r--muse/ChangeLog1
-rw-r--r--muse/muse/driver/Makefile.am3
-rw-r--r--muse/muse/driver/jack.cpp153
-rw-r--r--muse/muse/driver/jackmidi.cpp289
-rw-r--r--muse/muse/driver/jackmidi.h59
-rw-r--r--muse/muse/mididev.cpp8
-rw-r--r--muse/muse/midiseq.cpp11
7 files changed, 518 insertions, 6 deletions
diff --git a/muse/ChangeLog b/muse/ChangeLog
index 2aa745d8..9d027c74 100644
--- a/muse/ChangeLog
+++ b/muse/ChangeLog
@@ -3,6 +3,7 @@
* Added: Arranger track list: Quick 'right-click' or 'ctrl-click' or 'ctrl-mouse-wheel' toggling of Track On/Off. (T356)
- Note this includes midi tracks now! Remains to be seen whether allowing midi off is useful and will work,
or should be filtered out. **TODO: Turn off remaining controls in midi strips, turn off actual midi playback and record.
+ * Feature: Initial Jack midi support. Imported from Larry Valkama's GIT repo. (T356)
26.01.2010
* Fixed: Import midi 'replace' broken last fixes. (T356)
* Fixed: External midi sync: Wait until first clock after start to start transport, and >= second clock to increment ticks. (T356)
diff --git a/muse/muse/driver/Makefile.am b/muse/muse/driver/Makefile.am
index 6f23ed43..689b6c95 100644
--- a/muse/muse/driver/Makefile.am
+++ b/muse/muse/driver/Makefile.am
@@ -5,5 +5,6 @@ noinst_LIBRARIES = libdriver.a
AM_CXXFLAGS += $(JACK_CFLAGS)
libdriver_a_SOURCES = audiodev.h alsamidi.cpp alsamidi.h jack.cpp jackaudio.h \
- dummyaudio.cpp alsatimer.cpp alsatimer.h timerdev.h rtctimer.cpp rtctimer.h
+ dummyaudio.cpp alsatimer.cpp alsatimer.h timerdev.h rtctimer.cpp rtctimer.h \
+ jackmidi.cpp
diff --git a/muse/muse/driver/jack.cpp b/muse/muse/driver/jack.cpp
index 396f6b35..4674f460 100644
--- a/muse/muse/driver/jack.cpp
+++ b/muse/muse/driver/jack.cpp
@@ -11,6 +11,7 @@
#include <stdarg.h>
//#include <time.h>
#include <unistd.h>
+#include <jack/midiport.h>
#include "audio.h"
#include "globals.h"
@@ -22,6 +23,9 @@
#include "sync.h"
#include "utils.h"
+#include "jackmidi.h"
+
+
#define JACK_DEBUG 0
//#include "errorhandler.h"
@@ -35,6 +39,15 @@ extern void undoSetuid();
#include <fst.h>
#endif
+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;
//---------------------------------------------------------
@@ -105,12 +118,117 @@ static void jack_thread_init (void* ) // data
}
//---------------------------------------------------------
-// processAudio
+// 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++){
+ /* 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;
+ }
+ }
+ }
+ }
+}
static int processAudio(jack_nframes_t frames, void*)
- {
+{
+ handle_jack_midi_in_events(frames);
+ handle_jack_midi_out_events(frames);
// if (JACK_DEBUG)
// printf("processAudio - >>>>\n");
segmentSize = frames;
@@ -122,8 +240,8 @@ static int processAudio(jack_nframes_t frames, void*)
}
// if (JACK_DEBUG)
// printf("processAudio - <<<<\n");
- return 0;
- }
+ return 0;
+}
//---------------------------------------------------------
// processSync
@@ -382,6 +500,33 @@ bool initJackAudio()
}
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) {
audioDevice = jackAudio;
return false;
diff --git a/muse/muse/driver/jackmidi.cpp b/muse/muse/driver/jackmidi.cpp
new file mode 100644
index 00000000..fd9c87ce
--- /dev/null
+++ b/muse/muse/driver/jackmidi.cpp
@@ -0,0 +1,289 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+// $Id: jackmidi.cpp,v 1.1.1.1 2010/01/27 09:06:43 terminator356 Exp $
+// (C) Copyright 1999-2010 Werner Schweer (ws@seh.de)
+//=========================================================
+
+#include <stdio.h>
+
+#include <jack/jack.h>
+#include <jack/midiport.h>
+
+#include "jackmidi.h"
+#include "globals.h"
+#include "midi.h"
+#include "mididev.h"
+#include "../midiport.h"
+#include "../midiseq.h"
+#include "../midictrl.h"
+#include "../audio.h"
+#include "mpevent.h"
+//#include "sync.h"
+
+int jackmidi_pi[2];
+int jackmidi_po[2];
+
+extern muse_jack_midi_buffer jack_midi_out_data[JACK_MIDI_CHANNELS];
+extern muse_jack_midi_buffer jack_midi_in_data[JACK_MIDI_CHANNELS];
+
+MidiJackDevice* gmdev = NULL;
+
+int* jackSeq;
+//static snd_seq_addr_t musePort;
+
+//---------------------------------------------------------
+// MidiAlsaDevice
+//---------------------------------------------------------
+
+MidiJackDevice::MidiJackDevice(const int& a, const QString& n)
+ : MidiDevice(n)
+{
+ adr = a;
+ init();
+}
+
+//---------------------------------------------------------
+// select[RW]fd
+//---------------------------------------------------------
+
+int MidiJackDevice::selectRfd()
+{
+ return jackmidi_pi[0];
+}
+
+int MidiJackDevice::selectWfd()
+{
+ return jackmidi_po[0];
+}
+
+//---------------------------------------------------------
+// open
+//---------------------------------------------------------
+
+QString MidiJackDevice::open()
+{
+ _readEnable = true;
+ _writeEnable = true;
+
+
+ return QString("OK");
+}
+
+//---------------------------------------------------------
+// close
+//---------------------------------------------------------
+
+void MidiJackDevice::close()
+{
+ _readEnable = false;
+ _writeEnable = false;
+}
+
+//---------------------------------------------------------
+// putEvent
+//---------------------------------------------------------
+
+/* FIX: if we fail to transmit the event,
+ * we return false (indicating OK). Otherwise
+ * it seems muse will retry forever
+ */
+bool MidiJackDevice::putMidiEvent(const MidiPlayEvent& event)
+{
+ int give, channel = event.channel();
+ int x;
+
+ 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
+ */
+ 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 */
+ switch(event.type()){
+ case ME_NOTEOFF:
+ jack_midi_out_data[channel].buffer[give*4+0] = 0x80;
+ jack_midi_out_data[channel].buffer[give*4+1] = event.dataA() & 0x7f;
+ jack_midi_out_data[channel].buffer[give*4+2] = event.dataB() & 0x7f;
+ break;
+ case ME_NOTEON:
+ jack_midi_out_data[channel].buffer[give*4+0] = 0x90;
+ jack_midi_out_data[channel].buffer[give*4+1] = event.dataA() & 0x7f;
+ jack_midi_out_data[channel].buffer[give*4+2] = event.dataB() & 0x7f;
+ break;
+ case ME_CONTROLLER:
+ jack_midi_out_data[channel].buffer[give*4+0] = 0xb0;
+ jack_midi_out_data[channel].buffer[give*4+1] = event.dataA() & 0x7f;
+ jack_midi_out_data[channel].buffer[give*4+2] = event.dataB() & 0x7f;
+ break;
+ case ME_PROGRAM:
+ jack_midi_out_data[channel].buffer[give*4+0] = 0xc0;
+ jack_midi_out_data[channel].buffer[give*4+1] = event.dataA() & 0x7f;
+ jack_midi_out_data[channel].buffer[give*4+2] = 0;
+ break;
+ case ME_PITCHBEND:
+ jack_midi_out_data[channel].buffer[give*4+0] = 0xE0;
+ /* 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;
+ break;
+ default:
+ 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 */
+ give++;
+ if(give >= JACK_MIDI_BUFFER_SIZE){
+ give = 0;
+ }
+ jack_midi_out_data[channel].give = give;
+ return false;
+}
+
+//---------------------------------------------------------
+// putEvent
+// return false if event is delivered
+//---------------------------------------------------------
+
+bool MidiJackDevice::putEvent(int* event)
+{
+ int *y; y = event;
+ return false;
+}
+
+//---------------------------------------------------------
+// initMidiJack
+// return true on error
+//---------------------------------------------------------
+
+bool initMidiJack()
+{
+ int adr = 0;
+
+ 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));
+
+ MidiJackDevice* dev = new MidiJackDevice(adr, QString("jack-midi"));
+ dev->setrwFlags(3); /* set read and write flags */
+ if(pipe(jackmidi_pi) < 0){
+ fprintf(stderr, "cant create midi-jack input pipe\n");
+ }
+ if(pipe(jackmidi_po) < 0){
+ fprintf(stderr, "cant create midi-jack output pipe\n");
+ }
+ midiDevices.add(dev);
+ gmdev = dev; /* proclaim the global jack-midi instance */
+
+ return false;
+}
+
+struct JackPort {
+ int adr;
+ char* name;
+ int flags;
+ JackPort(int a, const char* s, int f) {
+ adr = a;
+ name = strdup(s);
+ flags = f;
+ }
+ };
+
+static std::list<JackPort> portList;
+
+//---------------------------------------------------------
+// jackScanMidiPorts
+//---------------------------------------------------------
+
+void jackScanMidiPorts()
+{
+ int adr;
+ const char* name;
+
+ portList.clear();
+ adr = 0;
+ name = strdup("namex");
+ portList.push_back(JackPort(adr, name, 0));
+ //
+ // check for devices to add
+ //
+ for (std::list<JackPort>::iterator k = portList.begin(); k != portList.end(); ++k) {
+ iMidiDevice i = midiDevices.begin();
+ for (;i != midiDevices.end(); ++i) {
+ //MidiJackDevice* d = dynamic_cast<MidiJackDevice*>(*i);
+ break;
+ //if (d == 0) continue;
+ //if ((k->adr.client == d->adr.client) && (k->adr.port == d->adr.port)) {
+ // break;
+ //}
+ }
+ if (i == midiDevices.end()) {
+ // add device
+ MidiJackDevice* dev = new MidiJackDevice(k->adr, QString(k->name));
+ dev->setrwFlags(k->flags);
+ midiDevices.add(dev);
+ }
+ }
+}
+
+//---------------------------------------------------------
+// processInput
+//---------------------------------------------------------
+static void handle_jack_midi_in(int channel)
+{
+ MidiRecordEvent event;
+ int t,n,v;
+ t = jack_midi_in_data[channel].buffer[0];
+ n = jack_midi_in_data[channel].buffer[1];
+ v = jack_midi_in_data[channel].buffer[2];
+
+ event.setType(0); // mark as unused
+ event.setPort(gmdev->midiPort());
+ event.setB(0);
+
+ if(t == 0x90){ /* note on */
+ fprintf(stderr, "jackProcessMidiInput note-on\n");
+ event.setChannel(channel);
+ event.setType(ME_NOTEON);
+ event.setA(n);
+ event.setB(v);
+ }else if (t == 0x80){ /* note off */
+ fprintf(stderr, "jackProcessMidiInput note-off\n");
+ event.setChannel(channel);
+ event.setType(ME_NOTEOFF);
+ event.setA(n);
+ event.setB(v);
+ }else{
+ fprintf(stderr, "WARNING: unknown midi-in on channel %d: %x,%x,%x\n",
+ channel, t, n, v);
+ return;
+ }
+ if(event.type()){
+ gmdev->recordEvent(event);
+ midiPorts[gmdev->midiPort()].syncInfo().trigActDetect(event.channel());
+ }
+}
+
+void MidiJackDevice::processInput()
+{
+ char buf;
+ int i,s;
+ read(gmdev->selectRfd(), &buf, 1);
+
+ s = 1;
+ for(i = 0; i < JACK_MIDI_CHANNELS; i++){
+ if(jack_midi_in_data[i].buffer[3]){
+ s = 0;
+ handle_jack_midi_in(i);
+ jack_midi_in_data[i].buffer[3] = 0;
+ }
+ }
+}
+
diff --git a/muse/muse/driver/jackmidi.h b/muse/muse/driver/jackmidi.h
new file mode 100644
index 00000000..cf651714
--- /dev/null
+++ b/muse/muse/driver/jackmidi.h
@@ -0,0 +1,59 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+// $Id: jackmidi.h,v 1.1.1.1 2010/01/27 09:06:43 terminator356 Exp $
+// (C) Copyright 1999-2010 Werner Schweer (ws@seh.de)
+//=========================================================
+
+#ifndef __JACKMIDI_H__
+#define __JACKMIDI_H__
+
+#include <config.h>
+
+#include "mididev.h"
+
+/* jack-midi channels */
+#define JACK_MIDI_CHANNELS 32
+/* jack-midi buffer size */
+#define JACK_MIDI_BUFFER_SIZE 32
+
+typedef struct {
+ int give;
+ int take;
+ /* 32 parallel midi events, where each event contains three
+ * midi-bytes and one busy-byte */
+ char buffer[4 * JACK_MIDI_BUFFER_SIZE];
+} muse_jack_midi_buffer;
+
+//---------------------------------------------------------
+// MidiJackDevice
+//---------------------------------------------------------
+
+class MidiJackDevice : public MidiDevice {
+ public:
+ int adr;
+
+ private:
+ virtual QString open();
+ virtual void close();
+ bool putEvent(int*);
+ virtual bool putMidiEvent(const MidiPlayEvent&);
+
+ public:
+ MidiJackDevice() {}
+ MidiJackDevice(const int&, const QString& name);
+ virtual ~MidiJackDevice() {}
+ virtual int selectRfd();
+ virtual int selectWfd();
+ virtual void processInput();
+ };
+
+extern bool initMidiJack();
+extern int jackSelectRfd();
+extern int jackSelectWfd();
+extern void jackProcessMidiInput();
+extern void jackScanMidiPorts();
+
+#endif
+
+
diff --git a/muse/muse/mididev.cpp b/muse/muse/mididev.cpp
index aec9a51d..48a8fa9f 100644
--- a/muse/muse/mididev.cpp
+++ b/muse/muse/mididev.cpp
@@ -29,6 +29,7 @@
extern void initMidiSerial();
#endif
extern bool initMidiAlsa();
+extern bool initMidiJack();
MidiDeviceList midiDevices;
extern void processMidiInputTransformPlugins(MEvent&);
@@ -49,6 +50,13 @@ void initMidiDevices()
"your configuration.");
exit(-1);
}
+ if(initMidiJack())
+ {
+ QMessageBox::critical(NULL, "MusE fatal error.", "MusE failed to initialize the\n"
+ "Jack midi subsystem, check\n"
+ "your configuration.");
+ exit(-1);
+ }
}
//---------------------------------------------------------
diff --git a/muse/muse/midiseq.cpp b/muse/muse/midiseq.cpp
index cecef2ab..e10427e5 100644
--- a/muse/muse/midiseq.cpp
+++ b/muse/muse/midiseq.cpp
@@ -22,6 +22,7 @@
#include "midictrl.h"
#include "audio.h"
#include "driver/alsamidi.h"
+//#include "driver/jackmidi.h"
#include "sync.h"
#include "synth.h"
#include "song.h"
@@ -377,16 +378,24 @@ void MidiSeq::updatePollFd()
for (iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) {
MidiDevice* dev = *imd;
int port = dev->midiPort();
+ const QString name = dev->name();
if (port == -1)
continue;
if ((dev->rwFlags() & 0x2) || (extSyncFlag.value()
//&& (rxSyncPort == port || rxSyncPort == -1))) {
//&& (dev->syncInfo().MCIn()))) {
&& (midiPorts[port].syncInfo().MCIn()))) {
+ if(dev->selectRfd() < 0){
+ fprintf(stderr, "WARNING: read-file-descriptor for {%s} is negative\n", name.latin1());
+ }
addPollFd(dev->selectRfd(), POLLIN, ::midiRead, this, dev);
}
- if (dev->bytesToWrite())
+ if (dev->bytesToWrite()){
+ if(dev->selectWfd() < 0){
+ fprintf(stderr, "WARNING: write-file-descriptor for {%s} is negative\n", name.latin1());
+ }
addPollFd(dev->selectWfd(), POLLOUT, ::midiWrite, this, dev);
+ }
}
// special handling for alsa midi:
// (one fd for all devices)