2012-05-26 14:49:10 +02:00
//=============================================================================
// MusE Score
// Linux Music Score Editor
// $Id: jackaudio.cpp 5660 2012-05-22 14:17:39Z wschweer $
//
// Copyright (C) 2002-2010 Werner Schweer and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
# include "jackaudio.h"
2014-05-30 23:18:52 +02:00
# include "musescore.h"
2012-05-26 14:49:10 +02:00
# include "libmscore/mscore.h"
# include "preferences.h"
2013-03-26 19:59:51 +01:00
// #include "msynth/synti.h"
2012-05-26 14:49:10 +02:00
# include "seq.h"
2014-05-30 23:18:52 +02:00
# include "libmscore/score.h"
2014-06-18 21:36:53 +02:00
# include "libmscore/repeatlist.h"
2014-07-02 20:47:42 +02:00
# include "mscore/playpanel.h"
2012-05-26 14:49:10 +02:00
# include <jack/midiport.h>
2014-08-18 13:58:57 +02:00
// Prevent killing sequencer with wrong data
2014-07-27 21:10:16 +02:00
# define less128(__less) ((__less >=0 && __less <= 127) ? __less : 0)
2014-08-18 13:58:57 +02:00
2013-05-13 18:49:17 +02:00
namespace Ms {
2012-05-26 14:49:10 +02:00
//---------------------------------------------------------
// JackAudio
//---------------------------------------------------------
JackAudio : : JackAudio ( Seq * s )
: Driver ( s )
{
client = 0 ;
}
//---------------------------------------------------------
// ~JackAudio
//---------------------------------------------------------
JackAudio : : ~ JackAudio ( )
{
if ( client ) {
2014-05-24 21:30:33 +02:00
stop ( ) ;
2012-05-26 14:49:10 +02:00
if ( jack_client_close ( client ) ) {
2014-03-25 13:33:47 +01:00
qDebug ( " jack_client_close() failed: %s " ,
2012-05-26 14:49:10 +02:00
strerror ( errno ) ) ;
}
}
}
//---------------------------------------------------------
// registerPort
//---------------------------------------------------------
void JackAudio : : registerPort ( const QString & name , bool input , bool midi )
{
int portFlag = input ? JackPortIsInput : JackPortIsOutput ;
const char * portType = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE ;
jack_port_t * port = jack_port_register ( client , qPrintable ( name ) , portType , portFlag , 0 ) ;
if ( port = = 0 ) {
2014-03-25 13:33:47 +01:00
qDebug ( " JackAudio:registerPort(%s) failed " , qPrintable ( name ) ) ;
2012-05-26 14:49:10 +02:00
return ;
}
if ( midi ) {
if ( input )
midiInputPorts . append ( port ) ;
else
midiOutputPorts . append ( port ) ;
}
else
ports . append ( port ) ;
}
//---------------------------------------------------------
// unregisterPort
//---------------------------------------------------------
2014-07-04 19:59:33 +02:00
void JackAudio : : unregisterPort ( jack_port_t * port )
2012-05-26 14:49:10 +02:00
{
2014-07-05 20:59:24 +02:00
if ( jack_port_is_mine ( client , port ) ) {
jack_port_unregister ( client , port ) ;
port = 0 ;
}
else
qDebug ( " Trying to unregister port that is not my! " ) ;
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// inputPorts
//---------------------------------------------------------
QList < QString > JackAudio : : inputPorts ( )
{
const char * * ports = jack_get_ports ( client , 0 , 0 , 0 ) ;
QList < QString > clientList ;
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 ] ;
strncpy ( buffer , * p , 128 ) ;
if ( strncmp ( buffer , " Mscore " , 6 ) = = 0 )
continue ;
clientList . append ( QString ( buffer ) ) ;
}
return clientList ;
}
//---------------------------------------------------------
// connect
//---------------------------------------------------------
void JackAudio : : connect ( void * src , void * dst )
{
const char * sn = jack_port_name ( ( jack_port_t * ) src ) ;
const char * dn = jack_port_name ( ( jack_port_t * ) dst ) ;
if ( sn = = 0 | | dn = = 0 ) {
2014-03-25 13:33:47 +01:00
qDebug ( " JackAudio::connect: unknown jack ports " ) ;
2012-05-26 14:49:10 +02:00
return ;
}
if ( jack_connect ( client , sn , dn ) ) {
2014-03-25 13:33:47 +01:00
qDebug ( " jack connect <%s>%p - <%s>%p failed " ,
2012-05-26 14:49:10 +02:00
sn , src , dn , dst ) ;
}
}
//---------------------------------------------------------
2014-07-05 20:59:24 +02:00
// connect
//---------------------------------------------------------
void JackAudio : : connect ( const char * src , const char * dst )
{
if ( src = = 0 | | dst = = 0 ) {
qDebug ( " JackAudio::connect: unknown jack ports " ) ;
return ;
}
int rv = jack_connect ( client , src , dst ) ;
if ( rv )
qDebug ( " jack connect port <%s> - <%s> failed: %d " , src , dst , rv ) ;
}
//---------------------------------------------------------
2012-05-26 14:49:10 +02:00
// disconnect
//---------------------------------------------------------
void JackAudio : : disconnect ( void * src , void * dst )
{
const char * sn = jack_port_name ( ( jack_port_t * ) src ) ;
const char * dn = jack_port_name ( ( jack_port_t * ) dst ) ;
if ( sn = = 0 | | dn = = 0 ) {
2014-03-25 13:33:47 +01:00
qDebug ( " JackAudio::disconnect: unknown jack ports " ) ;
2012-05-26 14:49:10 +02:00
return ;
}
if ( jack_disconnect ( client , sn , dn ) ) {
2014-03-25 13:33:47 +01:00
qDebug ( " jack disconnect <%s> - <%s> failed " , sn , dn ) ;
2012-05-26 14:49:10 +02:00
}
}
//---------------------------------------------------------
// start
// return false on error
//---------------------------------------------------------
2014-07-30 15:38:54 +02:00
bool JackAudio : : start ( bool hotPlug )
2012-05-26 14:49:10 +02:00
{
2014-07-30 15:38:54 +02:00
bool oldremember = preferences . rememberLastConnections ;
if ( hotPlug )
preferences . rememberLastConnections = true ;
2012-05-26 14:49:10 +02:00
if ( jack_activate ( client ) ) {
2014-03-25 13:33:47 +01:00
qDebug ( " JACK: cannot activate client " ) ;
2012-05-26 14:49:10 +02:00
return false ;
}
2014-07-05 20:59:24 +02:00
/* connect the ports. Note: you can't do this before
the client is activated , because we can ' t allow
connections to be made to clients that aren ' t
running .
*/
if ( preferences . useJackAudio )
2014-07-04 19:59:33 +02:00
restoreAudioConnections ( ) ;
2014-07-05 20:59:24 +02:00
if ( preferences . useJackMidi )
2014-07-04 19:59:33 +02:00
restoreMidiConnections ( ) ;
2014-07-30 15:38:54 +02:00
if ( hotPlug )
preferences . rememberLastConnections = oldremember ;
2012-05-26 14:49:10 +02:00
return true ;
}
//---------------------------------------------------------
// stop
// return false on error
//---------------------------------------------------------
bool JackAudio : : stop ( )
{
2014-07-05 20:59:24 +02:00
if ( preferences . useJackMidi )
2014-07-04 19:59:33 +02:00
rememberMidiConnections ( ) ;
2014-07-05 20:59:24 +02:00
if ( preferences . useJackAudio )
rememberAudioConnections ( ) ;
2012-05-26 14:49:10 +02:00
if ( jack_deactivate ( client ) ) {
qDebug ( " cannot deactivate client " ) ;
return false ;
}
return true ;
}
//---------------------------------------------------------
// framePos
//---------------------------------------------------------
int JackAudio : : framePos ( ) const
{
jack_nframes_t n = jack_frame_time ( client ) ;
return ( int ) n ;
}
2014-06-18 21:36:53 +02:00
//---------------------------------------------------------
// freewheel_callback
//---------------------------------------------------------
2012-05-26 14:49:10 +02:00
2014-06-18 21:36:53 +02:00
static void freewheel_callback ( int /*starting*/ , void * )
2012-05-26 14:49:10 +02:00
{
}
//---------------------------------------------------------
2014-06-18 21:36:53 +02:00
// sampleRateCallback
2012-05-26 14:49:10 +02:00
//---------------------------------------------------------
2014-06-18 21:36:53 +02:00
int sampleRateCallback ( jack_nframes_t sampleRate , void * )
2012-05-26 14:49:10 +02:00
{
2014-06-18 21:36:53 +02:00
qDebug ( " JACK: sample rate changed: %d " , sampleRate ) ;
MScore : : sampleRate = sampleRate ;
return 0 ;
2012-05-26 14:49:10 +02:00
}
2014-06-18 21:36:53 +02:00
//---------------------------------------------------------
// bufferSizeCallback called if JACK buffer changed
//---------------------------------------------------------
int bufferSizeCallback ( jack_nframes_t nframes , void * arg )
2012-05-26 14:49:10 +02:00
{
2014-06-18 21:36:53 +02:00
JackAudio * audio = ( JackAudio * ) arg ;
audio - > setBufferSize ( nframes ) ;
2012-05-26 14:49:10 +02:00
return 0 ;
}
static void registration_callback ( jack_port_id_t , int , void * )
{
2014-03-25 13:33:47 +01:00
// qDebug("JACK: registration changed");
2012-05-26 14:49:10 +02:00
}
static int graph_callback ( void * )
{
2014-03-25 13:33:47 +01:00
// qDebug("JACK: graph changed");
2012-05-26 14:49:10 +02:00
return 0 ;
}
2014-06-05 17:50:29 +02:00
void JackAudio : : timebase ( jack_transport_state_t state , jack_nframes_t /*nframes*/ , jack_position_t * pos , int /*new_pos*/ , void * arg )
2014-05-30 23:18:52 +02:00
{
JackAudio * audio = ( JackAudio * ) arg ;
2014-07-02 20:47:42 +02:00
if ( ! audio - > seq - > score ( ) ) {
if ( state = = JackTransportLooping | | state = = JackTransportRolling )
2014-05-30 23:18:52 +02:00
audio - > stopTransport ( ) ;
}
2014-07-02 20:47:42 +02:00
else if ( audio - > seq - > isRunning ( ) ) {
if ( ! audio - > seq - > score ( ) - > repeatList ( ) | | ! audio - > seq - > score ( ) - > sigmap ( ) )
return ;
2014-05-30 23:18:52 +02:00
2014-07-02 20:47:42 +02:00
pos - > valid = JackPositionBBT ;
int curTick = audio - > seq - > score ( ) - > repeatList ( ) - > utick2tick ( audio - > seq - > getCurTick ( ) ) ;
2014-05-30 23:18:52 +02:00
int bar , beat , tick ;
audio - > seq - > score ( ) - > sigmap ( ) - > tickValues ( curTick , & bar , & beat , & tick ) ;
2014-07-02 20:47:42 +02:00
// Providing the final tempo
pos - > beats_per_minute = 60 * audio - > seq - > curTempo ( ) * audio - > seq - > score ( ) - > tempomap ( ) - > relTempo ( ) ;
pos - > ticks_per_beat = MScore : : division ;
pos - > tick = tick ;
pos - > bar = bar + 1 ;
pos - > beat = beat + 1 ;
if ( audio - > timeSigTempoChanged ) {
Fraction timeSig = audio - > seq - > score ( ) - > sigmap ( ) - > timesig ( curTick ) . nominal ( ) ;
pos - > beats_per_bar = timeSig . numerator ( ) ;
pos - > beat_type = timeSig . denominator ( ) ;
audio - > timeSigTempoChanged = false ;
qDebug ( ) < < " Time signature changed: " < < pos - > beats_per_minute < < " , bar: " < < pos - > bar < < " ,beat: " < < pos - > beat < < " , tick: " < < pos - > tick < < " , time sig: " < < pos - > beats_per_bar < < " / " < < pos - > beat_type ;
}
2014-05-30 23:18:52 +02:00
}
2014-06-18 21:36:53 +02:00
// TODO: Handle new_pos
2014-05-30 23:18:52 +02:00
}
2012-05-26 14:49:10 +02:00
//---------------------------------------------------------
// processAudio
// JACK callback
//---------------------------------------------------------
int JackAudio : : processAudio ( jack_nframes_t frames , void * p )
{
JackAudio * audio = ( JackAudio * ) p ;
2014-05-30 23:18:52 +02:00
// Prevent from crash if score not opened yet
if ( ! audio - > seq - > score ( ) )
return 0 ;
2012-05-26 14:49:10 +02:00
float * l ;
float * r ;
2014-07-04 19:59:33 +02:00
if ( preferences . useJackAudio & & audio - > ports . size ( ) = = 2 ) {
2012-05-26 14:49:10 +02:00
l = ( float * ) jack_port_get_buffer ( audio - > ports [ 0 ] , frames ) ;
r = ( float * ) jack_port_get_buffer ( audio - > ports [ 1 ] , frames ) ;
}
else {
l = 0 ;
r = 0 ;
}
if ( preferences . useJackMidi ) {
foreach ( jack_port_t * port , audio - > midiOutputPorts ) {
void * portBuffer = jack_port_get_buffer ( port , frames ) ;
jack_midi_clear_buffer ( portBuffer ) ;
}
foreach ( jack_port_t * port , audio - > midiInputPorts ) {
void * portBuffer = jack_port_get_buffer ( port , frames ) ;
if ( portBuffer ) {
jack_nframes_t n = jack_midi_get_event_count ( portBuffer ) ;
for ( jack_nframes_t i = 0 ; i < n ; + + i ) {
jack_midi_event_t event ;
int r = jack_midi_event_get ( & event , portBuffer , i ) ;
if ( r ! = 0 )
continue ;
int nn = event . size ;
int type = event . buffer [ 0 ] ;
if ( nn & & ( type = = ME_CLOCK | | type = = ME_SENSE ) )
continue ;
Event e ;
e . setType ( type ) ;
e . setChannel ( type & 0xf ) ;
type & = 0xf0 ;
if ( type = = ME_NOTEON | | type = = ME_NOTEOFF ) {
e . setPitch ( event . buffer [ 1 ] ) ;
e . setVelo ( event . buffer [ 2 ] ) ;
audio - > seq - > eventToGui ( e ) ;
}
else if ( type = = ME_CONTROLLER ) {
e . setController ( event . buffer [ 1 ] ) ;
e . setValue ( event . buffer [ 2 ] ) ;
audio - > seq - > eventToGui ( e ) ;
}
}
}
}
}
2013-02-09 14:00:03 +01:00
if ( l & & r ) {
float buffer [ frames * 2 ] ;
audio - > seq - > process ( ( unsigned ) frames , buffer ) ;
float * sp = buffer ;
for ( unsigned i = 0 ; i < frames ; + + i ) {
* l + + = * sp + + ;
* r + + = * sp + + ;
}
2012-05-26 14:49:10 +02:00
}
2014-05-24 21:30:33 +02:00
else {
// JACK MIDI only
float buffer [ frames * 2 ] ;
audio - > seq - > process ( ( unsigned ) frames , buffer ) ;
}
2012-05-26 14:49:10 +02:00
return 0 ;
}
//---------------------------------------------------------
// jackError
//---------------------------------------------------------
static void jackError ( const char * s )
{
2014-03-25 13:33:47 +01:00
qDebug ( " JACK ERROR: %s " , s ) ;
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// noJackError
//---------------------------------------------------------
2014-05-24 21:30:33 +02:00
static void noJackError ( const char * s )
2012-05-26 14:49:10 +02:00
{
2014-05-24 21:30:33 +02:00
qDebug ( " noJACK ERROR: %s " , s ) ;
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// init
// return false on error
//---------------------------------------------------------
2014-07-04 19:59:33 +02:00
bool JackAudio : : init ( bool hot )
2012-05-26 14:49:10 +02:00
{
2014-07-04 19:59:33 +02:00
if ( hot ) {
hotPlug ( ) ;
return true ;
}
2012-05-26 14:49:10 +02:00
jack_set_error_function ( noJackError ) ;
client = 0 ;
2014-05-30 23:18:52 +02:00
timeSigTempoChanged = false ;
2014-07-03 18:02:33 +02:00
fakeState = Transport : : STOP ;
2012-05-26 14:49:10 +02:00
strcpy ( _jackName , " mscore " ) ;
jack_options_t options = ( jack_options_t ) 0 ;
jack_status_t status ;
client = jack_client_open ( _jackName , options , & status ) ;
2014-07-07 17:31:41 +02:00
2012-05-26 14:49:10 +02:00
if ( client = = 0 ) {
2014-03-25 13:33:47 +01:00
qDebug ( " JackAudio()::init(): failed, status 0x%0x " , status ) ;
2012-05-26 14:49:10 +02:00
return false ;
}
jack_set_error_function ( jackError ) ;
jack_set_process_callback ( client , processAudio , this ) ;
//jack_on_shutdown(client, processShutdown, this);
2014-06-18 21:36:53 +02:00
jack_set_sample_rate_callback ( client , sampleRateCallback , this ) ;
2012-05-26 14:49:10 +02:00
jack_set_port_registration_callback ( client , registration_callback , this ) ;
jack_set_graph_order_callback ( client , graph_callback , this ) ;
jack_set_freewheel_callback ( client , freewheel_callback , this ) ;
2014-07-30 15:38:54 +02:00
if ( preferences . jackTimebaseMaster )
2014-07-04 19:59:33 +02:00
setTimebaseCallback ( ) ;
if ( jack_set_buffer_size_callback ( client , bufferSizeCallback , this ) ! = 0 )
qDebug ( " Can not set bufferSizeCallback " ) ;
2012-05-26 14:49:10 +02:00
_segmentSize = jack_get_buffer_size ( client ) ;
MScore : : sampleRate = sampleRate ( ) ;
// register mscore left/right output ports
if ( preferences . useJackAudio ) {
registerPort ( " left " , false , false ) ;
registerPort ( " right " , false , false ) ;
}
if ( preferences . useJackMidi ) {
for ( int i = 0 ; i < preferences . midiPorts ; + + i )
registerPort ( QString ( " mscore-midi-%1 " ) . arg ( i + 1 ) , false , true ) ;
registerPort ( QString ( " mscore-midiin-1 " ) , true , true ) ;
}
return true ;
}
//---------------------------------------------------------
// startTransport
//---------------------------------------------------------
void JackAudio : : startTransport ( )
{
2014-07-03 18:02:33 +02:00
if ( preferences . useJackTransport )
jack_transport_start ( client ) ;
else
fakeState = Transport : : PLAY ;
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// stopTrasnport
//---------------------------------------------------------
void JackAudio : : stopTransport ( )
{
2014-07-03 18:02:33 +02:00
if ( preferences . useJackTransport )
jack_transport_stop ( client ) ;
else
fakeState = Transport : : STOP ;
2012-05-26 14:49:10 +02:00
}
//---------------------------------------------------------
// getState
//---------------------------------------------------------
2014-06-03 17:14:35 +02:00
Transport JackAudio : : getState ( )
2012-05-26 14:49:10 +02:00
{
2014-07-03 18:02:33 +02:00
if ( ! preferences . useJackTransport )
return fakeState ;
2014-06-18 21:36:53 +02:00
int transportState = jack_transport_query ( client , NULL ) ;
2012-05-26 14:49:10 +02:00
switch ( transportState ) {
2014-06-03 17:14:35 +02:00
case JackTransportStopped : return Transport : : STOP ;
2012-05-26 14:49:10 +02:00
case JackTransportLooping :
2014-06-03 17:14:35 +02:00
case JackTransportRolling : return Transport : : PLAY ;
2014-06-18 21:36:53 +02:00
case JackTransportStarting : return seq - > isPlaying ( ) ? Transport : : PLAY : Transport : : STOP ; // Keep current state
2012-05-26 14:49:10 +02:00
default :
2014-06-03 17:14:35 +02:00
return Transport : : STOP ;
2012-05-26 14:49:10 +02:00
}
}
//---------------------------------------------------------
// putEvent
//---------------------------------------------------------
2014-05-24 21:30:33 +02:00
void JackAudio : : putEvent ( const NPlayEvent & e , unsigned framePos )
2012-05-26 14:49:10 +02:00
{
if ( ! preferences . useJackMidi )
return ;
2014-07-27 21:10:16 +02:00
int portIdx = seq - > score ( ) - > midiPort ( e . channel ( ) ) ;
int chan = seq - > score ( ) - > midiChannel ( e . channel ( ) ) ;
2012-05-26 14:49:10 +02:00
2014-03-25 13:33:47 +01:00
// qDebug("JackAudio::putEvent %d:%d pos %d(%d)", portIdx, chan, framePos, _segmentSize);
2012-05-26 14:49:10 +02:00
if ( portIdx < 0 | | portIdx > = midiOutputPorts . size ( ) ) {
2014-03-25 13:33:47 +01:00
qDebug ( " JackAudio::putEvent: invalid port %d " , portIdx ) ;
2012-05-26 14:49:10 +02:00
return ;
}
jack_port_t * port = midiOutputPorts [ portIdx ] ;
if ( midiOutputTrace ) {
const char * portName = jack_port_name ( port ) ;
2014-07-05 20:59:24 +02:00
int a = e . dataA ( ) ;
int b = e . dataB ( ) ;
2014-08-18 13:58:57 +02:00
qDebug ( " MidiOut<%s>: jackMidi: %02x %02x %02x, chan: %i " , portName , e . type ( ) , a , b , chan ) ;
2012-05-26 14:49:10 +02:00
// e.dump();
}
void * pb = jack_port_get_buffer ( port , _segmentSize ) ;
2014-08-18 13:58:57 +02:00
if ( pb = = NULL ) {
qDebug ( ) < < " jack_port_get_buffer failed, cannot send anything " ;
}
2012-05-26 14:49:10 +02:00
if ( framePos > = _segmentSize ) {
2014-03-25 13:33:47 +01:00
qDebug ( " JackAudio::putEvent: time out of range %d(seg=%d) " , framePos , _segmentSize ) ;
2012-05-26 14:49:10 +02:00
if ( framePos > _segmentSize )
framePos = _segmentSize - 1 ;
}
switch ( e . type ( ) ) {
case ME_NOTEON :
case ME_NOTEOFF :
case ME_POLYAFTER :
case ME_CONTROLLER :
2014-07-27 21:10:16 +02:00
// Catch CTRL_PROGRAM and let other ME_CONTROLLER events to go
2014-08-18 13:58:57 +02:00
if ( e . dataA ( ) = = CTRL_PROGRAM ) {
2014-07-27 21:10:16 +02:00
// Convert CTRL_PROGRAM event to ME_PROGRAM
2014-08-18 13:58:57 +02:00
unsigned char * p = jack_midi_event_reserve ( pb , framePos , 2 ) ;
if ( p = = 0 ) {
qDebug ( " JackMidi: buffer overflow, event lost " ) ;
return ;
}
p [ 0 ] = ME_PROGRAM | chan ;
p [ 1 ] = less128 ( e . dataB ( ) ) ;
break ;
}
//Fallback
2012-05-26 14:49:10 +02:00
case ME_PITCHBEND :
{
unsigned char * p = jack_midi_event_reserve ( pb , framePos , 3 ) ;
if ( p = = 0 ) {
2014-03-25 13:33:47 +01:00
qDebug ( " JackMidi: buffer overflow, event lost " ) ;
2012-05-26 14:49:10 +02:00
return ;
}
p [ 0 ] = e . type ( ) | chan ;
2014-08-18 13:58:57 +02:00
p [ 1 ] = less128 ( e . dataA ( ) ) ;
p [ 2 ] = less128 ( e . dataB ( ) ) ;
2012-05-26 14:49:10 +02:00
}
break ;
case ME_PROGRAM :
case ME_AFTERTOUCH :
{
unsigned char * p = jack_midi_event_reserve ( pb , framePos , 2 ) ;
if ( p = = 0 ) {
2014-03-25 13:33:47 +01:00
qDebug ( " JackMidi: buffer overflow, event lost " ) ;
2012-05-26 14:49:10 +02:00
return ;
}
p [ 0 ] = e . type ( ) | chan ;
2014-08-18 13:58:57 +02:00
p [ 1 ] = less128 ( e . dataA ( ) ) ;
2012-05-26 14:49:10 +02:00
}
break ;
2014-05-24 21:30:33 +02:00
// Do we really need to handle ME_SYSEX?
/* case ME_SYSEX:
2012-05-26 14:49:10 +02:00
{
2013-04-08 10:31:17 +02:00
const unsigned char * data = e . edata ( ) ;
2012-05-26 14:49:10 +02:00
int len = e . len ( ) ;
unsigned char * p = jack_midi_event_reserve ( pb , framePos , len + 2 ) ;
if ( p = = 0 ) {
2014-03-25 13:33:47 +01:00
qDebug ( " JackMidi: buffer overflow, event lost " ) ;
2012-05-26 14:49:10 +02:00
return ;
}
p [ 0 ] = 0xf0 ;
p [ len + 1 ] = 0xf7 ;
memcpy ( p + 1 , data , len ) ;
}
2014-05-24 21:30:33 +02:00
break ; */
2012-05-26 14:49:10 +02:00
case ME_SONGPOS :
case ME_CLOCK :
case ME_START :
case ME_CONTINUE :
case ME_STOP :
2014-03-25 13:33:47 +01:00
qDebug ( " JackMidi: event type %x not supported " , e . type ( ) ) ;
2012-05-26 14:49:10 +02:00
break ;
}
}
//---------------------------------------------------------
// midiRead
//---------------------------------------------------------
void JackAudio : : midiRead ( )
{
// midiDriver->read();
}
2014-05-30 23:18:52 +02:00
2014-06-18 21:36:53 +02:00
//---------------------------------------------------------
// Called after tempo or time signature
// changed while playback
//---------------------------------------------------------
2014-05-30 23:18:52 +02:00
void JackAudio : : handleTimeSigTempoChanged ( )
{
2014-06-18 21:36:53 +02:00
timeSigTempoChanged = true ;
2014-05-30 23:18:52 +02:00
}
2012-05-26 14:49:10 +02:00
2014-06-18 21:36:53 +02:00
//---------------------------------------------------------
// checkTransportSeek
2014-07-02 20:47:42 +02:00
// The opposite of Timebase master:
// check JACK Transport for a new position or tempo.
2014-06-18 21:36:53 +02:00
//---------------------------------------------------------
2014-07-07 17:31:41 +02:00
void JackAudio : : checkTransportSeek ( int cur_frame , int nframes , bool inCountIn )
2014-06-18 21:36:53 +02:00
{
2014-07-07 17:31:41 +02:00
if ( ! seq | | ! seq - > score ( ) | | ! seq - > canStart ( ) | | inCountIn )
2014-07-02 20:47:42 +02:00
return ;
2014-07-04 19:59:33 +02:00
2014-06-18 21:36:53 +02:00
// Obtaining the current JACK Transport position
jack_position_t pos ;
jack_transport_query ( client , & pos ) ;
2014-07-07 17:31:41 +02:00
if ( preferences . useJackTransport ) {
if ( mscore - > getPlayPanel ( ) & & mscore - > getPlayPanel ( ) - > isTempoSliderPressed ( ) )
return ;
int cur_utick = seq - > score ( ) - > utime2utick ( ( qreal ) cur_frame / MScore : : sampleRate ) ;
int utick = seq - > score ( ) - > utime2utick ( ( qreal ) pos . frame / MScore : : sampleRate ) ;
// Conversion is not precise, should check frames and uticks
if ( labs ( ( long int ) cur_frame - ( long int ) pos . frame ) > nframes + 1 & & abs ( utick - cur_utick ) > seq - > score ( ) - > utime2utick ( ( qreal ) nframes / MScore : : sampleRate ) + 1 ) {
if ( MScore : : debugMode )
qDebug ( ) < < " JACK Transport position changed, cur_frame: " < < cur_frame < < " ,pos.frame: " < < pos . frame < < " , frame diff: " < < labs ( ( long int ) cur_frame - ( long int ) pos . frame ) < < " cur utick: " < < cur_utick < < " ,seek to utick: " < < utick < < " , tick diff: " < < abs ( utick - cur_utick ) ;
seq - > seekRT ( utick ) ;
}
2014-06-18 21:36:53 +02:00
}
2014-07-02 20:47:42 +02:00
// Tempo
2014-07-30 15:38:54 +02:00
if ( ! preferences . jackTimebaseMaster & & ( pos . valid & JackPositionBBT ) ) {
2014-07-07 17:31:41 +02:00
if ( ! seq - > score ( ) - > tempomap ( ) )
2014-07-02 20:47:42 +02:00
return ;
2014-07-07 17:31:41 +02:00
2014-07-02 20:47:42 +02:00
if ( int ( pos . beats_per_minute ) ! = int ( 60 * seq - > curTempo ( ) * seq - > score ( ) - > tempomap ( ) - > relTempo ( ) ) ) {
2014-07-07 17:31:41 +02:00
if ( MScore : : debugMode )
qDebug ( ) < < " JACK Transport tempo changed! JACK bpm: " < < ( int ) pos . beats_per_minute < < " , current bpm: " < < int ( 60 * seq - > curTempo ( ) * seq - > score ( ) - > tempomap ( ) - > relTempo ( ) ) ;
if ( 60 * seq - > curTempo ( ) = = 0.0 )
return ;
2014-07-02 20:47:42 +02:00
qreal newRelTempo = pos . beats_per_minute / ( 60 * seq - > curTempo ( ) ) ;
2014-07-07 17:31:41 +02:00
seq - > setRelTempo ( newRelTempo ) ;
2014-07-03 18:02:33 +02:00
// Update UI
2014-07-02 20:47:42 +02:00
if ( mscore - > getPlayPanel ( ) ) {
mscore - > getPlayPanel ( ) - > setRelTempo ( newRelTempo ) ;
mscore - > getPlayPanel ( ) - > setTempo ( seq - > curTempo ( ) * newRelTempo ) ;
}
}
}
2014-06-18 21:36:53 +02:00
}
//---------------------------------------------------------
// seekTransport
//---------------------------------------------------------
void JackAudio : : seekTransport ( int utick )
{
2014-07-07 17:31:41 +02:00
if ( MScore : : debugMode )
qDebug ( ) < < " jack locate to utick: " < < utick < < " , frame: " < < int ( seq - > score ( ) - > utick2utime ( utick ) * MScore : : sampleRate ) ;
2014-06-18 21:36:53 +02:00
jack_transport_locate ( client , seq - > score ( ) - > utick2utime ( utick ) * MScore : : sampleRate ) ;
}
2014-07-03 18:02:33 +02:00
//---------------------------------------------------------
// setTimebaseCallback
//---------------------------------------------------------
void JackAudio : : setTimebaseCallback ( )
{
2014-07-04 19:59:33 +02:00
int errCode = jack_set_timebase_callback ( client , 0 , timebase , this ) ; // 0: force set timebase
if ( errCode = = 0 ) {
2014-07-05 20:59:24 +02:00
if ( MScore : : debugMode )
qDebug ( " Registered as JACK Timebase Master. " ) ;
2014-07-04 19:59:33 +02:00
}
else {
2014-07-30 15:38:54 +02:00
preferences . jackTimebaseMaster = false ;
2014-07-04 19:59:33 +02:00
qDebug ( " Unable to take over JACK Timebase, error code: %i " , errCode ) ;
2014-07-03 18:02:33 +02:00
}
}
//---------------------------------------------------------
// releaseTimebaseCallback
//---------------------------------------------------------
void JackAudio : : releaseTimebaseCallback ( )
{
int errCode = jack_release_timebase ( client ) ;
if ( errCode = = 0 )
qDebug ( " Unregistered as JACK Timebase Master " ) ;
else
2014-07-04 19:59:33 +02:00
qDebug ( " Unable to unregister as JACK Timebase Master (not a Timebase Master?), error code: %i " , errCode ) ;
}
2014-07-05 20:59:24 +02:00
//---------------------------------------------------------
// rememberAudioConnections
//---------------------------------------------------------
void JackAudio : : rememberAudioConnections ( )
{
if ( ! preferences . rememberLastConnections )
return ;
if ( MScore : : debugMode )
qDebug ( " Saving audio connections... " ) ;
QSettings settings ;
2014-07-29 16:31:44 +02:00
settings . setValue ( QString ( " audio-0-connections " ) , 0 ) ;
settings . setValue ( QString ( " audio-1-connections " ) , 0 ) ;
2014-07-05 20:59:24 +02:00
int port = 0 ;
foreach ( jack_port_t * mp , ports ) {
const char * * cc = jack_port_get_connections ( mp ) ;
const char * * c = cc ;
int idx = 0 ;
while ( c ) {
const char * p = * c + + ;
if ( p = = 0 )
break ;
settings . setValue ( QString ( " audio-%1-%2 " ) . arg ( port ) . arg ( idx ) , p ) ;
+ + idx ;
}
settings . setValue ( QString ( " audio-%1-connections " ) . arg ( port ) , idx ) ;
free ( ( void * ) cc ) ;
+ + port ;
}
}
2014-07-04 19:59:33 +02:00
//---------------------------------------------------------
// restoreAudioConnections
// Connect to the ports in Preferences->I/O
//---------------------------------------------------------
void JackAudio : : restoreAudioConnections ( )
{
2014-07-05 20:59:24 +02:00
foreach ( jack_port_t * p , ports )
jack_port_disconnect ( client , p ) ;
2014-07-04 19:59:33 +02:00
2014-07-05 20:59:24 +02:00
QList < QString > portList = inputPorts ( ) ;
QList < QString > : : iterator pi = portList . begin ( ) ;
2014-07-04 19:59:33 +02:00
2014-07-05 20:59:24 +02:00
QSettings settings ;
// Number of saved ports
int n = settings . value ( QString ( " audio-0-connections " ) , 0 ) . toInt ( ) + settings . value ( QString ( " audio-1-connections " ) , 0 ) . toInt ( ) ;
// Connecting to system ports
if ( ! preferences . rememberLastConnections | | n = = 0 ) {
if ( MScore : : debugMode )
qDebug ( " Connecting to system ports... " ) ;
for ( int i = 0 ; i < ports . size ( ) ; i + + ) {
const char * src = jack_port_name ( ports [ i ] ) ;
if ( pi ! = portList . end ( ) ) {
connect ( src , qPrintable ( * pi ) ) ;
+ + pi ;
}
}
return ;
}
if ( MScore : : debugMode )
qDebug ( " Restoring audio connections... " ) ;
// Connecting to saved ports
int nPorts = ports . size ( ) ;
for ( int i = 0 ; i < nPorts ; + + i ) {
int n = settings . value ( QString ( " audio-%1-connections " ) . arg ( i ) , 0 ) . toInt ( ) ;
const char * src = jack_port_name ( ports [ i ] ) ;
for ( int k = 0 ; k < n ; + + k ) {
QString dst = settings . value ( QString ( " audio-%1-%2 " ) . arg ( i ) . arg ( k ) , " " ) . toString ( ) ;
if ( ! dst . isEmpty ( ) ) {
if ( jack_port_connected_to ( ports [ i ] , qPrintable ( dst ) ) )
qDebug ( ) < < " Audio port " < < src < < " ( " < < i < < " ) already connected to " < < qPrintable ( dst ) ;
else
connect ( src , qPrintable ( dst ) ) ;
}
}
2014-07-04 19:59:33 +02:00
}
}
//---------------------------------------------------------
// rememberMidiConnections
//---------------------------------------------------------
void JackAudio : : rememberMidiConnections ( )
{
2014-07-05 20:59:24 +02:00
if ( ! preferences . rememberLastConnections )
return ;
2014-07-04 19:59:33 +02:00
if ( MScore : : debugMode )
qDebug ( " Saving midi connections... " ) ;
QSettings settings ;
int port = 0 ;
foreach ( jack_port_t * mp , midiOutputPorts ) {
const char * * cc = jack_port_get_connections ( mp ) ;
const char * * c = cc ;
int idx = 0 ;
while ( c ) {
const char * p = * c + + ;
if ( p = = 0 )
break ;
settings . setValue ( QString ( " midi-%1-%2 " ) . arg ( port ) . arg ( idx ) , p ) ;
+ + idx ;
}
settings . setValue ( QString ( " midi-%1-connections " ) . arg ( port ) , idx ) ;
free ( ( void * ) cc ) ;
+ + port ;
}
2014-07-05 20:59:24 +02:00
2014-07-04 19:59:33 +02:00
port = 0 ;
foreach ( jack_port_t * mp , midiInputPorts ) {
const char * * cc = jack_port_get_connections ( mp ) ;
const char * * c = cc ;
int idx = 0 ;
while ( c ) {
const char * p = * c + + ;
if ( p = = 0 )
break ;
settings . setValue ( QString ( " midiin-%1-%2 " ) . arg ( idx ) . arg ( port ) , p ) ;
+ + idx ;
}
settings . setValue ( QString ( " midiin-%1-connections " ) . arg ( port ) , idx ) ;
free ( ( void * ) cc ) ;
+ + port ;
}
2014-07-03 18:02:33 +02:00
}
//---------------------------------------------------------
2014-07-04 19:59:33 +02:00
// restoreMidiConnections
// Connects to the ports from previous connection
2014-07-03 18:02:33 +02:00
//---------------------------------------------------------
2014-07-04 19:59:33 +02:00
void JackAudio : : restoreMidiConnections ( )
2014-07-03 18:02:33 +02:00
{
2014-07-05 20:59:24 +02:00
if ( ! preferences . rememberLastConnections )
return ;
if ( MScore : : debugMode )
qDebug ( " Restoring midi connections... " ) ;
2014-07-04 19:59:33 +02:00
QSettings settings ;
int nPorts = midiOutputPorts . size ( ) ;
for ( int i = 0 ; i < nPorts ; + + i ) {
int n = settings . value ( QString ( " midi-%1-connections " ) . arg ( i ) , 0 ) . toInt ( ) ;
const char * src = jack_port_name ( midiOutputPorts [ i ] ) ;
for ( int k = 0 ; k < n ; + + k ) {
QString dst = settings . value ( QString ( " midi-%1-%2 " ) . arg ( i ) . arg ( k ) , " " ) . toString ( ) ;
if ( ! dst . isEmpty ( ) ) {
if ( jack_port_connected_to ( midiOutputPorts [ i ] , qPrintable ( dst ) ) )
continue ;
2014-07-05 20:59:24 +02:00
connect ( src , qPrintable ( dst ) ) ;
2014-07-04 19:59:33 +02:00
}
}
}
nPorts = midiInputPorts . size ( ) ;
for ( int i = 0 ; i < nPorts ; + + i ) {
int n = settings . value ( QString ( " midiin-%1-connections " ) . arg ( i ) , 0 ) . toInt ( ) ;
const char * dst = jack_port_name ( midiInputPorts [ i ] ) ;
for ( int k = 0 ; k < n ; + + k ) {
QString src = settings . value ( QString ( " midiin-%1-%2 " ) . arg ( k ) . arg ( i ) , " " ) . toString ( ) ;
if ( ! src . isEmpty ( ) ) {
if ( jack_port_connected_to ( midiInputPorts [ i ] , qPrintable ( src ) ) )
continue ;
2014-07-05 20:59:24 +02:00
connect ( qPrintable ( src ) , dst ) ;
2014-07-04 19:59:33 +02:00
}
}
}
}
//---------------------------------------------------------
// hotPlug
// Change driver settings without unload
//---------------------------------------------------------
void JackAudio : : hotPlug ( )
{
2014-07-05 20:59:24 +02:00
bool oldremember = preferences . rememberLastConnections ;
preferences . rememberLastConnections = true ;
2014-07-04 19:59:33 +02:00
// Remember connections before calling jack_deactivate() - it disconnects all ports
2014-07-30 15:38:54 +02:00
rememberMidiConnections ( ) ;
if ( ports . size ( ) ! = 0 )
2014-07-05 20:59:24 +02:00
rememberAudioConnections ( ) ;
2014-07-04 19:59:33 +02:00
// We must set callbacks only on inactive client
if ( jack_deactivate ( client ) )
qDebug ( " cannot deactivate client " ) ;
// Audio connections
if ( preferences . useJackAudio ) {
if ( ports . size ( ) = = 0 ) {
registerPort ( " left " , false , false ) ;
registerPort ( " right " , false , false ) ;
}
}
else if ( ! preferences . useJackAudio ) {
foreach ( jack_port_t * p , ports ) {
unregisterPort ( p ) ;
ports . removeOne ( p ) ;
}
}
// Midi connections
if ( preferences . useJackMidi ) {
if ( midiOutputPorts . size ( ) < preferences . midiPorts ) {
2014-07-30 15:38:54 +02:00
for ( int i = midiOutputPorts . size ( ) ; i < preferences . midiPorts ; + + i )
2014-07-04 19:59:33 +02:00
registerPort ( QString ( " mscore-midi-%1 " ) . arg ( i + 1 ) , false , true ) ;
}
else if ( midiOutputPorts . size ( ) > preferences . midiPorts ) {
for ( int i = midiOutputPorts . size ( ) - 1 ; i > = preferences . midiPorts ; - - i ) {
unregisterPort ( midiOutputPorts [ i ] ) ;
midiOutputPorts . removeAt ( i ) ;
}
}
if ( midiInputPorts . size ( ) = = 0 )
registerPort ( QString ( " mscore-midiin-1 " ) , true , true ) ;
}
else { // No midi
foreach ( jack_port_t * mp , midiOutputPorts ) {
unregisterPort ( mp ) ;
midiOutputPorts . removeOne ( mp ) ;
}
if ( midiInputPorts . size ( ) ! = 0 ) {
unregisterPort ( midiInputPorts [ 0 ] ) ;
midiInputPorts . removeOne ( midiInputPorts [ 0 ] ) ;
}
}
2014-07-05 20:59:24 +02:00
// Timebase Master callback
2014-07-30 15:38:54 +02:00
if ( preferences . jackTimebaseMaster )
2014-07-05 20:59:24 +02:00
setTimebaseCallback ( ) ;
else
releaseTimebaseCallback ( ) ;
preferences . rememberLastConnections = oldremember ;
2014-07-03 18:02:33 +02:00
}
2014-06-18 21:36:53 +02:00
}