Initial commit
This commit is contained in:
commit
f23767ad93
7
README.md
Normal file
7
README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Audacious /dev/audio Output
|
||||
|
||||
Provides direct output to /dev/audio as an alternative to the SDL2
|
||||
output plugin.
|
||||
|
||||
**Please note this is still a heavy work in progress!** There will
|
||||
likely be bugs, and TODO probably needs a TODO.
|
10
TODO
Normal file
10
TODO
Normal file
|
@ -0,0 +1,10 @@
|
|||
* Figure out how to manage get_volume and set_volume
|
||||
* Should it be global? Does (audio_info_t)info.play.gain
|
||||
refer to the global mixer or just the track (i'm guessing
|
||||
from the manpage that it's global?)
|
||||
* Find out if/how audio(4) supports 24-bit linear PCM
|
||||
* Audacious supports two methods-- one is 3-byte, the other
|
||||
is padded 4-byte. I'm guessing audio(4) doesn't support
|
||||
both, but who knows
|
||||
* Clean up the code in terms of formatting/layout
|
||||
* Figure out a license
|
343
src/netbsdout.cc
Normal file
343
src/netbsdout.cc
Normal file
|
@ -0,0 +1,343 @@
|
|||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/audioio.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <libaudcore/audstrings.h>
|
||||
#include <libaudcore/i18n.h>
|
||||
#include <libaudcore/plugin.h>
|
||||
#include <libaudcore/ringbuf.h>
|
||||
#include <libaudcore/runtime.h>
|
||||
|
||||
|
||||
class NetBSDOutput : public OutputPlugin
|
||||
{
|
||||
public:
|
||||
static const char about[];
|
||||
static const char * const defaults[];
|
||||
static constexpr PluginInfo info = {
|
||||
"/dev/audio Output",
|
||||
"netbsdout",
|
||||
about
|
||||
};
|
||||
|
||||
constexpr NetBSDOutput () : OutputPlugin (info, 10) {}
|
||||
|
||||
bool init ();
|
||||
void cleanup ();
|
||||
|
||||
bool open_audio (int format, int rate, int chans, String & error);
|
||||
void close_audio ();
|
||||
|
||||
int write_audio (const void * data, int size);
|
||||
|
||||
void period_wait ();
|
||||
|
||||
void drain ();
|
||||
|
||||
void set_volume(StereoVolume volume);
|
||||
StereoVolume get_volume();
|
||||
|
||||
int get_delay ();
|
||||
|
||||
void pause (bool pause);
|
||||
|
||||
void flush ();
|
||||
};
|
||||
|
||||
__attribute__((visibility("default"))) NetBSDOutput aud_plugin_instance;
|
||||
|
||||
static String audio_path;
|
||||
static int audio_fd;
|
||||
|
||||
static bool audio_paused, audio_prebuffer, audio_flushed;
|
||||
|
||||
static pthread_mutex_t nbout_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t nbout_cond = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
struct fmt_conv {
|
||||
int aud_format;
|
||||
unsigned int encoding;
|
||||
unsigned int bits;
|
||||
};
|
||||
|
||||
const char
|
||||
NetBSDOutput::about[] =
|
||||
"NetBSD Audio Plugin";
|
||||
|
||||
const char * const
|
||||
NetBSDOutput::defaults[] = {
|
||||
"audio_dev", "/dev/audio",
|
||||
nullptr
|
||||
};
|
||||
|
||||
static bool
|
||||
pause_audio (bool pause)
|
||||
{
|
||||
audio_info_t info;
|
||||
|
||||
AUDIO_INITINFO (& info);
|
||||
|
||||
info.play.pause = pause;
|
||||
|
||||
if (ioctl(audio_fd, AUDIO_SETINFO, & info) != 0) {
|
||||
AUDERR ("Unable to set pause: %s.\n", strerror (errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
NetBSDOutput::init ()
|
||||
{
|
||||
aud_config_set_defaults ("netbsd", defaults);
|
||||
audio_path = aud_get_str ("netbsd", "audio_dev");
|
||||
|
||||
audio_fd = open (audio_path, O_WRONLY);
|
||||
|
||||
if (audio_fd == -1) {
|
||||
AUDERR ("Failed to open %s: %s.\n", audio_path, strerror (errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
NetBSDOutput::cleanup ()
|
||||
{
|
||||
close (audio_fd);
|
||||
}
|
||||
|
||||
bool
|
||||
NetBSDOutput::open_audio (int format,
|
||||
int rate,
|
||||
int chans,
|
||||
String & error)
|
||||
{
|
||||
// TODO:
|
||||
audio_info_t info;
|
||||
int props;
|
||||
// Audacious->NetBSD format table
|
||||
static const struct fmt_conv fmt_table[] = {
|
||||
{FMT_S8, AUDIO_ENCODING_SLINEAR, 8},
|
||||
{FMT_U8, AUDIO_ENCODING_ULINEAR, 8},
|
||||
{FMT_S16_LE, AUDIO_ENCODING_SLINEAR_LE, 16},
|
||||
{FMT_S16_BE, AUDIO_ENCODING_SLINEAR_BE, 16},
|
||||
{FMT_U16_LE, AUDIO_ENCODING_ULINEAR_LE, 16},
|
||||
{FMT_U16_BE, AUDIO_ENCODING_ULINEAR_BE, 16},
|
||||
{FMT_S32_LE, AUDIO_ENCODING_SLINEAR_LE, 32},
|
||||
{FMT_S32_BE, AUDIO_ENCODING_SLINEAR_BE, 32},
|
||||
{FMT_U32_LE, AUDIO_ENCODING_ULINEAR_LE, 32},
|
||||
{FMT_U32_BE, AUDIO_ENCODING_ULINEAR_BE, 32}
|
||||
};
|
||||
struct fmt_conv cur_format = { -1, 0, 0 };
|
||||
|
||||
if (ioctl (audio_fd, AUDIO_GETPROPS, & props) == -1) {
|
||||
error = String (str_printf
|
||||
("Failed to get audio properties on %s: %s.",
|
||||
audio_path, strerror (errno)));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (props | AUDIO_PROP_PLAYBACK != AUDIO_PROP_PLAYBACK) {
|
||||
error = String (str_printf
|
||||
("Device %s does not support playback.",
|
||||
audio_path));
|
||||
}
|
||||
|
||||
AUDIO_INITINFO (&info);
|
||||
|
||||
for (auto & conv : fmt_table) {
|
||||
if (conv.aud_format == format) {
|
||||
cur_format = conv;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cur_format.aud_format == -1) {
|
||||
error = String ("Audio format not supported.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
info.play.channels = chans;
|
||||
info.play.encoding = cur_format.encoding;
|
||||
info.play.precision = cur_format.bits;
|
||||
info.play.sample_rate = rate;
|
||||
info.mode = AUMODE_PLAY;
|
||||
|
||||
|
||||
if (ioctl (audio_fd, AUDIO_SETINFO, & info) == -1) {
|
||||
error = String (str_printf
|
||||
("Failed to set track info on %s: %s.\n",
|
||||
audio_path, strerror (errno)));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
NetBSDOutput::close_audio ()
|
||||
{
|
||||
AUDDBG ("close_audio is a stub!\n"); // XXX
|
||||
}
|
||||
|
||||
int
|
||||
NetBSDOutput::write_audio (const void * data,
|
||||
int size)
|
||||
{
|
||||
int res, len;
|
||||
|
||||
if ((res = pthread_mutex_lock (& nbout_mutex)) != 0) {
|
||||
AUDERR ("Couldn't lock mutex: %s.\n", strerror (res));
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = write(audio_fd, data, size);
|
||||
|
||||
pthread_mutex_unlock (& nbout_mutex);
|
||||
return len;
|
||||
}
|
||||
|
||||
void
|
||||
NetBSDOutput::period_wait ()
|
||||
{
|
||||
struct pollfd fds = {
|
||||
audio_fd,
|
||||
POLLOUT,
|
||||
0
|
||||
};
|
||||
int res;
|
||||
|
||||
if ((res = pthread_mutex_lock (& nbout_mutex)) != 0) {
|
||||
AUDERR ("Couldn't lock mutex: %s.\n", strerror (res));
|
||||
return;
|
||||
}
|
||||
|
||||
while ((res = poll(& fds, 1, 0)) == 0) {
|
||||
if (audio_flushed) {
|
||||
AUDDBG ("Audio flushed, exiting period_wait loop...\n");
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_cond_wait (& nbout_cond, & nbout_mutex);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock (& nbout_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
NetBSDOutput::pause (bool pause)
|
||||
{
|
||||
int res, len;
|
||||
audio_info_t info;
|
||||
|
||||
AUDDBG ("Setting pause = %s.\n", pause ? "true" : "false");
|
||||
|
||||
if ((res = pthread_mutex_lock (& nbout_mutex)) != 0) {
|
||||
AUDERR ("Couldn't lock mutex: %s.\n", strerror (res));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pause_audio (pause)) {
|
||||
audio_paused = pause;
|
||||
}
|
||||
|
||||
pthread_cond_broadcast (& nbout_cond);
|
||||
pthread_mutex_unlock (& nbout_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
NetBSDOutput::drain ()
|
||||
{
|
||||
int res;
|
||||
|
||||
AUDDBG ("Draining...\n");
|
||||
|
||||
if ((res = pthread_mutex_lock (& nbout_mutex)) != 0) {
|
||||
AUDERR ("Couldn't lock mutex: %s.\n", strerror (res));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ioctl (audio_fd, AUDIO_DRAIN) != 0) {
|
||||
AUDERR ("Couldn't drain audio: %s\n.", strerror (errno));
|
||||
}
|
||||
|
||||
pthread_mutex_unlock (& nbout_mutex);
|
||||
}
|
||||
|
||||
int
|
||||
NetBSDOutput::get_delay ()
|
||||
{
|
||||
audio_info_t info;
|
||||
unsigned int delay_time;
|
||||
int res;
|
||||
|
||||
if ((res = pthread_mutex_lock (& nbout_mutex)) != 0) {
|
||||
AUDERR ("Couldn't lock mutex: %s.\n", strerror (res));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl (audio_fd, AUDIO_GETBUFINFO, & info) != 0) {
|
||||
AUDERR ("Couldn't get audio info: %s.\n", strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the time remaining by dividing the bytes left to read
|
||||
* by (sample rate * bits of precision).
|
||||
*/
|
||||
if (info.play.sample_rate > 0 && info.play.precision > 0) {
|
||||
delay_time = aud::rdiv (info.play.seek,
|
||||
(info.play.sample_rate * info.play.precision));
|
||||
} else {
|
||||
AUDERR ("Sample rate and precision are both 0\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock (& nbout_mutex);
|
||||
return delay_time;
|
||||
}
|
||||
|
||||
void
|
||||
NetBSDOutput::flush ()
|
||||
{
|
||||
int res;
|
||||
|
||||
AUDDBG ("Flushing!\n");
|
||||
|
||||
if ((res = pthread_mutex_lock (& nbout_mutex)) != 0) {
|
||||
AUDERR ("Couldn't lock mutex: %s.\n", strerror (res));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ioctl (audio_fd, AUDIO_FLUSH) != 0) {
|
||||
AUDERR ("Couldn't flush audio: %s\n.", strerror (errno));
|
||||
}
|
||||
|
||||
audio_flushed = true;
|
||||
|
||||
pthread_cond_broadcast (& nbout_cond);
|
||||
pthread_mutex_unlock (& nbout_mutex);
|
||||
}
|
||||
|
||||
StereoVolume
|
||||
NetBSDOutput::get_volume ()
|
||||
{
|
||||
// too noisy, even for debug...
|
||||
// AUDDBG ("get_volume: Stub!\n");
|
||||
|
||||
return {100, 100}; // XXX
|
||||
}
|
||||
|
||||
void
|
||||
NetBSDOutput::set_volume (StereoVolume volume)
|
||||
{
|
||||
AUDDBG ("set_volume: Stub!\n"); // XXX
|
||||
}
|
Loading…
Reference in a new issue