Support for using jack midi input (#2038)
* Support jack midi input * If jack is used for audio then use the same connection for midi as well * Remove old FreeBSD adjustment for portaudio as multiple version support has been dropped. * Disable jack midi out port until it is functional
This commit is contained in:
committed by
Oskar Wallgren
parent
c9618961d6
commit
05ace7e348
@@ -589,6 +589,7 @@ MESSAGE(
|
||||
"* ALSA : ${STATUS_ALSA}\n"
|
||||
"* OSS : ${STATUS_OSS}\n"
|
||||
"* Sndio : ${STATUS_SNDIO}\n"
|
||||
"* JACK : ${STATUS_JACK}\n"
|
||||
"* WinMM : ${STATUS_WINMM}\n"
|
||||
"* AppleMidi : ${STATUS_APPLEMIDI}\n"
|
||||
)
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
#include "AudioDevice.h"
|
||||
#include "AudioDeviceSetupWidget.h"
|
||||
|
||||
|
||||
class QLineEdit;
|
||||
class LcdSpinBox;
|
||||
class MidiJack;
|
||||
|
||||
|
||||
class AudioJack : public QObject, public AudioDevice
|
||||
@@ -49,6 +49,12 @@ public:
|
||||
AudioJack( bool & _success_ful, Mixer* mixer );
|
||||
virtual ~AudioJack();
|
||||
|
||||
// this is to allow the jack midi connection to use the same jack client connection
|
||||
// the jack callback is handled here, we call the midi client so that it can read
|
||||
// it's midi data during the callback
|
||||
AudioJack * addMidiClient(MidiJack *midiClient);
|
||||
jack_client_t * jackClient() {return m_client;};
|
||||
|
||||
inline static QString name()
|
||||
{
|
||||
return QT_TRANSLATE_NOOP( "setupWidget",
|
||||
@@ -98,6 +104,7 @@ private:
|
||||
bool m_active;
|
||||
bool m_stopped;
|
||||
|
||||
MidiJack *m_midiClient;
|
||||
QVector<jack_port_t *> m_outputPorts;
|
||||
jack_default_audio_sample_t * * m_tempOutBufs;
|
||||
surroundSampleFrame * m_outBuf;
|
||||
|
||||
@@ -45,11 +45,7 @@ public:
|
||||
|
||||
#ifdef LMMS_HAVE_PORTAUDIO
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
#include <portaudio2/portaudio.h>
|
||||
#else
|
||||
#include <portaudio.h>
|
||||
#endif
|
||||
|
||||
#include <QtCore/QSemaphore>
|
||||
|
||||
|
||||
95
include/MidiJack.h
Normal file
95
include/MidiJack.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* MidiJack.h - MIDI client for Jack
|
||||
*
|
||||
* Copyright (c) 2015 Shane Ambler <develop/at/shaneware.biz>
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* 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 (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MIDIJACK_H
|
||||
#define MIDIJACK_H
|
||||
|
||||
#include "lmmsconfig.h"
|
||||
|
||||
#ifdef LMMS_HAVE_JACK
|
||||
#include <jack/jack.h>
|
||||
#include <jack/midiport.h>
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QMutex>
|
||||
#include <QtCore/QFile>
|
||||
|
||||
#include "MidiClient.h"
|
||||
#include "AudioJack.h"
|
||||
|
||||
#define JACK_MIDI_BUFFER_MAX 64 /* events */
|
||||
|
||||
class QLineEdit;
|
||||
|
||||
class MidiJack : public MidiClientRaw, public QThread
|
||||
{
|
||||
public:
|
||||
MidiJack();
|
||||
virtual ~MidiJack();
|
||||
|
||||
jack_client_t* jackClient();
|
||||
|
||||
static QString probeDevice();
|
||||
|
||||
inline static QString name()
|
||||
{
|
||||
return( QT_TRANSLATE_NOOP( "MidiSetupWidget",
|
||||
"Jack-MIDI" ) );
|
||||
}
|
||||
|
||||
void JackMidiWrite(jack_nframes_t nframes);
|
||||
void JackMidiRead(jack_nframes_t nframes);
|
||||
|
||||
|
||||
inline static QString configSection()
|
||||
{
|
||||
return "MidiJack";
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
virtual void sendByte( const unsigned char c );
|
||||
virtual void run();
|
||||
|
||||
|
||||
private:
|
||||
AudioJack *m_jackAudio;
|
||||
jack_client_t *m_jackClient;
|
||||
jack_port_t *m_input_port;
|
||||
jack_port_t *m_output_port;
|
||||
uint8_t m_jack_buffer[JACK_MIDI_BUFFER_MAX * 4];
|
||||
|
||||
void JackMidiOutEvent(uint8_t *buf, uint8_t len);
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
void getPortInfo( const QString& sPortName, int& nClient, int& nPort );
|
||||
|
||||
volatile bool m_quit;
|
||||
|
||||
};
|
||||
|
||||
#endif // LMMS_HAVE_JACK
|
||||
|
||||
#endif // MIDIJACK_H
|
||||
@@ -84,6 +84,7 @@ set(LMMS_SRCS
|
||||
core/midi/MidiAlsaSeq.cpp
|
||||
core/midi/MidiClient.cpp
|
||||
core/midi/MidiController.cpp
|
||||
core/midi/MidiJack.cpp
|
||||
core/midi/MidiOss.cpp
|
||||
core/midi/MidiSndio.cpp
|
||||
core/midi/MidiApple.cpp
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
// platform-specific midi-interface-classes
|
||||
#include "MidiAlsaRaw.h"
|
||||
#include "MidiAlsaSeq.h"
|
||||
#include "MidiJack.h"
|
||||
#include "MidiOss.h"
|
||||
#include "MidiSndio.h"
|
||||
#include "MidiWinMM.h"
|
||||
@@ -975,6 +976,19 @@ MidiClient * Mixer::tryMidiClients()
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LMMS_HAVE_JACK
|
||||
if( client_name == MidiJack::name() || client_name == "" )
|
||||
{
|
||||
MidiJack * mjack = new MidiJack;
|
||||
if( mjack->isRunning() )
|
||||
{
|
||||
m_midiClientName = MidiJack::name();
|
||||
return mjack;
|
||||
}
|
||||
delete mjack;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LMMS_HAVE_OSS
|
||||
if( client_name == MidiOss::name() || client_name == "" )
|
||||
{
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
#include "AudioPort.h"
|
||||
#include "MainWindow.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
#include "MidiJack.h"
|
||||
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ AudioJack::AudioJack( bool & _success_ful, Mixer* _mixer ) :
|
||||
_mixer ),
|
||||
m_client( NULL ),
|
||||
m_active( false ),
|
||||
m_midiClient( NULL ),
|
||||
m_tempOutBufs( new jack_default_audio_sample_t *[channels()] ),
|
||||
m_outBuf( new surroundSampleFrame[mixer()->framesPerPeriod()] ),
|
||||
m_framesDoneInCurBuf( 0 ),
|
||||
@@ -123,7 +124,15 @@ void AudioJack::restartAfterZombified()
|
||||
|
||||
|
||||
|
||||
AudioJack* AudioJack::addMidiClient(MidiJack *midiClient)
|
||||
{
|
||||
if( m_client == NULL )
|
||||
return NULL;
|
||||
|
||||
m_midiClient = midiClient;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
bool AudioJack::initJackClient()
|
||||
{
|
||||
@@ -331,6 +340,14 @@ void AudioJack::renamePort( AudioPort * _port )
|
||||
|
||||
int AudioJack::processCallback( jack_nframes_t _nframes, void * _udata )
|
||||
{
|
||||
// do midi processing first so that midi input can
|
||||
// add to the following sound processing
|
||||
if( m_midiClient && _nframes > 0 )
|
||||
{
|
||||
m_midiClient->JackMidiRead(_nframes);
|
||||
m_midiClient->JackMidiWrite(_nframes);
|
||||
}
|
||||
|
||||
for( int c = 0; c < channels(); ++c )
|
||||
{
|
||||
m_tempOutBufs[c] =
|
||||
|
||||
223
src/core/midi/MidiJack.cpp
Normal file
223
src/core/midi/MidiJack.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* MidiJack.cpp - MIDI client for Jack
|
||||
*
|
||||
* Copyright (c) 2015 Shane Ambler <develop/at/shaneware.biz>
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* 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 (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "MidiJack.h"
|
||||
|
||||
#ifdef LMMS_HAVE_JACK
|
||||
|
||||
#include <QCompleter>
|
||||
#include <QDirModel>
|
||||
#include <QMessageBox>
|
||||
#include <QTranslator>
|
||||
|
||||
#ifdef LMMS_HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include "ConfigManager.h"
|
||||
#include "gui_templates.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "Engine.h"
|
||||
#include "Mixer.h"
|
||||
#include "MainWindow.h"
|
||||
|
||||
/* callback functions for jack */
|
||||
static int JackMidiProcessCallback(jack_nframes_t nframes, void *arg)
|
||||
{
|
||||
MidiJack *jmd = (MidiJack *)arg;
|
||||
|
||||
if (nframes <= 0)
|
||||
return (0);
|
||||
|
||||
jmd->JackMidiRead(nframes);
|
||||
jmd->JackMidiWrite(nframes);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void JackMidiShutdown(void *arg)
|
||||
{
|
||||
// TODO: support translations here
|
||||
const QString mess_short = "JACK server down";
|
||||
const QString mess_long = "The JACK server seems to have been shutdown.";
|
||||
QMessageBox::information( gui->mainWindow(), mess_short, mess_long );
|
||||
}
|
||||
|
||||
MidiJack::MidiJack() :
|
||||
MidiClientRaw(),
|
||||
m_input_port( NULL ),
|
||||
m_output_port( NULL ),
|
||||
m_quit( false )
|
||||
{
|
||||
// if jack is used for audio then we share the connection
|
||||
// AudioJack creates and maintains the jack connection
|
||||
// and also handles the callback, we pass it our address
|
||||
// so that we can also process during the callback
|
||||
|
||||
if(Engine::mixer()->audioDevName() == AudioJack::name() )
|
||||
{
|
||||
// if a jack connection has been created for audio we use that
|
||||
m_jackAudio = dynamic_cast<AudioJack*>(Engine::mixer()->audioDev())->addMidiClient(this);
|
||||
}else{
|
||||
m_jackAudio = NULL;
|
||||
m_jackClient = jack_client_open(probeDevice().toLatin1().data(),
|
||||
JackNoStartServer, NULL);
|
||||
|
||||
if(m_jackClient)
|
||||
{
|
||||
jack_set_process_callback(m_jackClient,
|
||||
JackMidiProcessCallback, this);
|
||||
jack_on_shutdown(m_jackClient,
|
||||
JackMidiShutdown, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if(jackClient())
|
||||
{
|
||||
/* jack midi out not implemented
|
||||
JackMidiWrite and sendByte needs to be functional
|
||||
before enabling this
|
||||
m_output_port = jack_port_register(
|
||||
jackClient(), "MIDI out", JACK_DEFAULT_MIDI_TYPE,
|
||||
JackPortIsOutput, 0);
|
||||
*/
|
||||
|
||||
m_input_port = jack_port_register(
|
||||
jackClient(), "MIDI in", JACK_DEFAULT_MIDI_TYPE,
|
||||
JackPortIsInput, 0);
|
||||
|
||||
if(jack_activate(jackClient()) == 0 )
|
||||
{
|
||||
// only start thread, if we have an active jack client.
|
||||
start( QThread::LowPriority );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MidiJack::~MidiJack()
|
||||
{
|
||||
if(jackClient())
|
||||
{
|
||||
if( jack_port_unregister( jackClient(), m_input_port) != 0){
|
||||
printf("Failed to unregister jack midi input\n");
|
||||
}
|
||||
|
||||
if( jack_port_unregister( jackClient(), m_output_port) != 0){
|
||||
printf("Failed to unregister jack midi output\n");
|
||||
}
|
||||
|
||||
if(m_jackClient)
|
||||
{
|
||||
// an m_jackClient means we are handling the jack connection
|
||||
if( jack_deactivate(m_jackClient) != 0){
|
||||
printf("Failed to deactivate jack midi client\n");
|
||||
}
|
||||
|
||||
if( jack_client_close(m_jackClient) != 0){
|
||||
printf("Failed close jack midi client\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if( isRunning() )
|
||||
{
|
||||
m_quit = true;
|
||||
wait( 1000 );
|
||||
terminate();
|
||||
}
|
||||
}
|
||||
|
||||
jack_client_t* MidiJack::jackClient()
|
||||
{
|
||||
if( m_jackAudio == NULL && m_jackClient == NULL)
|
||||
return NULL;
|
||||
|
||||
if( m_jackAudio == NULL && m_jackClient )
|
||||
return m_jackClient;
|
||||
|
||||
return m_jackAudio->jackClient();
|
||||
}
|
||||
|
||||
QString MidiJack::probeDevice()
|
||||
{
|
||||
QString jid = ConfigManager::inst()->value( "midijack", "lmms" );
|
||||
if( jid.isEmpty() )
|
||||
{
|
||||
return "lmms";
|
||||
}
|
||||
return jid;
|
||||
}
|
||||
|
||||
// we read data from jack
|
||||
void MidiJack::JackMidiRead(jack_nframes_t nframes)
|
||||
{
|
||||
unsigned int i,b;
|
||||
void* port_buf = jack_port_get_buffer(m_input_port, nframes);
|
||||
jack_midi_event_t in_event;
|
||||
jack_nframes_t event_index = 0;
|
||||
jack_nframes_t event_count = jack_midi_get_event_count(port_buf);
|
||||
|
||||
jack_midi_event_get(&in_event, port_buf, 0);
|
||||
for(i=0; i<nframes; i++)
|
||||
{
|
||||
if((in_event.time == i) && (event_index < event_count))
|
||||
{
|
||||
// lmms is setup to parse bytes coming from a device
|
||||
// parse it byte by byte as it expects
|
||||
for(b=0;b<in_event.size;b++)
|
||||
parseData( *(in_event.buffer + b) );
|
||||
|
||||
event_index++;
|
||||
if(event_index < event_count)
|
||||
jack_midi_event_get(&in_event, port_buf, event_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* jack midi out is not implemented
|
||||
sending plain bytes to jack midi outputs doesn't work
|
||||
once working the output port needs to be enabled in the constructor
|
||||
*/
|
||||
|
||||
void MidiJack::sendByte( const unsigned char c )
|
||||
{
|
||||
//m_midiDev.putChar( c );
|
||||
}
|
||||
|
||||
// we write data to jack
|
||||
void MidiJack::JackMidiWrite(jack_nframes_t nframes)
|
||||
{
|
||||
// TODO: write midi data to jack port
|
||||
}
|
||||
|
||||
void MidiJack::run()
|
||||
{
|
||||
while( m_quit == false )
|
||||
{
|
||||
// we sleep the thread to keep it alive
|
||||
// midi processing is handled by jack server callbacks
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // LMMS_HAVE_JACK
|
||||
@@ -65,6 +65,7 @@
|
||||
// platform-specific midi-interface-classes
|
||||
#include "MidiAlsaRaw.h"
|
||||
#include "MidiAlsaSeq.h"
|
||||
#include "MidiJack.h"
|
||||
#include "MidiOss.h"
|
||||
#include "MidiSndio.h"
|
||||
#include "MidiWinMM.h"
|
||||
@@ -862,6 +863,11 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) :
|
||||
MidiSetupWidget::create<MidiAlsaRaw>( msw );
|
||||
#endif
|
||||
|
||||
#ifdef LMMS_HAVE_JACK
|
||||
m_midiIfaceSetupWidgets[MidiJack::name()] =
|
||||
MidiSetupWidget::create<MidiJack>( msw );
|
||||
#endif
|
||||
|
||||
#ifdef LMMS_HAVE_OSS
|
||||
m_midiIfaceSetupWidgets[MidiOss::name()] =
|
||||
MidiSetupWidget::create<MidiOss>( msw );
|
||||
|
||||
Reference in New Issue
Block a user