From 26d3ca0d29e87a14943d29fcafc93da753df60cd Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sun, 23 Jun 2013 22:23:57 +0300 Subject: [PATCH] support multiple sound drivers, add sdl --- linux/sndout_oss.c | 12 ++-- linux/sndout_oss.h | 7 ++- sndout.c | 80 ++++++++++++++++++++++++ sndout.h | 43 +++++++++++++ sndout_sdl.c | 151 +++++++++++++++++++++++++++++++++++++++++++++ sndout_sdl.h | 7 +++ 6 files changed, 294 insertions(+), 6 deletions(-) create mode 100644 sndout.c create mode 100644 sndout.h create mode 100644 sndout_sdl.c create mode 100644 sndout_sdl.h diff --git a/linux/sndout_oss.c b/linux/sndout_oss.c index 0df17b2..c0b775a 100644 --- a/linux/sndout_oss.c +++ b/linux/sndout_oss.c @@ -20,6 +20,8 @@ #include "sndout_oss.h" +int sndout_oss_frag_frames = 1; + static int sounddev = -1, mixerdev = -1; static int can_write_safe; @@ -47,7 +49,7 @@ void sndout_oss_stop(void) sounddev = -1; } -int sndout_oss_start(int rate, int stereo, int frames_in_frag) +int sndout_oss_start(int rate, int stereo) { static int s_oldrate = 0, s_oldstereo = 0; int frag, bsize, bits, ret; @@ -69,10 +71,11 @@ int sndout_oss_start(int rate, int stereo, int frames_in_frag) } } - // try to fit frames_in_frag frames worth of data in fragment + // try to fit sndout_oss_frag_frames (video) frames + // worth of sound data in OSS fragment // ignore mono because it's unlikely to be used and // both GP2X and Wiz mixes mono to stereo anyway. - bsize = (frames_in_frag * rate / 50) * 4; + bsize = (sndout_oss_frag_frames * rate / 50) * 4; for (frag = 0; bsize; bsize >>= 1, frag++) ; @@ -164,8 +167,9 @@ int sndout_oss_can_write(int bytes) return bi.bytes - bi.fragsize >= bytes ? 1 : 0; } -void sndout_oss_sync(void) +void sndout_oss_wait(void) { + // FIXME? ioctl(sounddev, SOUND_PCM_SYNC, 0); } diff --git a/linux/sndout_oss.h b/linux/sndout_oss.h index 0b3fe6c..fde9ff6 100644 --- a/linux/sndout_oss.h +++ b/linux/sndout_oss.h @@ -1,9 +1,12 @@ int sndout_oss_init(void); -int sndout_oss_start(int rate, int stereo, int frames_in_frag); +int sndout_oss_start(int rate, int stereo); void sndout_oss_stop(void); int sndout_oss_write(const void *buff, int len); int sndout_oss_write_nb(const void *buff, int len); int sndout_oss_can_write(int bytes); -void sndout_oss_sync(void); +void sndout_oss_wait(void); void sndout_oss_setvol(int l, int r); void sndout_oss_exit(void); + +/* make oss fragment size to fit this much video frames */ +extern int sndout_oss_frag_frames; diff --git a/sndout.c b/sndout.c new file mode 100644 index 0000000..ba34f3c --- /dev/null +++ b/sndout.c @@ -0,0 +1,80 @@ +/* + * (C) notaz, 2013 + * + * This work is licensed under the terms of any of these licenses + * (at your option): + * - GNU GPL, version 2 or later. + * - GNU LGPL, version 2.1 or later. + * - MAME license. + * See the COPYING file in the top-level directory. + */ + +#include +#include + +#include "linux/sndout_oss.h" +#include "sndout_sdl.h" +#include "sndout.h" + +static int sndout_null_init(void) +{ + return 0; +} + +static void sndout_null_exit(void) +{ +} + +static int sndout_null_start(int rate, int stereo) +{ + return 0; +} + +static void sndout_null_stop(void) +{ +} + +static void sndout_null_wait(void) +{ +} + +static int sndout_null_write_nb(const void *data, int bytes) +{ + return bytes; +} + +#define SNDOUT_DRIVER(name) { \ + #name, \ + sndout_##name##_init, \ + sndout_##name##_exit, \ + sndout_##name##_start, \ + sndout_##name##_stop, \ + sndout_##name##_wait, \ + sndout_##name##_write_nb, \ +} + +static struct sndout_driver sndout_avail[] = +{ +#ifdef HAVE_SDL + SNDOUT_DRIVER(sdl), +#endif +#ifdef HAVE_OSS + SNDOUT_DRIVER(oss), +#endif + SNDOUT_DRIVER(null) +}; + +struct sndout_driver sndout_current; + +void sndout_init(void) +{ + int i; + + for (i = 0; i < sizeof(sndout_avail) / sizeof(sndout_avail[0]); i++) { + if (sndout_avail[i].init() == 0) + break; + } + + memcpy(&sndout_current, &sndout_avail[i], sizeof(sndout_current)); + printf("using %s audio output driver\n", sndout_current.name); +} diff --git a/sndout.h b/sndout.h new file mode 100644 index 0000000..cd416c5 --- /dev/null +++ b/sndout.h @@ -0,0 +1,43 @@ +#ifndef LIBPICOFE_SNDOUT_H +#define LIBPICOFE_SNDOUT_H + +struct sndout_driver { + const char *name; + int (*init)(void); + void (*exit)(void); + int (*start)(int rate, int stereo); + void (*stop)(void); + void (*wait)(void); + int (*write_nb)(const void *data, int bytes); +}; + +extern struct sndout_driver sndout_current; + +void sndout_init(void); + +static inline void sndout_exit(void) +{ + sndout_current.exit(); +} + +static inline int sndout_start(int rate, int stereo) +{ + return sndout_current.start(rate, stereo); +} + +static inline void sndout_stop(void) +{ + sndout_current.stop(); +} + +static inline void sndout_wait(void) +{ + sndout_current.wait(); +} + +static inline int sndout_write_nb(const void *data, int bytes) +{ + return sndout_current.write_nb(data, bytes); +} + +#endif // LIBPICOFE_SNDOUT_H diff --git a/sndout_sdl.c b/sndout_sdl.c new file mode 100644 index 0000000..83b3ea2 --- /dev/null +++ b/sndout_sdl.c @@ -0,0 +1,151 @@ +/* + * (C) notaz, 2013 + * + * This work is licensed under the terms of any of these licenses + * (at your option): + * - GNU GPL, version 2 or later. + * - GNU LGPL, version 2.1 or later. + * - MAME license. + * See the COPYING file in the top-level directory. + */ + +#include +#include "sndout_sdl.h" + +// ~1/3s at 44kHz +// set this to power of 2 +#define BUF_LEN 32768 +#define BUF_MASK (BUF_LEN - 1) + +static short buf[BUF_LEN]; +static int buf_w, buf_r; +static int started; + +static void callback(void *userdata, Uint8 *stream, int len) +{ + int have = (buf_w - buf_r) & BUF_MASK; + int buf_left = BUF_LEN - buf_r; + + len /= 2; + if (have > len) + have = len; + if (have > 0) { + if (have > buf_left) { + memcpy(stream, buf + buf_r, buf_left * 2); + stream += buf_left * 2; + buf_r = 0; + have -= buf_left; + len -= buf_left; + } + memcpy(stream, buf + buf_r, have * 2); + stream += have * 2; + buf_r = (buf_r + have) & BUF_MASK; + len -= have; + } + + if (len > 0) { + // put in some silence.. + memset(stream, 0, len * 2); + } +} + +int sndout_sdl_init(void) +{ + int ret; + + ret = SDL_InitSubSystem(SDL_INIT_AUDIO); + if (ret != 0) + return -1; + + return 0; +} + +int sndout_sdl_start(int rate, int stereo) +{ + SDL_AudioSpec desired; + int samples, shift; + int ret; + + if (started) + sndout_sdl_stop(); + + desired.freq = rate; + desired.format = AUDIO_S16LSB; + desired.channels = stereo ? 2 : 1; + desired.callback = callback; + desired.userdata = NULL; + + samples = rate * 4 * 16 / 1000; + for (shift = 8; (1 << shift) < samples; shift++) + ; + desired.samples = 1 << shift; + + ret = SDL_OpenAudio(&desired, NULL); + if (ret != 0) { + fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError()); + return -1; + } + + buf_w = buf_r = 0; + + SDL_PauseAudio(0); + started = 1; + + return 0; +} + +void sndout_sdl_stop(void) +{ + SDL_PauseAudio(1); + SDL_CloseAudio(); + started = 0; +} + +void sndout_sdl_wait(void) +{ + int left; + + while (1) + { + left = (buf_r - buf_w - 2) & BUF_MASK; + if (left >= BUF_LEN / 2) + break; + + SDL_Delay(4); + } +} + +int sndout_sdl_write_nb(const void *samples, int len) +{ + int maxlen = (buf_r - buf_w - 2) & BUF_MASK; + int buf_left = BUF_LEN - buf_w; + int retval; + + len /= 2; + if (len > maxlen) + // not enough space + len = maxlen; + if (len == 0) + // totally full + return 0; + + retval = len; + + if (len > buf_left) { + memcpy(buf + buf_w, samples, buf_left * 2); + samples = (const short *)samples + buf_left; + len -= buf_left; + buf_w = 0; + } + memcpy(buf + buf_w, samples, len * 2); + buf_w = (buf_w + len) & BUF_MASK; + + return retval; +} + +void sndout_sdl_exit(void) +{ + if (started) + sndout_sdl_stop(); + SDL_QuitSubSystem(SDL_INIT_AUDIO); +} diff --git a/sndout_sdl.h b/sndout_sdl.h new file mode 100644 index 0000000..3310f27 --- /dev/null +++ b/sndout_sdl.h @@ -0,0 +1,7 @@ +int sndout_sdl_init(void); +int sndout_sdl_start(int rate, int stereo); +void sndout_sdl_stop(void); +void sndout_sdl_wait(void); +int sndout_sdl_write_nb(const void *buff, int len); +void sndout_sdl_exit(void); + -- 2.39.2