Add skeletal sound driver for JACK
This doesn't make any sound yet, but it creates the JACK client and connects the requested ports if any.
This commit is contained in:
@@ -265,6 +265,16 @@ if test x$have_pulse = xyes; then
|
|||||||
LIBS="$LIBS -lpulse"
|
LIBS="$LIBS -lpulse"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
dnl Search for JACK unless --without-jack or --with-jack=no is given; require it for --with-jack
|
||||||
|
AC_ARG_WITH([jack], [AS_HELP_STRING([--without-jack], [Disable JACK sound driver])])
|
||||||
|
AS_IF([test "x$with_jack" != "xno"], [
|
||||||
|
AC_SEARCH_LIBS([jack_client_open], [jack], [have_jack=yes], [
|
||||||
|
AS_IF([test "x$with_jack" != x], [
|
||||||
|
AC_MSG_ERROR([--with-jack was specified but JACK was not found])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
|
||||||
AC_MSG_CHECKING(if cstdlib breaks llabs)
|
AC_MSG_CHECKING(if cstdlib breaks llabs)
|
||||||
AC_LANG_PUSH(C++)
|
AC_LANG_PUSH(C++)
|
||||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <stdlib.h>
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <stdlib.h>
|
||||||
@@ -283,6 +293,7 @@ AC_DEFINE(__STDC_FORMAT_MACROS, 1, [Use PRId64 and similar])
|
|||||||
|
|
||||||
AM_CONDITIONAL(HAVE_ALSA, test x$alsa != xfalse )
|
AM_CONDITIONAL(HAVE_ALSA, test x$alsa != xfalse )
|
||||||
AM_CONDITIONAL(HAVE_GTK, test "$enable_gtk2" != "no" )
|
AM_CONDITIONAL(HAVE_GTK, test "$enable_gtk2" != "no" )
|
||||||
|
AM_CONDITIONAL(HAVE_JACK, test x$have_jack = xyes)
|
||||||
AM_CONDITIONAL(HAVE_OSS, test x$ac_cv_header_sys_soundcard_h = xyes )
|
AM_CONDITIONAL(HAVE_OSS, test x$ac_cv_header_sys_soundcard_h = xyes )
|
||||||
AM_CONDITIONAL(HAVE_PULSE, test x$have_pulse = xyes)
|
AM_CONDITIONAL(HAVE_PULSE, test x$have_pulse = xyes)
|
||||||
AM_CONDITIONAL(USE_CRASH_HANDLER, test "$use_crash_handler" = "yes" )
|
AM_CONDITIONAL(USE_CRASH_HANDLER, test "$use_crash_handler" = "yes" )
|
||||||
|
|||||||
@@ -225,6 +225,10 @@ if HAVE_PULSE
|
|||||||
Sound += arch/Sound/RageSoundDriver_PulseAudio.cpp arch/Sound/RageSoundDriver_PulseAudio.h
|
Sound += arch/Sound/RageSoundDriver_PulseAudio.cpp arch/Sound/RageSoundDriver_PulseAudio.h
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if HAVE_JACK
|
||||||
|
Sound += arch/Sound/RageSoundDriver_JACK.cpp arch/Sound/RageSoundDriver_JACK.h
|
||||||
|
endif
|
||||||
|
|
||||||
if HAVE_OSS
|
if HAVE_OSS
|
||||||
Sound += arch/Sound/RageSoundDriver_OSS.cpp arch/Sound/RageSoundDriver_OSS.h
|
Sound += arch/Sound/RageSoundDriver_OSS.cpp arch/Sound/RageSoundDriver_OSS.h
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -0,0 +1,184 @@
|
|||||||
|
#include "global.h"
|
||||||
|
#include "RageSoundDriver_JACK.h"
|
||||||
|
#include "RageLog.h"
|
||||||
|
#include "RageUtil.h"
|
||||||
|
#include "PrefsManager.h"
|
||||||
|
#include "ProductInfo.h"
|
||||||
|
|
||||||
|
REGISTER_SOUND_DRIVER_CLASS( JACK );
|
||||||
|
|
||||||
|
RageSoundDriver_JACK::RageSoundDriver_JACK() :
|
||||||
|
RageSoundDriver()
|
||||||
|
{
|
||||||
|
client = NULL;
|
||||||
|
port_l = NULL;
|
||||||
|
port_r = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
RageSoundDriver_JACK::~RageSoundDriver_JACK()
|
||||||
|
{
|
||||||
|
// Shut down client
|
||||||
|
jack_client_close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
RString RageSoundDriver_JACK::Init()
|
||||||
|
{
|
||||||
|
jack_status_t status;
|
||||||
|
RString error;
|
||||||
|
|
||||||
|
// Open JACK client and call it "StepMania" or whatever
|
||||||
|
client = jack_client_open(PRODUCT_FAMILY, JackNullOption, &status);
|
||||||
|
if (client == NULL)
|
||||||
|
return "Couldn't connect to JACK server";
|
||||||
|
|
||||||
|
LOG->Trace("JACK connected at %u Hz", jack_get_sample_rate(client));
|
||||||
|
|
||||||
|
// Set callback for processing audio
|
||||||
|
if (jack_set_process_callback(client, ProcessTrampoline, this))
|
||||||
|
{
|
||||||
|
error = "Couldn't set JACK process callback";
|
||||||
|
goto out_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Set a jack_on_shutdown callback as well? Probably just stop
|
||||||
|
// caring about sound altogether if that happens.
|
||||||
|
|
||||||
|
// Create output ports
|
||||||
|
port_l = jack_port_register(client, "out_l", JACK_DEFAULT_AUDIO_TYPE,
|
||||||
|
JackPortIsOutput, 0);
|
||||||
|
if (port_l == NULL)
|
||||||
|
{
|
||||||
|
error = "Couldn't create JACK port out_l";
|
||||||
|
goto out_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
port_r = jack_port_register(client, "out_r", JACK_DEFAULT_AUDIO_TYPE,
|
||||||
|
JackPortIsOutput, 0);
|
||||||
|
if (port_r == NULL)
|
||||||
|
{
|
||||||
|
error = "Couldn't create JACK port out_r";
|
||||||
|
goto out_unreg_l;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go!
|
||||||
|
if (jack_activate(client))
|
||||||
|
{
|
||||||
|
error = "Couldn't activate JACK client";
|
||||||
|
goto out_unreg_r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to playback port. This currently requires that you specify
|
||||||
|
// the client using the SoundDevice preference. The alternative is to
|
||||||
|
// connect automatically to something like `playback' if none is
|
||||||
|
// specified, which is not friendly JACK behavior.
|
||||||
|
error = ConnectPorts();
|
||||||
|
if (!error.empty())
|
||||||
|
goto out_deactivate;
|
||||||
|
|
||||||
|
// Success!
|
||||||
|
return RString();
|
||||||
|
|
||||||
|
|
||||||
|
// Not success!
|
||||||
|
out_deactivate:
|
||||||
|
jack_deactivate(client);
|
||||||
|
out_unreg_r:
|
||||||
|
jack_port_unregister(client, port_r);
|
||||||
|
out_unreg_l:
|
||||||
|
jack_port_unregister(client, port_l);
|
||||||
|
out_close:
|
||||||
|
jack_client_close(client);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
RString RageSoundDriver_JACK::ConnectPorts()
|
||||||
|
{
|
||||||
|
vector<RString> portNames;
|
||||||
|
split(PREFSMAN->m_iSoundDevice.Get(), ",", portNames, true);
|
||||||
|
|
||||||
|
switch (portNames.size())
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
// No ports specified
|
||||||
|
return RString();
|
||||||
|
case 1:
|
||||||
|
// HACK: Pointing both channels at one port is probably the
|
||||||
|
// wrong way to mix to mono. For now, it works.
|
||||||
|
portNames.push_back(portNames[0]);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG->Warn("More than two JACK ports specified; using the first two only.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(portNames.size() >= 2);
|
||||||
|
|
||||||
|
// Use jack_port_by_name to ensure ports exist, then jack_port_name to
|
||||||
|
// use their canonical name. (I'm not sure if that second step is
|
||||||
|
// necessary, I've seen something about "aliases" in the docs.)
|
||||||
|
jack_port_t *port_out = jack_port_by_name(client, portNames[0]);
|
||||||
|
if (port_out == NULL || jack_connect(client, jack_port_name(port_l), jack_port_name(port_out)))
|
||||||
|
return "Couldn't connect left JACK port";
|
||||||
|
|
||||||
|
port_out = jack_port_by_name(client, portNames[1]);
|
||||||
|
if (port_out == NULL || jack_connect(client, jack_port_name(port_r), jack_port_name(port_out)))
|
||||||
|
return "Couldn't connect right JACK port";
|
||||||
|
|
||||||
|
return RString();
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t RageSoundDriver_JACK::GetPosition() const
|
||||||
|
{
|
||||||
|
return jack_frame_time(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
int RageSoundDriver_JACK::GetSampleRate() const
|
||||||
|
{
|
||||||
|
return jack_get_sample_rate(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
int RageSoundDriver_JACK::ProcessCallback(jack_nframes_t nframes)
|
||||||
|
{
|
||||||
|
jack_default_audio_sample_t *out_l =
|
||||||
|
(jack_default_audio_sample_t *) jack_port_get_buffer(port_l, nframes);
|
||||||
|
jack_default_audio_sample_t *out_r =
|
||||||
|
(jack_default_audio_sample_t *) jack_port_get_buffer(port_r, nframes);
|
||||||
|
|
||||||
|
memset(out_l, 0, nframes * sizeof(jack_default_audio_sample_t));
|
||||||
|
memset(out_r, 0, nframes * sizeof(jack_default_audio_sample_t));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static callback trampoline
|
||||||
|
int RageSoundDriver_JACK::ProcessTrampoline(jack_nframes_t nframes, void *arg)
|
||||||
|
{
|
||||||
|
return ((RageSoundDriver_JACK *) arg)->ProcessCallback(nframes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (c) 2013 Devin J. Pohly
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, and/or sell copies of the Software, and to permit persons to
|
||||||
|
* whom the Software is furnished to do so, provided that the above
|
||||||
|
* copyright notice(s) and this permission notice appear in all copies of
|
||||||
|
* the Software and that both the above copyright notice(s) and this
|
||||||
|
* permission notice appear in supporting documentation.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||||
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||||
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||||
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||||
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
#ifndef RAGE_SOUND_JACK
|
||||||
|
#define RAGE_SOUND_JACK
|
||||||
|
|
||||||
|
#include "RageSoundDriver.h"
|
||||||
|
#include <jack/jack.h>
|
||||||
|
|
||||||
|
#define USE_RAGE_SOUND_JACK
|
||||||
|
|
||||||
|
class RageSoundDriver_JACK: public RageSoundDriver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RageSoundDriver_JACK();
|
||||||
|
~RageSoundDriver_JACK();
|
||||||
|
|
||||||
|
RString Init();
|
||||||
|
|
||||||
|
int GetSampleRate() const;
|
||||||
|
int64_t GetPosition() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
jack_client_t *client;
|
||||||
|
jack_port_t *port_l;
|
||||||
|
jack_port_t *port_r;
|
||||||
|
|
||||||
|
// Helper for Init()
|
||||||
|
RString ConnectPorts();
|
||||||
|
|
||||||
|
// JACK callbacks and trampolines
|
||||||
|
int ProcessCallback(jack_nframes_t nframes);
|
||||||
|
static int ProcessTrampoline(jack_nframes_t nframes, void *arg);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (c) 2013 Devin J. Pohly
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, and/or sell copies of the Software, and to permit persons to
|
||||||
|
* whom the Software is furnished to do so, provided that the above
|
||||||
|
* copyright notice(s) and this permission notice appear in all copies of
|
||||||
|
* the Software and that both the above copyright notice(s) and this
|
||||||
|
* permission notice appear in supporting documentation.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||||
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||||
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||||
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||||
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
@@ -264,6 +264,9 @@ RageSoundDriver *MakeRageSoundDriver( const RString &drivers )
|
|||||||
#ifdef USE_RAGE_SOUND_DSOUND_SOFTWARE
|
#ifdef USE_RAGE_SOUND_DSOUND_SOFTWARE
|
||||||
if( !DriversToTry[i].CompareNoCase("DirectSound-sw") ) ret = new RageSound_DSound_Software;
|
if( !DriversToTry[i].CompareNoCase("DirectSound-sw") ) ret = new RageSound_DSound_Software;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_RAGE_SOUND_JACK
|
||||||
|
if( !DriversToTry[i].CompareNoCase("JACK") ) ret = new RageSound_JACK;
|
||||||
|
#endif
|
||||||
#ifdef USE_RAGE_SOUND_NULL
|
#ifdef USE_RAGE_SOUND_NULL
|
||||||
if( !DriversToTry[i].CompareNoCase("Null") ) ret = new RageSound_Null;
|
if( !DriversToTry[i].CompareNoCase("Null") ) ret = new RageSound_Null;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user