Hello,

I've been working on adding JACK MIDI support to Hydrogen. To generate discussion/feedback/openness... I'm attaching the current state of my work. This code is *not* intended to be committed to SVN. This code needs a good bit of cleanup... and is known to SEGFAULT when Hydrogen exits.

The patches attached are against SVN rev 251. Below is a single patch for the whole thing. Attached is a tarball that has the patches divided up over 7 commits.

Main concepts:
  * Creating a static global JACK Client object to help JackMidiDriver
    and JackOutput cooperate.
  * Adding hooks to MidiInput so that MIDI processing can be done
    inside the audioEngine_process() callback.
  * Adding timing data to MidiMessage to make sample-perfect matching
    of midi messages and audio possible.

Known warts / to-do list:
  * Often SEGFAULTs when exiting.  The pointer to the audio driver somehow
    becomes NULL while the sampler engine is still processing it.
  * No LASH support.
  * JackClient implemented as static global rather (my preference) rather
    than a singleton (like the rest of the code).  (...sorry for that.)
  * Jack types (esp. jack_nframes_t), JACK_SUPPORT, JACKMIDI_SUPPORT is a
    little messy.
  * No JACK API checking (e.g. this code works with jack 0.103, but not 0.109.
  * I'm throwing exceptions, but handling none... and all but ignoring the
    Hydrogen logging system.
  * My code is formatted with spaces rather than tabs.
  * Restarting drivers never actually restarts the JACK client... just
    the ports.

Peace,
Gabriel


diff --git a/extra/hydrogenPlayer/hydrogenPlayer.pro b/extra/hydrogenPlayer/hydrogenPlayer.pro
index 36a1c65..33fb250 100644
--- a/extra/hydrogenPlayer/hydrogenPlayer.pro
+++ b/extra/hydrogenPlayer/hydrogenPlayer.pro
@@ -58,6 +58,10 @@ contains(H2DEFINES, JACK_SUPPORT ) {
        LIBS += -ljack
 }

+contains(H2DEFINES, JACKMIDI_SUPPORT ) {
+       LIBS += -ljack
+}
+
 contains(H2DEFINES, LASH_SUPPORT ) {
        LIBS += -llash
 }
diff --git a/features.pri b/features.pri
index b395054..99db09b 100644
--- a/features.pri
+++ b/features.pri
@@ -14,6 +14,7 @@ macx-g++ {
 linux-g++ {
        H2DEFINES += ALSA_SUPPORT
        H2DEFINES += JACK_SUPPORT
+       H2DEFINES += JACKMIDI_SUPPORT
        H2DEFINES += LASH_SUPPORT
        H2DEFINES += FLAC_SUPPORT
        H2DEFINES += LADSPA_SUPPORT
@@ -24,6 +25,7 @@ linux-g++ {
 linux-g++-64 {
        H2DEFINES += ALSA_SUPPORT
        H2DEFINES += JACK_SUPPORT
+       H2DEFINES += JACKMIDI_SUPPORT
        H2DEFINES += LASH_SUPPORT
        H2DEFINES += FLAC_SUPPORT
        H2DEFINES += LADSPA_SUPPORT
diff --git a/gui/src/PreferencesDialog.cpp b/gui/src/PreferencesDialog.cpp
index a6aa261..0c2364c 100644
--- a/gui/src/PreferencesDialog.cpp
+++ b/gui/src/PreferencesDialog.cpp
@@ -95,6 +95,7 @@ PreferencesDialog::PreferencesDialog(QWidget* parent)
        m_pMidiDriverComboBox->addItem( "ALSA" );
        m_pMidiDriverComboBox->addItem( "PortMidi" );
        m_pMidiDriverComboBox->addItem( "CoreMidi" );
+       m_pMidiDriverComboBox->addItem( "JackMidi" );

        if ( pPref->m_sMidiDriver == "ALSA" ) {
                m_pMidiDriverComboBox->setCurrentIndex(0);
@@ -105,6 +106,9 @@ PreferencesDialog::PreferencesDialog(QWidget* parent)
        else if ( pPref->m_sMidiDriver == "CoreMidi" ) {
                m_pMidiDriverComboBox->setCurrentIndex(2);
        }
+       else if ( pPref->m_sMidiDriver == "JackMidi" ) {
+               m_pMidiDriverComboBox->setCurrentIndex(3);
+       }
        else {
ERRORLOG( "Unknown midi input from preferences [" + pPref->m_sMidiDriver + "]" );
        }
@@ -325,6 +329,9 @@ void PreferencesDialog::on_okBtn_clicked()
        else if ( m_pMidiDriverComboBox->currentText() == "CoreMidi" ) {
                pPref->m_sMidiDriver = "CoreMidi";
        }
+       else if ( m_pMidiDriverComboBox->currentText() == "JackMidi" ) {
+               pPref->m_sMidiDriver = "JackMidi";
+       }

        pPref->m_bMidiNoteOffIgnore = m_pIgnoreNoteOffCheckBox->isChecked();

diff --git a/libs/hydrogen/hydrogen.pro b/libs/hydrogen/hydrogen.pro
index f2488bb..4cd45fa 100644
--- a/libs/hydrogen/hydrogen.pro
+++ b/libs/hydrogen/hydrogen.pro
@@ -72,6 +72,7 @@ HEADERS += \
                include/hydrogen/IO/TransportInfo.h \
                include/hydrogen/IO/MidiInput.h \
                include/hydrogen/IO/CoreMidiDriver.h \
+               include/hydrogen/IO/JackClient.h \
                include/hydrogen/IO/JackOutput.h \
                include/hydrogen/IO/NullDriver.h \
                \
@@ -93,6 +94,7 @@ HEADERS += \
                src/IO/PortMidiDriver.h \
                src/IO/PortAudioDriver.h \
                src/IO/CoreAudioDriver.h \
+                src/IO/JackMidiDriver.h \
                src/flac_file.h \


@@ -107,6 +109,7 @@ SOURCES += \
                src/IO/alsa_midi_driver.cpp \
                src/IO/disk_writer_driver.cpp \
                src/IO/fake_driver.cpp \
+               src/IO/jack_client.cpp \
                src/IO/jack_output.cpp \
                src/IO/null_driver.cpp \
                src/IO/oss_driver.cpp \
@@ -117,6 +120,7 @@ SOURCES += \
                src/IO/portaudio_driver.cpp \
                src/IO/coreaudio_driver.cpp \
                src/IO/coremidi_driver.cpp \
+                src/IO/jack_midi_driver.cpp \
                \
                src/fx/effects.cpp \
                src/fx/ladspa_fx.cpp \
diff --git a/libs/hydrogen/include/hydrogen/IO/JackClient.h b/libs/hydrogen/include/hydrogen/IO/JackClient.h
new file mode 100644
index 0000000..ab189ee
--- /dev/null
+++ b/libs/hydrogen/include/hydrogen/IO/JackClient.h
@@ -0,0 +1,72 @@
+/*
+ * Hydrogen
+ * Copyright(c) 2002-2008 by Alex >Comix< Cominu [EMAIL PROTECTED]
+ *
+ * http://www.hydrogen-music.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+/* JackClient.h
+ * Copyright(c) 2008 by Gabriel M. Beddingfield <[EMAIL PROTECTED]>
+ *
+ * This class manages the Jack client handle (returned by jack_client_open()).
+ */
+
+#ifndef JACK_CLIENT_H
+#define JACK_CLIENT_H
+
+#ifdef JACK_SUPPORT
+
+#include <jack/types.h>
+#include <hydrogen/h2_exception.h>
+#include <QtCore/QString>
+
+namespace H2Core
+{
+    class JackClient
+    {
+    public:
+       JackClient(void);
+       ~JackClient(void);
+
+       jack_client_t* ref(void) { return m_client; }
+       int setAudioProcessCallback(JackProcessCallback process);
+       int setNonAudioProcessCallback(JackProcessCallback process);
+       int clearAudioProcessCallback(void);
+       int clearNonAudioProcessCallback(void);
+       std::vector<QString> getMidiOutputPortList(void);
+
+    private:
+       jack_client_t* m_client;
+       JackProcessCallback m_audio_process;
+       JackProcessCallback m_nonaudio_process;
+    };
+
+    class JackClientException : public H2Exception
+    {
+    public:
+       JackClientException(const QString& msg ) : H2Exception(msg) {}
+    };
+
+
+    JackClient& getJackClientInstance(void);
+
+
+}
+
+#endif // JACK_SUPPORT
+
+#endif // JACK_CLIENT_H
diff --git a/libs/hydrogen/include/hydrogen/IO/JackOutput.h b/libs/hydrogen/include/hydrogen/IO/JackOutput.h
index 4e7a1ae..4a1d211 100644
--- a/libs/hydrogen/include/hydrogen/IO/JackOutput.h
+++ b/libs/hydrogen/include/hydrogen/IO/JackOutput.h
@@ -48,8 +48,6 @@ class Instrument;
 class JackOutput : public AudioOutput
 {
 public:
-       jack_client_t *client;
-
        JackOutput( JackProcessCallback processCallback );
        ~JackOutput();

diff --git a/libs/hydrogen/include/hydrogen/IO/MidiInput.h b/libs/hydrogen/include/hydrogen/IO/MidiInput.h
index 60e6f71..9d8343c 100644
--- a/libs/hydrogen/include/hydrogen/IO/MidiInput.h
+++ b/libs/hydrogen/include/hydrogen/IO/MidiInput.h
@@ -26,6 +26,8 @@
 #include <hydrogen/Object.h>
 #include <string>
 #include <vector>
+#warning "TODO: Jack corruption on the main line!"
+#include <jack/types.h>

 namespace H2Core
 {
@@ -56,12 +58,17 @@ public:
        int m_nData2;
        int m_nChannel;
        std::vector<unsigned char> m_sysexData;
+       bool m_use_frame;
+       jack_nframes_t m_frame;

        MidiMessage()
                        : m_type( UNKNOWN )
                        , m_nData1( -1 )
                        , m_nData2( -1 )
-                       , m_nChannel( -1 ) {}
+                       , m_nChannel( -1 )
+                       , m_use_frame(false)
+                       , m_frame(0)
+               {}
 };


@@ -82,7 +89,7 @@ class MidiInput : public Object
 {
 public:
        MidiInput( const QString class_name );
-       ~MidiInput();
+       virtual ~MidiInput();

        virtual void open() = 0;
        virtual void close() = 0;
@@ -94,6 +101,12 @@ public:
        void handleMidiMessage( const MidiMessage& msg );
        void handleSysexMessage( const MidiMessage& msg );

+       // Process callback hooks.  Specifically added for JACK MIDI,
+       // but could be used by others.  The default implementation
+       // does nothing.
+ virtual int processAudio(jack_nframes_t nframes); // Assumes processing in same thread as audio + virtual int processNonAudio(jack_nframes_t nframes); // Assumes processing in other thread.
+
 protected:
        bool m_bActive;

diff --git a/libs/hydrogen/include/hydrogen/hydrogen.h b/libs/hydrogen/include/hydrogen/hydrogen.h
index aa340ce..cb5c0bf 100644
--- a/libs/hydrogen/include/hydrogen/hydrogen.h
+++ b/libs/hydrogen/include/hydrogen/hydrogen.h
@@ -74,7 +74,8 @@ public:
        Song* getSong();
        void removeSong();

- void addRealtimeNote ( int instrument, float velocity, float pan_L=1.0, float pan_R=1.0, float pitch=0.0, bool forcePlay=false ); + void addRealtimeNote ( int instrument, float velocity, float pan_L=1.0, float pan_R=1.0, float pitch=0.0, bool forcePlay=false, bool use_frame = false, uint32_t frame = 0 );
+

        float getMasterPeak_L();
        void setMasterPeak_L( float value );
@@ -87,7 +88,7 @@ public:


        unsigned long getTickPosition();
-       unsigned long getRealtimeTickPosition();
+       unsigned long getRealtimeTickPosition(unsigned long offset = 0);
        unsigned long getTotalFrames();
        unsigned long getRealtimeFrames();

diff --git a/libs/hydrogen/src/IO/JackMidiDriver.h b/libs/hydrogen/src/IO/JackMidiDriver.h
new file mode 100644
index 0000000..e15490e
--- /dev/null
+++ b/libs/hydrogen/src/IO/JackMidiDriver.h
@@ -0,0 +1,79 @@
+/*
+ * Hydrogen
+ * Copyright(c) 2002-2008 by Alex >Comix< Cominu [EMAIL PROTECTED]
+ *
+ * http://www.hydrogen-music.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+/* JackMidiDriver.h
+ * Copyright(c) 2008 by Gabriel M. Beddingfield <[EMAIL PROTECTED]>
+ *
+ * Note: This class implements it's own Jack Client object and process
+ * callback.  Once working, it should be merged with the JackOutput
+ * class into a superclass called something like JackDriver.
+ */
+
+#ifndef JACK_MIDI_DRIVER_H
+#define JACK_MIDI_DRIVER_H
+
+#ifdef JACKMIDI_SUPPORT
+
+#include <hydrogen/IO/MidiInput.h>
+#include <hydrogen/h2_exception.h>
+#include <jack/jack.h>
+#include <jack/midiport.h>
+#include <vector>
+#include <QtCore/QString>
+#include <memory>
+
+namespace H2Core
+{
+
+    class JackMidiDriver : public MidiInput
+    {
+    public:
+       JackMidiDriver(void);
+       ~JackMidiDriver();
+
+       // Reimplemented from MidiInput
+       void open(void);
+       void close(void);
+       virtual std::vector<QString> getOutputPortList(void);
+
+       int processAudio(jack_nframes_t nframes);
+       int processNonAudio(jack_nframes_t nframes);
+
+    private:
+       jack_port_t* m_port;
+
+       int process(jack_nframes_t nframes, bool use_frame);
+
+    }; // JackMidiDriver
+
+    class JackMidiException : public H2Exception
+    {
+    public:
+       JackMidiException(const QString& msg ) : H2Exception(msg) {}
+       JackMidiException(const QString& msg, JackStatus /*stat*/)
+           : H2Exception(msg) {}
+    };
+
+} // namespace H2Core
+
+#endif // JACKMIDI_SUPPORT
+
+#endif // JACK_MIDI_DRIVER_H
diff --git a/libs/hydrogen/src/IO/jack_client.cpp b/libs/hydrogen/src/IO/jack_client.cpp
new file mode 100644
index 0000000..c35a44e
--- /dev/null
+++ b/libs/hydrogen/src/IO/jack_client.cpp
@@ -0,0 +1,140 @@
+/*
+ * Hydrogen
+ * Copyright(c) 2002-2008 by Alex >Comix< Cominu [EMAIL PROTECTED]
+ *
+ * http://www.hydrogen-music.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+/* JackClient.cpp
+ * Copyright(c) 2008 by Gabriel M. Beddingfield <[EMAIL PROTECTED]>
+ */
+
+#include <hydrogen/IO/JackClient.h>
+#include <jack/jack.h>
+#include <hydrogen/Object.h>
+
+#ifdef JACK_SUPPORT
+
+using namespace std;
+namespace H2Core
+{
+
+JackClient& getJackClientInstance(void)
+{
+    static JackClient client;
+    return client;
+}
+
+JackClient::JackClient()
+    : m_client(0),
+      m_audio_process(0),
+      m_nonaudio_process(0)
+{
+    m_client = jack_client_open(
+       "Hydrogen",
+       JackNullOption,
+       0);
+    if (!m_client) throw JackClientException("Could not create JACK client");
+
+}
+
+JackClient::~JackClient()
+{
+    _INFOLOG( "DESTROY" );
+    if(m_client) {
+       jack_deactivate(m_client); // return value ignored
+       jack_client_close(m_client);  // Ignore return value
+       m_client = 0;
+    }
+}
+
+int JackClient::setAudioProcessCallback(JackProcessCallback process)
+{
+    if (jack_deactivate(m_client))
+       throw JackClientException("Could not deactivate JACK MIDI client.");
+    int rv = jack_set_process_callback(m_client, process, 0);
+    if (!rv) m_audio_process = process;
+    if (jack_activate(m_client))
+       throw JackClientException("Could not activate JACK MIDI client.");
+    return rv;
+}
+
+int JackClient::setNonAudioProcessCallback(JackProcessCallback process)
+{
+    if (jack_deactivate(m_client))
+       throw JackClientException("Could not deactivate JACK MIDI client.");
+    int rv = 0;
+    if (!m_audio_process) {
+       rv = jack_set_process_callback(m_client, process, 0);
+    }
+    if (!rv) m_nonaudio_process = process;
+    if (jack_activate(m_client))
+       throw JackClientException("Could not activate JACK MIDI client.");
+    return rv;
+}
+
+int JackClient::clearAudioProcessCallback(void)
+{
+    int rv = 0;
+    if (jack_deactivate(m_client))
+       throw JackClientException("Could not deactivate JACK MIDI client.");
+    // make sure the process cycle is over before killing anything
+    if (m_nonaudio_process) {
+       rv = jack_set_process_callback(m_client, m_nonaudio_process, 0);
+    }
+    if (m_nonaudio_process && rv) {
+       rv = jack_set_process_callback(m_client, 0, 0);
+       if (jack_activate(m_client))
+           throw JackClientException("Could not activate JACK MIDI client.");
+    }
+    m_audio_process = 0;
+    return rv;
+}
+
+int JackClient::clearNonAudioProcessCallback(void)
+{
+    int rv = 0;
+    if (!m_audio_process) {
+       if (jack_deactivate(m_client))
+           throw JackClientException("Could not deactivate JACK MIDI client.");
+       rv = jack_set_process_callback(m_client, 0, 0);
+    }
+    m_nonaudio_process = 0;
+    return rv;
+}
+
+std::vector<QString> JackClient::getMidiOutputPortList(void)
+{
+    vector<QString> ports;
+    const char **port_names = 0;
+    port_names = jack_get_ports(m_client,
+                               0,
+                               JACK_DEFAULT_MIDI_TYPE,
+                               JackPortIsOutput);
+
+    while (*port_names) {
+       ports.push_back(QString(*port_names));
+       free(*port_names);
+       ++port_names;
+    }
+    return ports;
+}
+
+
+} // namespace H2Core
+
+#endif // JACKMIDI_SUPPORT
diff --git a/libs/hydrogen/src/IO/jack_midi_driver.cpp b/libs/hydrogen/src/IO/jack_midi_driver.cpp
new file mode 100644
index 0000000..ee3064f
--- /dev/null
+++ b/libs/hydrogen/src/IO/jack_midi_driver.cpp
@@ -0,0 +1,277 @@
+/*
+ * Hydrogen
+ * Copyright(c) 2002-2008 by Alex >Comix< Cominu [EMAIL PROTECTED]
+ *
+ * http://www.hydrogen-music.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+/* JackMidiDriver.cpp
+ * Copyright(c) 2008 by Gabriel M. Beddingfield <[EMAIL PROTECTED]>
+ */
+
+#include "JackMidiDriver.h"
+#include <hydrogen/IO/JackClient.h>
+#include <cassert>
+#include <cstdlib> // free()
+#include <hydrogen/Preferences.h> // For preferred auto-connection
+#include <cerrno> // EEXIST for jack_connect()
+#ifdef JACKMIDI_SUPPORT
+
+using namespace std;
+using namespace H2Core;
+
+JackProcessCallback jackMidiFallbackProcess; // implemented in hydrogen.cpp
+
+JackMidiDriver::JackMidiDriver()
+    : MidiInput( "JackMidiDriver" ),
+      m_port(0)
+{
+}
+
+JackMidiDriver::~JackMidiDriver()
+{
+    INFOLOG( "DESTROY" );
+    close();
+}
+
+void JackMidiDriver::open(void)
+{
+    JackClient& client = getJackClientInstance();
+    if (client.setNonAudioProcessCallback(jackMidiFallbackProcess)) {
+       throw JackMidiException("Could not set JACK process callback");
+    }
+    m_port = jack_port_register(client.ref(),
+                               "midi_in",
+                               JACK_DEFAULT_MIDI_TYPE,
+                               JackPortIsInput,
+                               0);
+    if (!m_port) throw JackMidiException("Could not create JACK MIDI input 
port");
+
+    // Autoconnect port to an Output (readable) port
+    QString OutPort = Preferences::getInstance()->m_sMidiPortName;
+    int err = jack_connect(client.ref(),
+                          OutPort.toLatin1().constData(),
+                          jack_port_name(m_port));
+    if(err && (err != EEXIST)) {
+       _INFOLOG("Jack could not connect to port " + OutPort);
+    }
+
+}
+
+void JackMidiDriver::close(void)
+{
+    if(m_port) {
+       jack_client_t* client = getJackClientInstance().ref();
+       assert(client);
+       jack_port_unregister(client, m_port);  // Ignore return value
+       m_port = 0;
+    }
+}
+
+
+void translate_jack_midi_to_h2(H2Core::MidiMessage& msg,
+                              const jack_midi_event_t& event,
+                              bool use_frame)
+{
+    typedef enum {
+       UNKNOWN = 0x00,
+       NOTE_OFF = 0x80,
+       NOTE_ON = 0x90,
+       POLYPHONIC_KEY_PRESSURE = 0xA0,
+       CONTROL_CHANGE = 0xB0,
+       PROGRAM_CHANGE = 0xC0,
+       CHANNEL_PRESSURE = 0xD0,
+       PITCH_WHEEL = 0xE0,
+       SYSTEM_EXCLUSIVE = 0xF0
+    } midi_commands;
+    typedef enum {
+       SYSEX_START = 0x00,
+       MTC_QUARTER_FRAME = 0x01,
+       SONG_POS = 0x02,
+       SONG_SELECT = 0x03,
+       UNDEFINED_04 = 0x04,
+       UNDEFINED_05 = 0x05,
+       TUNE_REQ = 0x06,
+       SYSEX_END = 0x07,
+       CLOCK = 0x08,
+       UNDEFINED_09 = 0x09,
+       SONG_START = 0x0A,
+       SONG_CONT = 0x0B,
+       SONG_STOP = 0x0C,
+       UNDEFINED_0D = 0x0D,
+       ACTIVE_SENSING = 0x0E,
+       SYSTEM_RESET = 0x0F
+    } sysex_messages;
+       
+
+    midi_commands status;
+    unsigned char tmp;
+    msg.m_type = MidiMessage::UNKNOWN;
+    msg.m_nData1 = -1;
+    msg.m_nData2 = -1;
+    msg.m_nChannel = -1;
+    msg.m_sysexData.clear();
+
+    if (event.size == 0)
+       return;
+
+    if (use_frame) {
+       msg.m_use_frame = true;
+       msg.m_frame = event.time;
+    } else {
+       msg.m_use_frame = false;
+       msg.m_frame = 0;
+    }
+    tmp = event.buffer[0] & 0xF0;
+    if (0x80 & tmp) {
+       status = (unsigned char)tmp;
+    } else {
+       status = UNKNOWN;
+    }
+    switch(status) {
+    case UNKNOWN:
+       msg = MidiMessage();
+       break;
+    case NOTE_ON:
+       msg.m_type = MidiMessage::NOTE_ON;
+       msg.m_nData1 = event.buffer[1];
+       msg.m_nData2 = event.buffer[2];
+       msg.m_nChannel = 0x0F & event.buffer[0];
+       break;
+    case NOTE_OFF:
+       msg.m_type = MidiMessage::NOTE_OFF;
+       msg.m_nData1 = event.buffer[1];
+       msg.m_nData2 = event.buffer[2];
+       msg.m_nChannel = 0x0F & event.buffer[0];
+       break;
+    case POLYPHONIC_KEY_PRESSURE:
+       msg.m_type = MidiMessage::POLYPHONIC_KEY_PRESSURE;
+       msg.m_nData1 = event.buffer[1];
+       msg.m_nData2 = event.buffer[2];
+       msg.m_nChannel = 0x0F & event.buffer[0];
+       break;
+    case CONTROL_CHANGE:
+       msg.m_type = MidiMessage::CONTROL_CHANGE;
+       msg.m_nData1 = event.buffer[1];
+       msg.m_nData2 = event.buffer[2];
+       msg.m_nChannel = 0x0F & event.buffer[0];
+       break;
+    case PROGRAM_CHANGE:
+       msg.m_type = MidiMessage::PROGRAM_CHANGE;
+       msg.m_nData1 = event.buffer[1];
+       msg.m_nData2 = event.buffer[2];
+       msg.m_nChannel = 0x0F & event.buffer[0];
+       break;
+    case CHANNEL_PRESSURE:
+       msg.m_type = MidiMessage::CHANNEL_PRESSURE;
+       msg.m_nData1 = event.buffer[1];
+       msg.m_nData2 = -1;
+       msg.m_nChannel = 0x0F & event.buffer[0];
+       break;
+    case PITCH_WHEEL:
+       msg.m_type = MidiMessage::PITCH_WHEEL;
+       msg.m_nData1 = event.buffer[1];
+       msg.m_nData2 = event.buffer[2];
+       msg.m_nChannel = 0x0F & event.buffer[0];
+       break;
+    case SYSTEM_EXCLUSIVE:
+       switch (event.buffer[0] & 0x0F) {
+       case SYSEX_START:
+           msg.m_type = MidiMessage::SYSEX;
+           msg.m_sysexData.assign(event.buffer+1, event.buffer+event.size);
+           break;
+       case MTC_QUARTER_FRAME:
+           msg.m_type = MidiMessage::QUARTER_FRAME;
+           msg.m_nData1 = event.buffer[1];
+           break;
+       case SONG_POS:
+           msg.m_type = MidiMessage::SONG_POS;
+           msg.m_nData1 = event.buffer[1];
+           msg.m_nData2 = event.buffer[2];
+           break;
+       case SONG_START:
+           msg.m_type = MidiMessage::START;
+           break;
+       case SONG_CONT:
+           msg.m_type = MidiMessage::CONTINUE;
+           break;
+       case SONG_STOP:
+           msg.m_type = MidiMessage::STOP;
+           break;
+       // Following not handled by H2Core::MidiMessage
+       case SYSEX_END:
+       case SONG_SELECT:
+       case TUNE_REQ:
+       case CLOCK:
+       case ACTIVE_SENSING:
+       case SYSTEM_RESET:
+       case UNDEFINED_04:
+       case UNDEFINED_05:
+       case UNDEFINED_09:
+       case UNDEFINED_0D:
+           msg.m_type = MidiMessage::UNKNOWN;
+           break;
+       default:
+           assert(false);  // Should not reach this line
+       }
+       break;
+    default:
+       assert(false); // Should not reach this line
+    }
+}
+
+int JackMidiDriver::processAudio(jack_nframes_t nframes)
+{
+    return process(nframes, true);
+}
+
+int JackMidiDriver::processNonAudio(jack_nframes_t nframes)
+{
+    return process(nframes, false);
+}
+
+int JackMidiDriver::process(jack_nframes_t nframes, bool use_frame)
+{
+    if (!m_port) return 0;
+
+    jack_nframes_t event_ct, event_pos;
+    jack_midi_event_t jack_event;
+    H2Core::MidiMessage msg;
+
+    void* port_buf = jack_port_get_buffer(m_port, nframes);
+    event_ct = jack_midi_get_event_count(port_buf, nframes);
+    for ( event_pos=0 ; event_pos<event_ct ; ++event_pos ) {
+       if (jack_midi_event_get(&jack_event,
+                               port_buf,
+                               event_pos,
+                               nframes)) {
+           break;
+       }
+       translate_jack_midi_to_h2(msg, jack_event, use_frame);
+       if (msg.m_type != MidiMessage::UNKNOWN) {
+           handleMidiMessage(msg);
+       }
+    }
+    return 0;
+}
+
+std::vector<QString> JackMidiDriver::getOutputPortList(void)
+{
+    return getJackClientInstance().getMidiOutputPortList();
+}
+
+#endif // JACKMIDI_SUPPORT
diff --git a/libs/hydrogen/src/IO/jack_output.cpp b/libs/hydrogen/src/IO/jack_output.cpp
index 90dcbb1..c7442d8 100644
--- a/libs/hydrogen/src/IO/jack_output.cpp
+++ b/libs/hydrogen/src/IO/jack_output.cpp
@@ -20,6 +20,7 @@
  *
  */

+#include <hydrogen/IO/JackClient.h>
 #include <hydrogen/IO/JackOutput.h>
 #ifdef JACK_SUPPORT

@@ -60,7 +61,7 @@ void jackDriverShutdown( void *arg )
 {
        UNUSED( arg );
 //     jackDriverInstance->deactivate();
-       jackDriverInstance->client = NULL;
+       getJackClientInstance().clearAudioProcessCallback();
        Hydrogen::get_instance()->raiseError( Hydrogen::JACK_SERVER_SHUTDOWN );
 }

@@ -99,8 +100,9 @@ JackOutput::~JackOutput()
 int JackOutput::connect()
 {
        INFOLOG( "connect" );
+       jack_client_t* client = getJackClientInstance().ref();

-       if ( jack_activate ( client ) ) {
+       if ( !client ) {
                Hydrogen::get_instance()->raiseError( 
Hydrogen::JACK_CANNOT_ACTIVATE_CLIENT );
                return 1;
        }
@@ -155,19 +157,18 @@ int JackOutput::connect()
 void JackOutput::disconnect()
 {
        INFOLOG( "disconnect" );
+       jack_client_t* client = getJackClientInstance().ref();

        deactivate();
-       jack_client_t *oldClient = client;
-       client = NULL;
-       if ( oldClient ) {
-               INFOLOG( "calling jack_client_close" );
-               int res = jack_client_close( oldClient );
-               if ( res ) {
-                       ERRORLOG( "Error in jack_client_close" );
-                       // FIXME: raise exception
-               }
+
+       if (output_port_1)
+           jack_port_unregister(client, output_port_1);
+       if (output_port_2)
+           jack_port_unregister(client, output_port_2);
+       for (int j=0; j<track_port_count; ++j) {
+           jack_port_unregister(client, track_output_ports_L[j]);
+           jack_port_unregister(client, track_output_ports_R[j]);
        }
-       client = NULL;
 }


@@ -176,13 +177,7 @@ void JackOutput::disconnect()
 void JackOutput::deactivate()
 {
        INFOLOG( "[deactivate]" );
-       if ( client ) {
-               INFOLOG( "calling jack_deactivate" );
-               int res = jack_deactivate( client );
-               if ( res ) {
-                       ERRORLOG( "Error in jack_deactivate" );
-               }
-       }
+       getJackClientInstance().clearAudioProcessCallback();
 }


@@ -270,7 +265,7 @@ void JackOutput::updateTransportInfo()
 {

if ( Preferences::getInstance()->m_bJackTransportMode == Preferences::USE_JACK_TRANSPORT ) {
-               m_JackTransportState = jack_transport_query( client, 
&m_JackTransportPos );
+ m_JackTransportState = jack_transport_query( getJackClientInstance().ref(), &m_JackTransportPos );


                // update m_transport with jack-transport data
@@ -403,18 +398,8 @@ int JackOutput::init( unsigned nBufferSize )
        output_port_name_1 = Preferences::getInstance()->m_sJackPortName1;
        output_port_name_2 = Preferences::getInstance()->m_sJackPortName2;

-
+       jack_client_t* client = getJackClientInstance().ref();
 // test!!!
-       if ( ( client = jack_client_open( "hydrogen", JackNullOption, NULL ) ) 
== NULL ) {
-               WARNINGLOG( "Error: can't start JACK server" );
-               return 3;
-       }
-
-       // check if another hydrogen instance is connected to jack
-       if ( ( client = jack_client_new( "hydrogen-tmp" ) ) == 0 ) {
-               WARNINGLOG( "Jack server not running?" );
-               return 3;
-       }

        std::vector<QString> clientList;
        const char **readports = jack_get_ports( client, NULL, NULL, 
JackPortIsOutput );
@@ -435,9 +420,12 @@ int JackOutput::init( unsigned nBufferSize )
                }
                nPort++;
        }
-       jack_client_close( client );

-       QString sClientName = "";
+//     jack_client_close( client );
+
+       #warning "TODO: Fix this section of code for LASH support"
+       QString sClientName = "";
+#if 0
        for ( int nInstance = 1; nInstance < 16; nInstance++ ) {
 //             sprintf( clientName, "Hydrogen-%d", nInstance );
                //      sprintf( clientName, "Hydrogen-%d", getpid() );
@@ -460,7 +448,7 @@ int JackOutput::init( unsigned nBufferSize )
ERRORLOG( "Jack server not running? (jack_client_new). Using " + sClientName + " as client name" );
                return 3;
        }
-
+#endif
        jack_server_sampleRate = jack_get_sample_rate ( client );
        jack_server_bufferSize = jack_get_buffer_size ( client );

@@ -468,7 +456,7 @@ int JackOutput::init( unsigned nBufferSize )
        /* tell the JACK server to call `process()' whenever
           there is work to be done.
        */
-       jack_set_process_callback ( client, this->processCallback, 0 );
+       getJackClientInstance().setAudioProcessCallback(this->processCallback);


        /* tell the JACK server to call `srate()' whenever
@@ -535,6 +523,7 @@ void JackOutput::makeTrackOutputs( Song * song )
                setTrackOutput( n, instr );
        }
        // clean up unused ports
+       jack_client_t* client = getJackClientInstance().ref();
        for ( int n = nInstruments; n < track_port_count; n++ ) {
                jack_port_unregister( client, track_output_ports_L[n] );
                jack_port_unregister( client, track_output_ports_R[n] );
@@ -553,6 +542,7 @@ void JackOutput::setTrackOutput( int n, Instrument * instr )
 {

        QString chName;
+       jack_client_t* client = getJackClientInstance().ref();

        if ( track_port_count <= n ) { // need to create more ports
                for ( int m = track_port_count; m <= n; m++ ) {
@@ -574,6 +564,7 @@ void JackOutput::setTrackOutput( int n, Instrument * instr )

 void JackOutput::play()
 {
+       jack_client_t* client = getJackClientInstance().ref();
if ( ( Preferences::getInstance() )->m_bJackTransportMode == Preferences::USE_JACK_TRANSPORT || Preferences::getInstance()->m_bJackMasterMode == Preferences::USE_JACK_TIME_MASTER ) {
                if ( client ) {
                        INFOLOG( "jack_transport_start()" );
@@ -588,6 +579,7 @@ void JackOutput::play()

 void JackOutput::stop()
 {
+       jack_client_t* client = getJackClientInstance().ref();
if ( ( Preferences::getInstance() )->m_bJackTransportMode == Preferences::USE_JACK_TRANSPORT || Preferences::getInstance()->m_bJackMasterMode == Preferences::USE_JACK_TIME_MASTER ) {
                if ( client ) {
                        INFOLOG( "jack_transport_stop()" );
@@ -602,6 +594,7 @@ void JackOutput::stop()

 void JackOutput::locate( unsigned long nFrame )
 {
+       jack_client_t* client = getJackClientInstance().ref();
if ( ( Preferences::getInstance() )->m_bJackTransportMode == Preferences::USE_JACK_TRANSPORT || Preferences::getInstance()->m_bJackMasterMode == Preferences::USE_JACK_TIME_MASTER ) {
                if ( client ) {
                        WARNINGLOG( QString( "Calling 
jack_transport_locate(%1)" ).arg( nFrame ) );
@@ -647,6 +640,7 @@ int JackOutput::getNumTracks()

 void JackOutput::initTimeMaster(void)
 {
+       jack_client_t* client = getJackClientInstance().ref();
        if ( client == NULL) return;

        bool cond = false;
@@ -671,6 +665,7 @@ void JackOutput::initTimeMaster(void)

 void JackOutput::com_release()
 {
+       jack_client_t* client = getJackClientInstance().ref();
        if ( client == NULL) return;

        jack_release_timebase(client);
diff --git a/libs/hydrogen/src/IO/midi_input.cpp b/libs/hydrogen/src/IO/midi_input.cpp
index acbf929..5a6a833 100644
--- a/libs/hydrogen/src/IO/midi_input.cpp
+++ b/libs/hydrogen/src/IO/midi_input.cpp
@@ -176,7 +176,7 @@ void MidiInput::handleNoteOnMessage( const MidiMessage& msg 
)
                                nInstrument = MAX_INSTRUMENTS - 1;
                        }

-                       pEngine->addRealtimeNote( nInstrument, fVelocity, 
fPan_L, fPan_R, 0.0, true );
+ pEngine->addRealtimeNote( nInstrument, fVelocity, fPan_L, fPan_R, 0.0, true, msg.m_use_frame, msg.m_frame );
                }
        }
 }
@@ -202,6 +202,7 @@ void MidiInput::handleNoteOffMessage( const MidiMessage& 
msg )
                nInstrument = MAX_INSTRUMENTS - 1;
        }
        Instrument *pInstr = pSong->get_instrument_list()->get( nInstrument );
+ #warning "Need some frame positional data for ms.m_use_frame... but Sampler::note_off isn't implemented, yet... so neither are we."
        const unsigned nPosition = 0;
        const float fVelocity = 0.0f;
        const float fPan_L = 0.5f;
@@ -322,6 +323,17 @@ void MidiInput::handleSysexMessage( const MidiMessage& msg 
)
        }
 }

+int MidiInput::processAudio(jack_nframes_t /*nframes*/)
+{
+    return 0;
+}
+
+int MidiInput::processNonAudio(jack_nframes_t /*nframes*/)
+{
+    return 0;
+}
+
+
 };


diff --git a/libs/hydrogen/src/hydrogen.cpp b/libs/hydrogen/src/hydrogen.cpp
index c0ecf46..57cdc29 100644
--- a/libs/hydrogen/src/hydrogen.cpp
+++ b/libs/hydrogen/src/hydrogen.cpp
@@ -68,7 +68,7 @@
 #include "IO/AlsaMidiDriver.h"
 #include "IO/PortMidiDriver.h"
 #include "IO/CoreAudioDriver.h"
-
+#include "IO/JackMidiDriver.h"

 namespace H2Core
 {
@@ -696,6 +696,10 @@ int audioEngine_process( uint32_t nframes, void *arg )
 {
        UNUSED( arg );

+       // Hook for MIDI in-process callbacks.  It calls its own locks
+       // on the audioengine
+       if (m_pMidiDriver) m_pMidiDriver->processAudio(nframes);
+
        if ( AudioEngine::get_instance()->try_lock( "audioEngine_process" ) == 
false ) {
                return 0;
        }
@@ -1522,6 +1526,12 @@ void audioEngine_startAudioDrivers()
                m_pMidiDriver->open();
                m_pMidiDriver->setActive( true );
 #endif
+       } else if ( preferencesMng->m_sMidiDriver == "JackMidi" ) {
+#ifdef JACKMIDI_SUPPORT
+           m_pMidiDriver = new JackMidiDriver();
+           m_pMidiDriver->open();
+           m_pMidiDriver->setActive( true );
+#endif
        }

        // change the current audio engine state
@@ -1584,6 +1594,8 @@ void audioEngine_stopAudioDrivers()
 {
        _INFOLOG( "[audioEngine_stopAudioDrivers]" );

+       AudioEngine::get_instance()->lock( "audioEngine_stopAudioDrivers" );
+
        // check current state
        if ( m_audioEngineState == STATE_PLAYING ) {
                audioEngine_stop();
@@ -1611,7 +1623,6 @@ void audioEngine_stopAudioDrivers()
        }


-       AudioEngine::get_instance()->lock( "audioEngine_stopAudioDrivers" );
        // change the current audio engine state
        m_audioEngineState = STATE_INITIALIZED;
        EventQueue::get_instance()->push_event( EVENT_STATE, STATE_INITIALIZED 
);
@@ -1741,9 +1752,7 @@ void Hydrogen::midi_noteOff( Note *note )
        audioEngine_noteOff( note );
 }

-
-
-void Hydrogen::addRealtimeNote( int instrument, float velocity, float pan_L, float pan_R, float pitch, bool forcePlay ) +void Hydrogen::addRealtimeNote( int instrument, float velocity, float pan_L, float pan_R, float pitch, bool forcePlay, bool use_frame, uint32_t frame )
 {
        UNUSED( pitch );

@@ -1765,9 +1774,8 @@ void Hydrogen::addRealtimeNote( int instrument, float velocity, float pan_L, flo
                return;
        }

-       unsigned int column = getTickPosition();
-
-       realcolumn = getRealtimeTickPosition();
+ realcolumn = (use_frame) ? getRealtimeTickPosition(frame) : getRealtimeTickPosition();
+       unsigned int column = (use_frame) ? realcolumn : getTickPosition();

        // quantize it to scale
        int qcolumn = ( int )::round( column / ( double )scalar ) * scalar;
@@ -1865,10 +1873,10 @@ unsigned long Hydrogen::getTickPosition()



-unsigned long Hydrogen::getRealtimeTickPosition()
+unsigned long Hydrogen::getRealtimeTickPosition(unsigned long offset)
 {
        //unsigned long initTick = audioEngine_getTickPosition();
- unsigned int initTick = ( unsigned int )( m_nRealtimeFrames / m_pAudioDriver->m_transport.m_nTickSize ); + unsigned int initTick = ( unsigned int )( (m_nRealtimeFrames+offset) / m_pAudioDriver->m_transport.m_nTickSize );
        unsigned long retTick;

        struct timeval currtime;
@@ -2706,5 +2714,13 @@ void Hydrogen::togglePlaysSelected() {
        
 }

+#ifdef JACK_SUPPORT
+int jackMidiFallbackProcess(jack_nframes_t nframes, void* /*arg*/)
+{
+    JackMidiDriver* instance =
+       dynamic_cast<JackMidiDriver*>(m_pMidiDriver)->processNonAudio(nframes);
+}
+#endif
+
 };

Attachment: h2_jack_midi_support_alpha_20080721.tar.gz
Description: application/gzip

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Hydrogen-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/hydrogen-devel

Reply via email to