diff options
| author | Takamichi Horikawa <takamichiho@gmail.com> | 2017-07-12 00:34:35 +0900 | 
|---|---|---|
| committer | Takamichi Horikawa <takamichiho@gmail.com> | 2017-07-12 00:34:35 +0900 | 
| commit | 44c20e41ddd0c394daf5aa45d88baa95334f04fc (patch) | |
| tree | 9d503654c49585ee580c4033e086e1a2275b933e /gtk | |
| parent | f0c957012f65775976be05d2497a6a30c222b7e6 (diff) | |
test soundout
Diffstat (limited to 'gtk')
| -rw-r--r-- | gtk/Makefile.am | 14 | ||||
| -rw-r--r-- | gtk/configure.ac | 4 | ||||
| -rw-r--r-- | gtk/main.c | 67 | ||||
| -rw-r--r-- | gtk/soundout/alsaout.c | 152 | ||||
| -rw-r--r-- | gtk/soundout/alsaout.h | 9 | ||||
| -rw-r--r-- | gtk/soundout/jackout.c | 127 | ||||
| -rw-r--r-- | gtk/soundout/jackout.h | 9 | ||||
| -rw-r--r-- | gtk/soundout/ossout.c | 8 | ||||
| -rw-r--r-- | gtk/soundout/pulseout.c | 133 | ||||
| -rw-r--r-- | gtk/soundout/pulseout.h | 9 | ||||
| -rw-r--r-- | gtk/soundout/soundout.c | 14 | ||||
| -rw-r--r-- | gtk/soundout/soundout.h | 16 | 
12 files changed, 511 insertions, 51 deletions
| diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 8777418..43a767d 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -19,12 +19,17 @@ FMDSP_SRC=../fmdsp/fmdsp.c \            ../fmdsp/font_fmdsp_small.c \            ../fmdsp/fmdsp_platform_unix.c +SOUNDOUT_SRC=soundout/soundout.c \ +             soundout/pulseout.c \ +             soundout/jackout.c \ +             soundout/alsaout.c +  #fmplayer_CFLAGS=$(CFLAGS)  #CFLAGS=  fmplayer_CPPFLAGS=-Wall -Wextra -pedantic \ -                  -I.. \ -                  $(GTK3_CFLAGS) $(PORTAUDIO_CFLAGS) $(SNDFILE_CFLAGS) -fmplayer_LDADD=$(GTK3_LIBS) $(PORTAUDIO_LIBS) $(SNDFILE_LIBS) +                  -I.. -Isoundout \ +                  $(GTK3_CFLAGS) $(JACK_CFLAGS) $(PULSE_CFLAGS) $(ALSA_CFLAGS) $(SNDFILE_CFLAGS) +fmplayer_LDADD=$(GTK3_LIBS) $(JACK_LIBS) $(PULSE_LIBS) $(ALSA_LIBS) $(SNDFILE_LIBS) -lm -lpthread  if ENABLE_NEON  LIBOPNA_SRC+=../libopna/opnassg-sinc-neon.s @@ -52,7 +57,8 @@ fmplayer_SOURCES=main.c \                   ../fft/fft.c \                   $(LIBOPNA_SRC) \                   $(FMDRIVER_SRC) \ -                 $(FMDSP_SRC) +                 $(FMDSP_SRC) \ +                 $(SOUNDOUT_SRC)  if ENABLE_OPENGL  fmplayer_SOURCES+=oscilloview-gl.c diff --git a/gtk/configure.ac b/gtk/configure.ac index cab8553..8a61ac1 100644 --- a/gtk/configure.ac +++ b/gtk/configure.ac @@ -6,7 +6,9 @@ AC_PROG_RANLIB  AM_PROG_AR  AM_PROG_AS -PKG_CHECK_MODULES([PORTAUDIO], [portaudio-2.0]) +PKG_CHECK_MODULES([JACK], [jack soxr]) +PKG_CHECK_MODULES([PULSE], [libpulse]) +PKG_CHECK_MODULES([ALSA], [alsa])  PKG_CHECK_MODULES([GTK3], [gtk+-3.0 cairo])  PKG_CHECK_MODULES([SNDFILE], [sndfile]) @@ -1,5 +1,4 @@  #include <gtk/gtk.h> -#include <portaudio.h>  #include <stdlib.h>  #include <string.h>  #include <stdint.h> @@ -21,6 +20,8 @@  #include "common/fmplayer_common.h"  #include "fft/fft.h" +#include "soundout.h" +  #include "fmplayer.xpm"  #include "fmplayer32.xpm" @@ -29,6 +30,7 @@  enum {    SRATE = 55467, +//  SRATE = 55555,    PPZ8MIX = 0xa000,    AUDIOBUFLEN = 0,  }; @@ -40,9 +42,8 @@ static struct {    GtkWidget *box_widget;    GtkWidget *fmdsp_widget;    GtkWidget *filechooser_widget; -  bool pa_initialized; -  bool pa_paused; -  PaStream *pastream; +  bool sound_paused; +  struct sound_state *ss;    struct opna opna;    struct opna_timer opna_timer;    struct ppz8 ppz8; @@ -68,10 +69,9 @@ static struct {  };  static void quit(void) { -  if (g.pastream) { -    Pa_CloseStream(g.pastream); +  if (g.ss) { +    g.ss->free(g.ss);    } -  if (g.pa_initialized) Pa_Terminate();    fmplayer_file_free(g.fmfile);    gtk_main_quit();  } @@ -118,17 +118,9 @@ static void msgbox_err(const char *msg) {    gtk_widget_destroy(d);  } - -static int pastream_cb(const void *inptr, void *outptr, unsigned long frames, -                       const PaStreamCallbackTimeInfo *timeinfo, -                       PaStreamCallbackFlags statusFlags, -                       void *userdata) { -  (void)inptr; -  (void)timeinfo; -  (void)statusFlags; -  struct opna_timer *timer = (struct opna_timer *)userdata; -  int16_t *buf = (int16_t *)outptr; -  memset(outptr, 0, sizeof(int16_t)*frames*2); +static void soundout_cb(void *userptr, int16_t *buf, unsigned frames) { +  struct opna_timer *timer = (struct opna_timer *)userptr; +  memset(buf, 0, sizeof(int16_t)*frames*2);    opna_timer_mix_oscillo(timer, buf, frames,                           g.oscillo_should_update ?                           g.oscillodata_audiothread : 0); @@ -150,7 +142,6 @@ static int pastream_cb(const void *inptr, void *outptr, unsigned long frames,      fft_write(&g.at_fftdata, buf, frames);      atomic_flag_clear_explicit(&g.at_fftdata_flag, memory_order_release);    } -  return paContinue;  }  static void load_fontrom(void) { @@ -188,10 +179,6 @@ err:  static bool openfile(const char *uri) {    struct fmplayer_file *fmfile = 0; -  if (!g.pa_initialized) { -    msgbox_err("Could not initialize Portaudio"); -    goto err; -  }    enum fmplayer_file_error error;    fmfile = fmplayer_file_alloc(uri, &error);    if (!fmfile) { @@ -206,19 +193,14 @@ static bool openfile(const char *uri) {      free(errbuf);      goto err;    } -  if (!g.pastream) { -    PaError pe = Pa_OpenDefaultStream(&g.pastream, 0, 2, paInt16, SRATE, AUDIOBUFLEN, -                                      pastream_cb, &g.opna_timer); -    if (pe != paNoError) { -      msgbox_err("cannot open portaudio stream"); -      goto err; -    } -  } else if (!g.pa_paused) { -    PaError pe = Pa_StopStream(g.pastream); -    if (pe != paNoError) { -      msgbox_err("Portaudio Error"); +  if (!g.ss) { +    g.ss = sound_init("FMPlayer", SRATE, soundout_cb, &g.opna_timer); +    if (!g.ss) { +      msgbox_err("cannot open audio stream");        goto err;      } +  } else if (!g.sound_paused) { +    g.ss->pause(g.ss, 1, 0);    }    fmplayer_file_free(g.fmfile);    g.fmfile = fmfile; @@ -236,8 +218,8 @@ static bool openfile(const char *uri) {    }    fmplayer_file_load(&g.work, g.fmfile, 1);    fmdsp_vram_init(&g.fmdsp, &g.work, g.vram); -  Pa_StartStream(g.pastream); -  g.pa_paused = false; +  g.ss->pause(g.ss, 0, 1); +  g.sound_paused = false;    g.work.paused = false;    {      const char *turi = strdup(uri); @@ -404,15 +386,9 @@ static gboolean key_press_cb(GtkWidget *w,      }      break;    case GDK_KEY_F7: -    if (g.pa_paused) { -      Pa_StartStream(g.pastream); -      g.pa_paused = false; -      g.work.paused = false; -    } else { -      Pa_StopStream(g.pastream); -      g.pa_paused = true; -      g.work.paused = true; -    } +    g.sound_paused ^= 1; +    g.work.paused = g.sound_paused; +    g.ss->pause(g.ss, g.sound_paused, 0);      break;    case GDK_KEY_F11:      fmdsp_dispstyle_set(&g.fmdsp, (g.fmdsp.style+1) % FMDSP_DISPSTYLE_CNT); @@ -528,7 +504,6 @@ int main(int argc, char **argv) {    create_box(); -  g.pa_initialized = (Pa_Initialize() == paNoError);    fmdsp_init(&g.fmdsp, &g.font98);    fmdsp_vram_init(&g.fmdsp, &g.work, g.vram);    g.vram32_stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, PC98_W); diff --git a/gtk/soundout/alsaout.c b/gtk/soundout/alsaout.c new file mode 100644 index 0000000..5c5e667 --- /dev/null +++ b/gtk/soundout/alsaout.c @@ -0,0 +1,152 @@ +#include "alsaout.h" +#include <stdbool.h> +#include <stdatomic.h> +#include <stdlib.h> +#include <sys/eventfd.h> +#include <pthread.h> +#include <unistd.h> +#include <poll.h> +#include <asoundlib.h> + +enum { +  BUF_FRAMES = 1024, +}; + +struct alsaout_state { +  struct sound_state ss; +  sound_callback cbfunc; +  void *userptr; +  snd_pcm_t *apcm; +  int fd_event; +  int fds_space; +  struct pollfd *fds; +  bool paused; +  bool terminate; +  atomic_flag cb_flag; +  bool thread_valid; +  pthread_t alsa_thread; +  int16_t buf[BUF_FRAMES*2]; +}; + +static void *alsaout_thread(void *ptr) { +  struct alsaout_state *as = ptr; +  for (;;) { +    as->fds[0] = (struct pollfd) { +      .fd = as->fd_event, +      .events = POLLIN, +    }; +    int fd_cnt = 1; +    fd_cnt += snd_pcm_poll_descriptors( +        as->apcm, as->fds + 1, as->fds_space - 1 +    ); +    int err = poll(as->fds, fd_cnt, -1); +    if (err <= 0) { +      continue; +    } +    if (as->terminate) return 0; +    if (as->fds[0].revents) { +      uint64_t eventdata; +      read(as->fd_event, &eventdata, sizeof(eventdata)); +    } +    unsigned short event; +    if (snd_pcm_poll_descriptors_revents( +        as->apcm, as->fds + 1, fd_cnt - 1, &event) < 0) continue; +    if (!event) continue; +    snd_pcm_sframes_t frames = snd_pcm_avail_update(as->apcm); +    if (frames <= 0) continue; +    while (frames) { +      snd_pcm_sframes_t genframes = frames; +      if (genframes > BUF_FRAMES) genframes = BUF_FRAMES; +      while (atomic_flag_test_and_set_explicit( +          &as->cb_flag, memory_order_acquire)); +      if (as->paused) { +        atomic_flag_clear_explicit(&as->cb_flag, memory_order_release); +        break; +      } +      as->cbfunc(as->userptr, as->buf, genframes); +      atomic_flag_clear_explicit(&as->cb_flag, memory_order_release); +      frames -= genframes; +      if (snd_pcm_state(as->apcm) == SND_PCM_STATE_XRUN) { +        err = snd_pcm_prepare(as->apcm); +      } +      snd_pcm_sframes_t written = snd_pcm_writei(as->apcm, as->buf, genframes); +      if (written < 0) { +        snd_pcm_prepare(as->apcm); +      } +    } +  } +} + +static void alsaout_pause(struct sound_state *ss, int pause, int flush) { +  struct alsaout_state *as = (struct alsaout_state *)ss; +   +  while (atomic_flag_test_and_set_explicit( +      &as->cb_flag, memory_order_acquire)); +  as->paused = pause; +  snd_pcm_pause(as->apcm, pause); +  atomic_flag_clear_explicit(&as->cb_flag, memory_order_release); +  uint64_t event = 1; +  write(as->fd_event, &event, sizeof(event)); +} + +static void alsaout_free(struct sound_state *ss) { +  if (!ss) return; +  struct alsaout_state *as = (struct alsaout_state *)ss; +  if (as->thread_valid) { +    as->terminate = true; +    uint64_t event = 1; +    write(as->fd_event, &event, sizeof(event)); +    pthread_join(as->alsa_thread, 0); +  } +  if (as->fd_event != -1) { +    close(as->fd_event); +  } +  if (as->fds) free(as->fds); +  if (as->apcm) { +    snd_pcm_close(as->apcm); +  } +  free(as); +} + +struct sound_state *alsaout_init( +  const char *clientname, unsigned srate, +  sound_callback cbfunc, void *userptr) { +  (void)clientname; +  struct alsaout_state *as = malloc(sizeof(*as)); +  if (!as) goto err; +  *as = (struct alsaout_state) { +    .ss = { +      .pause = alsaout_pause, +      .free = alsaout_free, +      .apiname = "ALSA", +    }, +    .cbfunc = cbfunc, +    .userptr = userptr, +    .fd_event = -1, +    .cb_flag = ATOMIC_FLAG_INIT, +    .paused = true, +  }; +  if (snd_pcm_open(&as->apcm, "default", SND_PCM_STREAM_PLAYBACK, 0)) { +    goto err; +  } +  as->fd_event = eventfd(0, EFD_CLOEXEC); +  if (as->fd_event < 0) goto err; +  as->fds_space = snd_pcm_poll_descriptors_count(as->apcm) + 1; +  as->fds = malloc(sizeof(*as->fds) * as->fds_space); +  if (!as->fds) goto err; +  if (snd_pcm_set_params( +      as->apcm, +      SND_PCM_FORMAT_S16, SND_PCM_ACCESS_RW_INTERLEAVED, 2, srate, 1, +      1000*1000/60)) { +    goto err; +  } +  if (pthread_create(&as->alsa_thread, 0, alsaout_thread, as)) { +    goto err; +  } +  as->thread_valid = true; +  return as; + +err: +  alsaout_free(&as->ss); +  return 0; +} diff --git a/gtk/soundout/alsaout.h b/gtk/soundout/alsaout.h new file mode 100644 index 0000000..cd79c27 --- /dev/null +++ b/gtk/soundout/alsaout.h @@ -0,0 +1,9 @@ +#ifndef MYON_ALSAOUT_H_INCLUDED +#define MYON_ALSAOUT_H_INCLUDED + +#include "soundout.h" + +struct sound_state *alsaout_init(const char *clientname, unsigned srate, sound_callback cbfunc, void *userptr); + +#endif // MYON_ALSAOUT_H_INCLUDED + diff --git a/gtk/soundout/jackout.c b/gtk/soundout/jackout.c new file mode 100644 index 0000000..54e7f16 --- /dev/null +++ b/gtk/soundout/jackout.c @@ -0,0 +1,127 @@ +#include "jackout.h" + +#include <stdbool.h> +#include <stdlib.h> +#include <jack/jack.h> +#include <soxr.h> + +typedef jack_default_audio_sample_t sample_t; + +enum { +  RFRAMES = 1024, +  JFRAMES = 2048, +}; + +struct jackout_state { +  struct sound_state ss; +  sound_callback cbfunc; +  void *userptr; +  jack_client_t *jc; +  jack_port_t *jp[2]; +  soxr_t soxr; +  bool paused; +  int16_t rbuf[RFRAMES*2]; +  float jbuf[JFRAMES*2]; +}; + +static size_t soxr_cb(void *ptr, soxr_in_t *data, size_t nframes) { +  struct jackout_state *js = ptr; +  if (nframes > RFRAMES) nframes = RFRAMES; +  for (size_t i = 0; i < RFRAMES*2; i++) { +    js->rbuf[i] = 0; +  } +  js->cbfunc(js->userptr, js->rbuf, nframes); +  *data = js->rbuf; +  return nframes; +} + +static int jack_cb(jack_nframes_t nframes, void *arg) { +  struct jackout_state *js = arg; +  sample_t *out[2]; +  for (int i = 0; i < 2; i++) out[i] = jack_port_get_buffer(js->jp[i], nframes); +  jack_nframes_t outi = 0; +  while (nframes) { +    jack_nframes_t pframes = nframes < JFRAMES ? nframes : JFRAMES; +    pframes = soxr_output(js->soxr, js->jbuf, pframes); +    for (jack_nframes_t j = 0; j < pframes; j++) { +      out[0][outi] = js->jbuf[j*2+0]; +      out[1][outi] = js->jbuf[j*2+1]; +      outi++; +    } +    nframes -= pframes; +  } +  return 0; +} + +static void jackout_pause(struct sound_state *ss, int pause, int flush) { +  struct jackout_state *js = (struct jackout_state *)ss; +  // TODO +  if (js->paused && !pause) { +    if (flush) { +      soxr_clear(js->soxr); +      soxr_set_input_fn(js->soxr, soxr_cb, js, RFRAMES); +    } +    jack_activate(js->jc); +    jack_connect(js->jc, jack_port_name(js->jp[0]), "system:playback_1"); +    jack_connect(js->jc, jack_port_name(js->jp[1]), "system:playback_2"); +  } else if (!js->paused && pause) { +    jack_deactivate(js->jc); +  } +  js->paused = pause; +} + +static void jackout_free(struct sound_state *ss) { +  struct jackout_state *js = (struct jackout_state *)ss; +  if (js) { +    if (js->jc) { +      for (int i = 0; i < 2; i++) { +        if (js->jp[i]) jack_port_unregister(js->jc, js->jp[i]); +      } +      jack_client_close(js->jc); +    } +    if (js->soxr) soxr_delete(js->soxr); +    free(js); +  } +} + +struct sound_state *jackout_init( +  const char *clientname, unsigned srate, +  sound_callback cbfunc, void *userptr) { +  struct jackout_state *js = malloc(sizeof(*js)); +  if (!js) goto err; +  *js = (struct jackout_state){ +    .ss = { +      .pause = jackout_pause, +      .free = jackout_free, +      .apiname = "JACK Audio", +    }, +    .cbfunc = cbfunc, +    .userptr = userptr, +    .paused = true, +  }; +  js->jc = jack_client_open(clientname, 0, 0); +  if (!js->jc) goto err; +  for (int i = 0; i < 2; i++) { +    js->jp[i] = jack_port_register( +        js->jc, i ? "right" : "left", JACK_DEFAULT_AUDIO_TYPE, +        JackPortIsOutput | JackPortIsTerminal, 0); +    if (!js->jp[i]) goto err; +  } +  soxr_io_spec_t iospec = { +    .itype = SOXR_INT16_I, +    .otype = SOXR_FLOAT32_I, +    .scale = 1.0, +  }; +  js->soxr = soxr_create( +      srate, jack_get_sample_rate(js->jc), +      2, 0, &iospec, 0, 0 +  ); +  if (!js->soxr) goto err; +  soxr_set_input_fn(js->soxr, soxr_cb, js, RFRAMES); +  if (jack_set_process_callback(js->jc, jack_cb, js)) goto err; + +  return &js->ss; +err: +  jackout_free(&js->ss); +  return 0; +} diff --git a/gtk/soundout/jackout.h b/gtk/soundout/jackout.h new file mode 100644 index 0000000..4b72f2e --- /dev/null +++ b/gtk/soundout/jackout.h @@ -0,0 +1,9 @@ +#ifndef MYON_JACKOUT_H_INCLUDED +#define MYON_JACKOUT_H_INCLUDED + +#include "soundout.h" + +struct sound_state *jackout_init(const char *clientname, unsigned srate, sound_callback cbfunc, void *userptr); + +#endif // MYON_JACKOUT_H_INCLUDED + diff --git a/gtk/soundout/ossout.c b/gtk/soundout/ossout.c new file mode 100644 index 0000000..1083f20 --- /dev/null +++ b/gtk/soundout/ossout.c @@ -0,0 +1,8 @@ +#include <sys/stat.h> +#include <fcntl.h> + +struct ossout_state { +  struct sound_state ss; +}; + +struct sound_state *ossout_init( diff --git a/gtk/soundout/pulseout.c b/gtk/soundout/pulseout.c new file mode 100644 index 0000000..9dbcd6c --- /dev/null +++ b/gtk/soundout/pulseout.c @@ -0,0 +1,133 @@ +#include "pulseout.h" + +#include <stdbool.h> +#include <stdlib.h> +#include <pulse/thread-mainloop.h> +#include <pulse/stream.h> + +struct pulseout_state { +  struct sound_state ss; +  bool paused; +  bool flush; +  unsigned srate; +  sound_callback cbfunc; +  void *userptr; +  pa_threaded_mainloop *pa_tm; +  pa_context *pa_c; +  pa_stream *pa_s; +  bool pa_status_changed; +}; + +static void pulseout_pause(struct sound_state *ss, int pause, int flush) { +  //return; +  struct pulseout_state *ps = (struct pulseout_state *)ss; +  if (ps->paused != !!pause) { +    if (pause) { +      pa_threaded_mainloop_lock(ps->pa_tm); +    } +    ps->paused = pause; +    if (!pause) { +      if (flush) ps->flush = true; +      pa_threaded_mainloop_unlock(ps->pa_tm); +    } +  } +} + +static void pulseout_free(struct sound_state *ss) { +  struct pulseout_state *ps = (struct pulseout_state *)ss; +  if (ps) { +    if (ps->pa_tm) { +      if (ps->paused) pa_threaded_mainloop_unlock(ps->pa_tm); +      pa_threaded_mainloop_stop(ps->pa_tm); +      if (ps->pa_s) { +        pa_stream_disconnect(ps->pa_s); +        pa_stream_unref(ps->pa_s); +      } +      if (ps->pa_c) pa_context_unref(ps->pa_c); +      pa_threaded_mainloop_free(ps->pa_tm); +    } +    free(ps); +  } +} + +static void pulseout_cb(pa_stream *p, size_t bytes, void *userdata) { +  struct pulseout_state *ps = userdata; +  int16_t *buf; +  pa_stream_begin_write(p, (void **)&buf, &bytes); +  size_t nframes = bytes / (2 * sizeof(int16_t)); +  if (!ps->paused) { +    ps->cbfunc(ps->userptr, buf, nframes); +  } else { +    for (size_t i = 0; i < nframes; i++) { +      buf[i*2+0] = 0; +      buf[i*2+1] = 0; +    } +  } +  pa_seek_mode_t smode = +      ps->flush ? PA_SEEK_RELATIVE_ON_READ : PA_SEEK_RELATIVE; +  pa_stream_write(p, buf, nframes * 2 * sizeof(int16_t), 0, 0, smode); +  ps->flush = false; +} + +static void pa_c_cb(pa_context *pa_c, void *userdata) { +  struct pulseout_state *ps = userdata; +  ps->pa_status_changed = true; +  pa_threaded_mainloop_signal(ps->pa_tm, 0); +} + +struct sound_state *pulseout_init( +  const char *clientname, unsigned srate, +  sound_callback cbfunc, void *userptr) { +  struct pulseout_state *ps = malloc(sizeof(*ps)); +  if (!ps) goto err; +  *ps = (struct pulseout_state){ +    .ss = { +      .pause = pulseout_pause, +      .free = pulseout_free, +      .apiname = "PulseAudio", +    }, +    .cbfunc = cbfunc, +    .userptr = userptr, +    .paused = false, +    .srate = srate, +  }; +  ps->pa_tm = pa_threaded_mainloop_new(); +  if (!ps->pa_tm) goto err; +  ps->pa_c = pa_context_new( +      pa_threaded_mainloop_get_api(ps->pa_tm), clientname +  ); +  if (!ps->pa_c) goto err; +  if (pa_context_connect(ps->pa_c, 0, 0, 0) < 0) goto err; +  pa_context_set_state_callback(ps->pa_c, pa_c_cb, ps); +  if (pa_threaded_mainloop_start(ps->pa_tm) < 0) goto err; +  pa_threaded_mainloop_lock(ps->pa_tm); +  ps->paused = true; +  for (;;) { +    while (!ps->pa_status_changed) { +      pa_threaded_mainloop_wait(ps->pa_tm); +    } +    ps->pa_status_changed = false; +    pa_context_state_t state = pa_context_get_state(ps->pa_c); +    if (state == PA_CONTEXT_CONNECTING || +        state == PA_CONTEXT_AUTHORIZING || +        state == PA_CONTEXT_SETTING_NAME) continue; +    else if (state == PA_CONTEXT_READY) break; +    else goto err; +  } +  pa_sample_spec ss = { +    .format = PA_SAMPLE_S16NE, +    .rate = ps->srate, +    .channels = 2, +  }; +  ps->pa_s = pa_stream_new( +      ps->pa_c, +      "stereoout", &ss, 0 +  ); +  if (!ps->pa_s) goto err; +  pa_stream_set_write_callback(ps->pa_s, pulseout_cb, ps); +  if (pa_stream_connect_playback(ps->pa_s, 0, 0, 0, 0, 0) < 0) goto err; +  return &ps->ss; +err: +  pulseout_free(&ps->ss); +  return 0; +} diff --git a/gtk/soundout/pulseout.h b/gtk/soundout/pulseout.h new file mode 100644 index 0000000..d9fbb20 --- /dev/null +++ b/gtk/soundout/pulseout.h @@ -0,0 +1,9 @@ +#ifndef MYON_PULSEOUT_H_INCLUDED +#define MYON_PULSEOUT_H_INCLUDED + +#include "soundout.h" + +struct sound_state *pulseout_init(const char *clientname, unsigned srate, sound_callback cbfunc, void *userptr); + +#endif // MYON_PULSEOUT_H_INCLUDED + diff --git a/gtk/soundout/soundout.c b/gtk/soundout/soundout.c new file mode 100644 index 0000000..56134be --- /dev/null +++ b/gtk/soundout/soundout.c @@ -0,0 +1,14 @@ +#include "soundout.h" +#include "jackout.h" +#include "pulseout.h" +#include "alsaout.h" + +struct sound_state *sound_init(const char *clientname, unsigned srate, sound_callback cbfunc, void *userptr) { +  struct sound_state *ss = 0; +  //ss = jackout_init(clientname, srate, cbfunc, userptr); +  if (ss) return ss; +  //ss = pulseout_init(clientname, srate, cbfunc, userptr); +  if (ss) return ss; +  ss = alsaout_init(clientname, srate, cbfunc, userptr); +  return ss; +} diff --git a/gtk/soundout/soundout.h b/gtk/soundout/soundout.h new file mode 100644 index 0000000..aea926f --- /dev/null +++ b/gtk/soundout/soundout.h @@ -0,0 +1,16 @@ +#ifndef MYON_SOUNDOUT_H_INCLUDED +#define MYON_SOUNDOUT_H_INCLUDED + +#include <stdint.h> + +typedef void (*sound_callback)(void *userptr, int16_t *buf, unsigned frames); + +struct sound_state { +  void (*pause)(struct sound_state *state, int pause, int flush); +  void (*free)(struct sound_state *state); +  const char *apiname; +}; + +struct sound_state *sound_init(const char *clientname, unsigned srate, sound_callback cbfunc, void *userptr); + +#endif // MYON_SOUNDOUT_H_INCLUDED | 
