jamulus/linux/sound.cpp

260 lines
8.4 KiB
C++
Raw Normal View History

/******************************************************************************\
* Copyright (c) 2004-2010
*
* Author(s):
* Volker Fischer
*
* This code is based on the simple_client example of the Jack audio interface.
******************************************************************************
*
* 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
*
\******************************************************************************/
#include "sound.h"
#ifdef WITH_SOUND
2009-03-28 10:05:56 +01:00
void CSound::OpenJack()
2009-03-08 09:10:10 +01:00
{
jack_status_t JackStatus;
// try to become a client of the JACK server
pJackClient = jack_client_open ( "llcon", JackNullOption, &JackStatus );
if ( pJackClient == NULL )
{
throw CGenErr ( tr ( "Jack server not running" ) );
2009-03-08 09:10:10 +01:00
}
2009-08-28 09:39:22 +02:00
2009-03-08 09:10:10 +01:00
// tell the JACK server to call "process()" whenever
// there is work to be done
jack_set_process_callback ( pJackClient, process, this );
// register a "buffer size changed" callback function
jack_set_buffer_size_callback ( pJackClient, bufferSizeCallback, this );
2009-03-12 12:56:15 +01:00
// register shutdown callback function
jack_on_shutdown ( pJackClient, shutdownCallback, this );
// TEST check sample rate, if not correct, just fire error
2009-07-26 09:29:32 +02:00
if ( jack_get_sample_rate ( pJackClient ) != SYSTEM_SAMPLE_RATE )
{
throw CGenErr ( tr ( "Jack server sample rate is different from "
"required one" ) );
2009-03-08 09:10:10 +01:00
}
// create four ports (two for input, two for output -> stereo)
input_port_left = jack_port_register ( pJackClient, "input left",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );
2009-03-10 19:39:29 +01:00
input_port_right = jack_port_register ( pJackClient, "input right",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );
2009-03-08 09:10:10 +01:00
output_port_left = jack_port_register ( pJackClient, "output left",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
2009-03-10 19:39:29 +01:00
output_port_right = jack_port_register ( pJackClient, "output right",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
2009-03-08 09:10:10 +01:00
2009-08-28 09:39:22 +02:00
const char** ports;
// tell the JACK server that we are ready to roll
if ( jack_activate ( pJackClient ) )
{
throw CGenErr ( tr ( "Cannot activate client" ) );
}
2009-03-08 09:10:10 +01:00
// connect the ports, note: you cannot do this before
// the client is activated, because we cannot allow
// connections to be made to clients that are not
// running
if ( ( ports = jack_get_ports ( pJackClient, NULL, NULL,
JackPortIsPhysical | JackPortIsOutput ) ) == NULL )
{
throw CGenErr ( tr ( "Cannot find any physical capture ports" ) );
}
if ( !ports[1] )
{
throw CGenErr ( tr ( "Cannot find enough physical capture ports" ) );
}
if ( jack_connect ( pJackClient, ports[0], jack_port_name ( input_port_left ) ) )
{
throw CGenErr ( tr ( "Cannot connect input ports" ) );
}
if ( jack_connect ( pJackClient, ports[1], jack_port_name ( input_port_right ) ) )
{
throw CGenErr ( tr ( "Cannot connect input ports" ) );
}
free ( ports );
if ( ( ports = jack_get_ports ( pJackClient, NULL, NULL,
JackPortIsPhysical | JackPortIsInput ) ) == NULL )
{
throw CGenErr ( tr ( "Cannot find any physical playback ports" ) );
}
if ( !ports[1] )
{
throw CGenErr ( tr ( "Cannot find enough physical playback ports" ) );
}
if ( jack_connect ( pJackClient, jack_port_name ( output_port_left ), ports[0] ) )
{
throw CGenErr ( tr ( "Cannot connect output ports" ) );
}
if ( jack_connect ( pJackClient, jack_port_name ( output_port_right ), ports[1] ) )
{
throw CGenErr ( tr ( "Cannot connect output ports" ) );
}
free ( ports );
2009-03-08 09:10:10 +01:00
}
2009-08-28 09:39:22 +02:00
void CSound::CloseJack()
2009-03-08 09:10:10 +01:00
{
// deactivate client
jack_deactivate ( pJackClient );
2009-03-08 09:10:10 +01:00
2009-03-28 10:05:56 +01:00
// unregister ports
jack_port_unregister ( pJackClient, input_port_left );
jack_port_unregister ( pJackClient, input_port_right );
jack_port_unregister ( pJackClient, output_port_left );
jack_port_unregister ( pJackClient, output_port_right );
2009-08-28 09:39:22 +02:00
// close client connection to jack server
jack_client_close ( pJackClient );
}
void CSound::Start()
{
// call base class
CSoundBase::Start();
}
void CSound::Stop()
{
// call base class
CSoundBase::Stop();
2009-03-08 09:10:10 +01:00
}
int CSound::Init ( const int iNewPrefMonoBufferSize )
2009-03-08 09:10:10 +01:00
{
2009-03-10 19:39:29 +01:00
// try setting buffer size
// TODO seems not to work! -> no audio after this operation!
//jack_set_buffer_size ( pJackClient, iNewPrefMonoBufferSize );
2009-03-08 09:10:10 +01:00
2009-03-09 19:06:27 +01:00
// get actual buffer size
iJACKBufferSizeMono = jack_get_buffer_size ( pJackClient );
2009-03-08 09:10:10 +01:00
2010-03-05 22:18:11 +01:00
// init base class
CSoundBase::Init ( iJACKBufferSizeMono );
// set internal buffer size value and calculate stereo buffer size
iJACKBufferSizeStero = 2 * iJACKBufferSizeMono;
// create memory for intermediate audio buffer
vecsTmpAudioSndCrdStereo.Init ( iJACKBufferSizeStero );
return iJACKBufferSizeMono;
2009-03-08 09:10:10 +01:00
}
2009-03-08 09:10:10 +01:00
// JACK callbacks --------------------------------------------------------------
int CSound::process ( jack_nframes_t nframes, void* arg )
{
CSound* pSound = reinterpret_cast<CSound*> ( arg );
2009-08-28 09:39:22 +02:00
int i;
2009-03-08 09:10:10 +01:00
2009-08-28 09:39:22 +02:00
if ( pSound->IsRunning() )
{
2009-08-28 09:39:22 +02:00
// get input data pointer
jack_default_audio_sample_t* in_left =
(jack_default_audio_sample_t*) jack_port_get_buffer (
pSound->input_port_left, nframes );
jack_default_audio_sample_t* in_right =
(jack_default_audio_sample_t*) jack_port_get_buffer (
pSound->input_port_right, nframes );
// copy input data
for ( i = 0; i < pSound->iJACKBufferSizeMono; i++ )
{
pSound->vecsTmpAudioSndCrdStereo[2 * i] = (short) ( in_left[i] * _MAXSHORT );
pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] = (short) ( in_right[i] * _MAXSHORT );
}
// call processing callback function
pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo );
// get output data pointer
jack_default_audio_sample_t* out_left =
(jack_default_audio_sample_t*) jack_port_get_buffer (
pSound->output_port_left, nframes );
jack_default_audio_sample_t* out_right =
(jack_default_audio_sample_t*) jack_port_get_buffer (
pSound->output_port_right, nframes );
// copy output data
for ( i = 0; i < pSound->iJACKBufferSizeMono; i++ )
{
out_left[i] = (jack_default_audio_sample_t)
pSound->vecsTmpAudioSndCrdStereo[2 * i] / _MAXSHORT;
out_right[i] = (jack_default_audio_sample_t)
pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] / _MAXSHORT;
}
}
2009-08-28 09:39:22 +02:00
else
{
2009-08-28 09:39:22 +02:00
// get output data pointer
jack_default_audio_sample_t* out_left =
(jack_default_audio_sample_t*) jack_port_get_buffer (
pSound->output_port_left, nframes );
jack_default_audio_sample_t* out_right =
(jack_default_audio_sample_t*) jack_port_get_buffer (
pSound->output_port_right, nframes );
// clear output data
for ( i = 0; i < pSound->iJACKBufferSizeMono; i++ )
{
out_left[i] = 0;
out_right[i] = 0;
}
}
return 0; // zero on success, non-zero on error
}
2010-04-11 09:55:06 +02:00
int CSound::bufferSizeCallback ( jack_nframes_t, void *arg )
{
CSound* pSound = reinterpret_cast<CSound*> ( arg );
2009-03-12 12:56:15 +01:00
pSound->EmitReinitRequestSignal();
return 0; // zero on success, non-zero on error
}
2009-03-12 12:56:15 +01:00
2010-04-11 09:55:06 +02:00
void CSound::shutdownCallback ( void* )
2009-03-12 12:56:15 +01:00
{
// without a Jack server, our software makes no sense to run, throw
// error message
throw CGenErr ( tr ( "Jack server was shut down" ) );
2009-03-12 12:56:15 +01:00
}
2009-03-08 09:10:10 +01:00
#endif // WITH_SOUND