AudioJack: fixed lockup when getting zombified

This commit fixes lockup of the whole audio engine as soon as LMMS'
JACK backend got zombified due to longer delays (e.g. when audio engine
is blocked while loading a project). Restart JACK backend in such cases
and show an according message to the user.

Furthermore this commit includes various cleanups and improvements.
(cherry picked from commit b4e38e3978)
This commit is contained in:
Tobias Doerffel
2009-04-07 00:07:19 +02:00
parent 99807eeb98
commit 2e1c7ce8a2
2 changed files with 177 additions and 141 deletions

View File

@@ -1,7 +1,7 @@
/*
* audio_jack.h - support for JACK-transport
*
* Copyright (c) 2005-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2005-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
@@ -29,9 +29,8 @@
#include "lmmsconfig.h"
#ifdef LMMS_HAVE_JACK
#include <jack/jack.h>
#endif
#include <QtCore/QVector>
#include <QtCore/QList>
@@ -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<jack_port_t *> m_outputPorts;
surroundSampleFrame * m_outBuf;
@@ -108,9 +118,11 @@ private:
typedef QMap<audioPort *, stereoPort> jackPortMap;
jackPortMap m_portMap;
#endif
signals:
void zombified( void );
} ;
#endif
#endif

View File

@@ -1,9 +1,7 @@
#ifndef SINGLE_SOURCE_COMPILE
/*
* audio_jack.cpp - support for JACK-transport
*
* Copyright (c) 2005-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2005-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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<audioJACK *>( _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<jack_default_audio_sample_t *> outbufs( _this->channels(),
NULL );
Uint8 chnl = 0;
QVector<jack_default_audio_sample_t *> outbufs( channels(), NULL );
ch_cnt_t chnl = 0;
for( QVector<jack_default_audio_sample_t *>::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<Uint32>( _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<jack_nframes_t>(
_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<audioJACK *>( _udata )->
processCallback( _nframes, _udata );
}
@@ -412,13 +440,7 @@ void audioJACK::shutdownCallback( void * _udata )
{
audioJACK * _this = static_cast<audioJACK *>( _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