diff --git a/include/audio_jack.h b/include/audio_jack.h index 3fcf54ed1c..92fe57ddd4 100644 --- a/include/audio_jack.h +++ b/include/audio_jack.h @@ -1,7 +1,7 @@ /* * audio_jack.h - support for JACK-transport * - * Copyright (c) 2005-2008 Tobias Doerffel + * Copyright (c) 2005-2009 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -29,9 +29,8 @@ #include "lmmsconfig.h" #ifdef LMMS_HAVE_JACK - #include - +#endif #include #include @@ -45,16 +44,17 @@ class QLineEdit; class lcdSpinBox; -class audioJACK : public audioDevice +class audioJACK : public QObject, public audioDevice { + Q_OBJECT public: audioJACK( bool & _success_ful, mixer * _mixer ); virtual ~audioJACK(); inline static QString name( void ) { - return( QT_TRANSLATE_NOOP( "setupWidget", - "JACK (JACK Audio Connection Kit)" ) ); + return QT_TRANSLATE_NOOP( "setupWidget", + "JACK (JACK Audio Connection Kit)" ); } @@ -73,7 +73,14 @@ public: } ; +private slots: + void restartAfterZombified( void ); + + +#ifdef LMMS_HAVE_JACK private: + bool initJackClient( void ); + virtual void startProcessing( void ); virtual void stopProcessing( void ); virtual void applyQualitySettings( void ); @@ -82,7 +89,10 @@ private: virtual void unregisterPort( audioPort * _port ); virtual void renamePort( audioPort * _port ); - static int processCallback( jack_nframes_t _nframes, void * _udata ); + int processCallback( jack_nframes_t _nframes, void * _udata ); + + static int staticProcessCallback( jack_nframes_t _nframes, + void * _udata ); static void shutdownCallback( void * _udata ); @@ -91,7 +101,7 @@ private: bool m_active; bool m_stopped; - QSemaphore m_stop_semaphore; + QSemaphore m_stopSemaphore; QVector m_outputPorts; surroundSampleFrame * m_outBuf; @@ -108,9 +118,11 @@ private: typedef QMap jackPortMap; jackPortMap m_portMap; +#endif + +signals: + void zombified( void ); } ; #endif - -#endif diff --git a/src/core/audio/audio_jack.cpp b/src/core/audio/audio_jack.cpp index 8f7b643072..d5f9681fb9 100644 --- a/src/core/audio/audio_jack.cpp +++ b/src/core/audio/audio_jack.cpp @@ -1,9 +1,7 @@ -#ifndef SINGLE_SOURCE_COMPILE - /* * audio_jack.cpp - support for JACK-transport * - * Copyright (c) 2005-2008 Tobias Doerffel + * Copyright (c) 2005-2009 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -45,6 +43,7 @@ #include "config_mgr.h" #include "lcd_spinbox.h" #include "audio_port.h" +#include "main_window.h" @@ -55,87 +54,21 @@ audioJACK::audioJACK( bool & _success_ful, mixer * _mixer ) : DEFAULT_CHANNELS, SURROUND_CHANNELS ), _mixer ), m_client( NULL ), - m_active( FALSE ), - m_stop_semaphore( 1 ), + m_active( false ), + m_stopSemaphore( 1 ), m_outBuf( new surroundSampleFrame[getMixer()->framesPerPeriod()] ), m_framesDoneInCurBuf( 0 ), m_framesToDoInCurBuf( 0 ) { - _success_ful = FALSE; - - QString client_name = configManager::inst()->value( "audiojack", - "clientname" ); - if( client_name == "" ) + _success_ful = initJackClient(); + if( _success_ful ) { - client_name = "lmms"; + m_stopSemaphore.acquire(); + + connect( this, SIGNAL( zombified() ), + this, SLOT( restartAfterZombified() ), + Qt::QueuedConnection ); } - -#ifndef OLD_JACK - const char * server_name = NULL; - jack_status_t status; - m_client = jack_client_open( client_name.toAscii().constData(), - JackNullOption, &status, - server_name ); - if( m_client == NULL ) - { - printf( "jack_client_open() failed, status 0x%2.0x\n", status ); - if( status & JackServerFailed ) - { - printf( "Could not connect to JACK server.\n" ); - } - return; - } - if( status & JackNameNotUnique ) - { - printf( "there's already a client with name '%s', so unique " - "name '%s' was assigned\n", client_name. - toAscii().constData(), - jack_get_client_name( m_client ) ); - } - -#else /* OLD_JACK */ - - m_client = jack_client_new( client_name.toAscii().constData() ); - if( m_client == NULL ) - { - printf( "jack_client_new() failed\n" ); - return; - } - -#endif - - // set process-callback - jack_set_process_callback( m_client, processCallback, this ); - - // set shutdown-callback - jack_on_shutdown( m_client, shutdownCallback, this ); - - - - if( jack_get_sample_rate( m_client ) != sampleRate() ) - { - setSampleRate( jack_get_sample_rate( m_client ) ); - } - - for( Uint8 ch = 0; ch < channels(); ++ch ) - { - QString name = QString( "master out " ) + - ( ( ch % 2 ) ? "R" : "L" ) + - QString::number( ch / 2 + 1 ); - m_outputPorts.push_back( jack_port_register( m_client, - name.toAscii().constData(), - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsOutput, 0 ) ); - if( m_outputPorts.back() == NULL ) - { - printf( "no more JACK-ports available!\n" ); - return; - } - } - - m_stop_semaphore.acquire(); - - _success_ful = TRUE; } @@ -143,7 +76,7 @@ audioJACK::audioJACK( bool & _success_ful, mixer * _mixer ) : audioJACK::~audioJACK() { - m_stop_semaphore.release(); + m_stopSemaphore.release(); while( m_portMap.size() ) { @@ -160,7 +93,101 @@ audioJACK::~audioJACK() } delete[] m_outBuf; +} + + + +void audioJACK::restartAfterZombified( void ) +{ + if( initJackClient() ) + { + m_active = false; + startProcessing(); + QMessageBox::information( engine::getMainWindow(), + tr( "JACK client restarted" ), + tr( "LMMS was kicked by JACK for some reason. " + "Therefore the JACK backend of LMMS has been " + "restarted. You will have to make manual " + "connections again." ) ); + } + else + { + QMessageBox::information( engine::getMainWindow(), + tr( "JACK server down" ), + tr( "The JACK server seems to have been shutdown " + "and starting a new instance failed. " + "Therefore LMMS is unable to proceed. " + "You should save your project and restart " + "JACK and LMMS." ) ); + } +} + + + + + +bool audioJACK::initJackClient( void ) +{ + QString clientName = configManager::inst()->value( "audiojack", + "clientname" ); + if( clientName.isEmpty() ) + { + clientName = "lmms"; + } + + const char * serverName = NULL; + jack_status_t status; + m_client = jack_client_open( clientName.toAscii().constData(), + JackNullOption, &status, + serverName ); + if( m_client == NULL ) + { + printf( "jack_client_open() failed, status 0x%2.0x\n", status ); + if( status & JackServerFailed ) + { + printf( "Could not connect to JACK server.\n" ); + } + return false; + } + if( status & JackNameNotUnique ) + { + printf( "there's already a client with name '%s', so unique " + "name '%s' was assigned\n", clientName. + toAscii().constData(), + jack_get_client_name( m_client ) ); + } + + // set process-callback + jack_set_process_callback( m_client, staticProcessCallback, this ); + + // set shutdown-callback + jack_on_shutdown( m_client, shutdownCallback, this ); + + + + if( jack_get_sample_rate( m_client ) != sampleRate() ) + { + setSampleRate( jack_get_sample_rate( m_client ) ); + } + + for( ch_cnt_t ch = 0; ch < channels(); ++ch ) + { + QString name = QString( "master out " ) + + ( ( ch % 2 ) ? "R" : "L" ) + + QString::number( ch / 2 + 1 ); + m_outputPorts.push_back( jack_port_register( m_client, + name.toAscii().constData(), + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0 ) ); + if( m_outputPorts.back() == NULL ) + { + printf( "no more JACK-ports available!\n" ); + return false; + } + } + + return true; } @@ -168,9 +195,9 @@ audioJACK::~audioJACK() void audioJACK::startProcessing( void ) { - m_stopped = FALSE; + m_stopped = false; - if( m_active ) + if( m_active || m_client == NULL ) { return; } @@ -181,7 +208,7 @@ void audioJACK::startProcessing( void ) return; } - m_active = TRUE; + m_active = true; // make sure, JACK transport is rolling @@ -192,7 +219,7 @@ void audioJACK::startProcessing( void ) // try to sync JACK's and LMMS's buffer-size - jack_set_buffer_size( m_client, getMixer()->framesPerPeriod() ); +// jack_set_buffer_size( m_client, getMixer()->framesPerPeriod() ); @@ -206,7 +233,7 @@ void audioJACK::startProcessing( void ) } else { - for( Uint8 ch = 0; ch < channels(); ++ch ) + for( ch_cnt_t ch = 0; ch < channels(); ++ch ) { if( jack_connect( m_client, jack_port_name( m_outputPorts[ch] ), @@ -227,7 +254,7 @@ void audioJACK::startProcessing( void ) void audioJACK::stopProcessing( void ) { - m_stop_semaphore.acquire(); + m_stopSemaphore.acquire(); } @@ -259,7 +286,7 @@ void audioJACK::registerPort( audioPort * _port ) const QString name[2] = { _port->name() + " L", _port->name() + " R" } ; - for( Uint8 ch = 0; ch < DEFAULT_CHANNELS; ++ch ) + for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch ) { m_portMap[_port].ports[ch] = jack_port_register( m_client, name[ch].toAscii().constData(), @@ -277,7 +304,7 @@ void audioJACK::unregisterPort( audioPort * _port ) #ifdef AUDIO_PORT_SUPPORT if( m_portMap.contains( _port ) ) { - for( Uint8 ch = 0; ch < DEFAULT_CHANNELS; ++ch ) + for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch ) { if( m_portMap[_port].ports[ch] != NULL ) { @@ -300,7 +327,7 @@ void audioJACK::renamePort( audioPort * _port ) { const QString name[2] = { _port->name() + " L", _port->name() + " R" }; - for( Uint8 ch = 0; ch < DEFAULT_CHANNELS; ++ch ) + for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch ) { jack_port_set_name( m_portMap[_port].ports[ch], name[ch].toAscii().constData() ); @@ -314,33 +341,24 @@ void audioJACK::renamePort( audioPort * _port ) int audioJACK::processCallback( jack_nframes_t _nframes, void * _udata ) { - audioJACK * _this = static_cast( _udata ); + jack_transport_state_t ts = jack_transport_query( m_client, NULL ); -/* printf( "%f\n", jack_cpu_load( _this->m_client ) );*/ - -#ifdef LMMS_DEBUG - assert( _this != NULL ); -#endif - jack_transport_state_t ts = jack_transport_query( _this->m_client, - NULL ); - - QVector outbufs( _this->channels(), - NULL ); - Uint8 chnl = 0; + QVector outbufs( channels(), NULL ); + ch_cnt_t chnl = 0; for( QVector::iterator it = outbufs.begin(); it != outbufs.end(); ++it, ++chnl ) { *it = (jack_default_audio_sample_t *) jack_port_get_buffer( - _this->m_outputPorts[chnl], _nframes ); + m_outputPorts[chnl], _nframes ); } #ifdef AUDIO_PORT_SUPPORT const Uint32 frames = qMin( _nframes, - _this->getMixer()->framesPerPeriod() ); - for( jackPortMap::iterator it = _this->m_portMap.begin(); - it != _this->m_portMap.end(); ++it ) + getMixer()->framesPerPeriod() ); + for( jackPortMap::iterator it = m_portMap.begin(); + it != m_portMap.end(); ++it ) { - for( Uint8 ch = 0; ch < _this->channels(); ++ch ) + for( ch_cnt_t ch = 0; ch < channels(); ++ch ) { if( it.data().ports[ch] == NULL ) { @@ -359,50 +377,60 @@ int audioJACK::processCallback( jack_nframes_t _nframes, void * _udata ) #endif jack_nframes_t done = 0; - while( done < _nframes && _this->m_stopped == FALSE ) + while( done < _nframes && m_stopped == false ) { jack_nframes_t todo = qMin( _nframes, - _this->m_framesToDoInCurBuf - - _this->m_framesDoneInCurBuf ); + m_framesToDoInCurBuf - + m_framesDoneInCurBuf ); if( ts == JackTransportRolling ) { - for( Uint8 chnl = 0; chnl < _this->channels(); ++chnl ) + const float gain = getMixer()->masterGain(); + for( ch_cnt_t chnl = 0; chnl < channels(); ++chnl ) { + jack_default_audio_sample_t * o = outbufs[chnl]; for( jack_nframes_t frame = 0; frame < todo; ++frame ) { - outbufs[chnl][done+frame] = - _this->m_outBuf[_this->m_framesDoneInCurBuf+frame][chnl] * - _this->getMixer()->masterGain(); + o[done+frame] = + m_outBuf[m_framesDoneInCurBuf+ + frame][chnl] * gain; } } } done += todo; - _this->m_framesDoneInCurBuf += todo; - if( _this->m_framesDoneInCurBuf == _this->m_framesToDoInCurBuf ) + m_framesDoneInCurBuf += todo; + if( m_framesDoneInCurBuf == m_framesToDoInCurBuf ) { - _this->m_framesToDoInCurBuf = _this->getNextBuffer( - _this->m_outBuf ); - if( !_this->m_framesToDoInCurBuf ) + m_framesToDoInCurBuf = getNextBuffer( m_outBuf ); + if( !m_framesToDoInCurBuf ) { - _this->m_stopped = TRUE; - _this->m_stop_semaphore.release(); + m_stopped = true; + m_stopSemaphore.release(); } - _this->m_framesDoneInCurBuf = 0; + m_framesDoneInCurBuf = 0; } } - if( ts != JackTransportRolling || _this->m_stopped == TRUE ) + if( ts != JackTransportRolling || m_stopped == true ) { - for( Uint8 ch = 0; ch < _this->channels(); ++ch ) + for( ch_cnt_t ch = 0; ch < channels(); ++ch ) { jack_default_audio_sample_t * b = outbufs[ch] + done; memset( b, 0, sizeof( *b ) * ( _nframes - done ) ); } } - return( 0 ); + return 0; +} + + + + +int audioJACK::staticProcessCallback( jack_nframes_t _nframes, void * _udata ) +{ + return static_cast( _udata )-> + processCallback( _nframes, _udata ); } @@ -412,13 +440,7 @@ void audioJACK::shutdownCallback( void * _udata ) { audioJACK * _this = static_cast( _udata ); _this->m_client = NULL; -/* QMessageBox::information( 0, setupWidget::tr( "JACK-server down" ), - setupWidget::tr( "You seem to have " - "shutdown JACK-server, so " - "LMMS is unable to proceed. " - "You should save your project " - "and restart LMMS!" ), - QMessageBox::Ok );*/ + _this->zombified(); } @@ -472,6 +494,8 @@ void audioJACK::setupWidget::saveSettings( void ) } -#endif + +#include "moc_audio_jack.cxx" #endif +