Commit 860d9048 authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/kraxel/tags/audio-20190924-pull-request' into staging



audio: documentation fixes.
audio: new backend api (first part of the surround sound patch series).

# gpg: Signature made Tue 24 Sep 2019 07:19:31 BST
# gpg:                using RSA key 4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full]
# gpg:                 aka "Gerd Hoffmann <gerd@kraxel.org>" [full]
# gpg:                 aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full]
# Primary key fingerprint: A032 8CFF B93A 17A7 9901  FE7D 4CB6 D8EE D3E8 7138

* remotes/kraxel/tags/audio-20190924-pull-request:
  audio: split ctl_* functions into enable_* and volume_*
  audio: common rate control code for timer based outputs
  audio: unify input and output mixeng buffer management
  audio: remove remains of the old backend api
  wavaudio: port to the new audio backend api
  spiceaudio: port to the new audio backend api
  sdlaudio: port to the new audio backend api
  paaudio: port to the new audio backend api
  ossaudio: port to the new audio backend api
  noaudio: port to the new audio backend api
  dsoundaudio: port to the new audio backend api
  coreaudio: port to the new audio backend api
  alsaaudio: port to the new audio backend api
  audio: api for mixeng code free backends
  audio: fix ALSA period-length typo in documentation
  audio: fix buffer-length typo in documentation

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 8dc57281 571a8c52
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -2,7 +2,6 @@ common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o
common-obj-$(CONFIG_SPICE) += spiceaudio.o
common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o
common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
common-obj-y += wavcapture.o

+105 −259
Original line number Diff line number Diff line
@@ -44,9 +44,6 @@ struct pollhlp {

typedef struct ALSAVoiceOut {
    HWVoiceOut hw;
    int wpos;
    int pending;
    void *pcm_buf;
    snd_pcm_t *handle;
    struct pollhlp pollhlp;
    Audiodev *dev;
@@ -55,7 +52,6 @@ typedef struct ALSAVoiceOut {
typedef struct ALSAVoiceIn {
    HWVoiceIn hw;
    snd_pcm_t *handle;
    void *pcm_buf;
    struct pollhlp pollhlp;
    Audiodev *dev;
} ALSAVoiceIn;
@@ -602,102 +598,64 @@ static int alsa_open(bool in, struct alsa_params_req *req,
    return -1;
}

static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
static size_t alsa_write(HWVoiceOut *hw, void *buf, size_t len)
{
    snd_pcm_sframes_t avail;

    avail = snd_pcm_avail_update (handle);
    if (avail < 0) {
        if (avail == -EPIPE) {
            if (!alsa_recover (handle)) {
                avail = snd_pcm_avail_update (handle);
            }
        }

        if (avail < 0) {
            alsa_logerr (avail,
                         "Could not obtain number of available frames\n");
            return -1;
        }
    }

    return avail;
}

static void alsa_write_pending (ALSAVoiceOut *alsa)
{
    HWVoiceOut *hw = &alsa->hw;

    while (alsa->pending) {
        int left_till_end_samples = hw->samples - alsa->wpos;
        int len = MIN (alsa->pending, left_till_end_samples);
        char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift);
    ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
    size_t pos = 0;
    size_t len_frames = len >> hw->info.shift;

        while (len) {
    while (len_frames) {
        char *src = advance(buf, pos);
        snd_pcm_sframes_t written;

            written = snd_pcm_writei (alsa->handle, src, len);
        written = snd_pcm_writei(alsa->handle, src, len_frames);

        if (written <= 0) {
            switch (written) {
            case 0:
                    trace_alsa_wrote_zero(len);
                    return;
                trace_alsa_wrote_zero(len_frames);
                return pos;

            case -EPIPE:
                if (alsa_recover(alsa->handle)) {
                        alsa_logerr (written, "Failed to write %d frames\n",
                                     len);
                        return;
                    alsa_logerr(written, "Failed to write %zu frames\n",
                                len_frames);
                    return pos;
                }
                trace_alsa_xrun_out();
                continue;

            case -ESTRPIPE:
                    /* stream is suspended and waiting for an
                       application recovery */
                /*
                 * stream is suspended and waiting for an application
                 * recovery
                 */
                if (alsa_resume(alsa->handle)) {
                        alsa_logerr (written, "Failed to write %d frames\n",
                                     len);
                        return;
                    alsa_logerr(written, "Failed to write %zu frames\n",
                                len_frames);
                    return pos;
                }
                trace_alsa_resume_out();
                continue;

            case -EAGAIN:
                    return;
                return pos;

            default:
                    alsa_logerr (written, "Failed to write %d frames from %p\n",
                alsa_logerr(written, "Failed to write %zu frames from %p\n",
                            len, src);
                    return;
                return pos;
            }
        }

            alsa->wpos = (alsa->wpos + written) % hw->samples;
            alsa->pending -= written;
            len -= written;
        }
        pos += written << hw->info.shift;
        if (written < len_frames) {
            break;
        }
        len_frames -= written;
    }

static size_t alsa_run_out(HWVoiceOut *hw, size_t live)
{
    ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
    size_t decr;
    snd_pcm_sframes_t avail;

    avail = alsa_get_avail (alsa->handle);
    if (avail < 0) {
        dolog ("Could not get number of available playback frames\n");
        return 0;
    }

    decr = MIN (live, avail);
    decr = audio_pcm_hw_clip_out (hw, alsa->pcm_buf, decr, alsa->pending);
    alsa->pending += decr;
    alsa_write_pending (alsa);
    return decr;
    return pos;
}

static void alsa_fini_out (HWVoiceOut *hw)
@@ -706,9 +664,6 @@ static void alsa_fini_out (HWVoiceOut *hw)

    ldebug ("alsa_fini\n");
    alsa_anal_close (&alsa->handle, &alsa->pollhlp);

    g_free(alsa->pcm_buf);
    alsa->pcm_buf = NULL;
}

static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
@@ -737,14 +692,6 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
    audio_pcm_init_info (&hw->info, &obt_as);
    hw->samples = obt.samples;

    alsa->pcm_buf = audio_calloc(__func__, obt.samples, 1 << hw->info.shift);
    if (!alsa->pcm_buf) {
        dolog("Could not allocate DAC buffer (%zu samples, each %d bytes)\n",
              hw->samples, 1 << hw->info.shift);
        alsa_anal_close1 (&handle);
        return -1;
    }

    alsa->pollhlp.s = hw->s;
    alsa->handle = handle;
    alsa->dev = dev;
@@ -784,14 +731,12 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
    return 0;
}

static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
static void alsa_enable_out(HWVoiceOut *hw, bool enable)
{
    ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
    AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out;

    switch (cmd) {
    case VOICE_ENABLE:
        {
    if (enable) {
        bool poll_mode = apdo->try_poll;

        ldebug("enabling voice\n");
@@ -799,19 +744,15 @@ static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
            poll_mode = 0;
        }
        hw->poll_mode = poll_mode;
            return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PREPARE);
        }

    case VOICE_DISABLE:
        alsa_voice_ctl(alsa->handle, "playback", VOICE_CTL_PREPARE);
    } else {
        ldebug("disabling voice\n");
        if (hw->poll_mode) {
            hw->poll_mode = 0;
            alsa_fini_poll(&alsa->pollhlp);
        }
        return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PAUSE);
        alsa_voice_ctl(alsa->handle, "playback", VOICE_CTL_PAUSE);
    }

    return -1;
}

static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
@@ -839,14 +780,6 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
    audio_pcm_init_info (&hw->info, &obt_as);
    hw->samples = obt.samples;

    alsa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
    if (!alsa->pcm_buf) {
        dolog("Could not allocate ADC buffer (%zu samples, each %d bytes)\n",
              hw->samples, 1 << hw->info.shift);
        alsa_anal_close1 (&handle);
        return -1;
    }

    alsa->pollhlp.s = hw->s;
    alsa->handle = handle;
    alsa->dev = dev;
@@ -858,139 +791,56 @@ static void alsa_fini_in (HWVoiceIn *hw)
    ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;

    alsa_anal_close (&alsa->handle, &alsa->pollhlp);

    g_free(alsa->pcm_buf);
    alsa->pcm_buf = NULL;
}

static size_t alsa_run_in(HWVoiceIn *hw)
static size_t alsa_read(HWVoiceIn *hw, void *buf, size_t len)
{
    ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
    int hwshift = hw->info.shift;
    int i;
    size_t live = audio_pcm_hw_get_live_in (hw);
    size_t dead = hw->samples - live;
    size_t decr;
    struct {
        size_t add;
        size_t len;
    } bufs[2] = {
        { .add = hw->wpos, .len = 0 },
        { .add = 0,        .len = 0 }
    };
    snd_pcm_sframes_t avail;
    snd_pcm_uframes_t read_samples = 0;

    if (!dead) {
        return 0;
    }
    size_t pos = 0;

    avail = alsa_get_avail (alsa->handle);
    if (avail < 0) {
        dolog ("Could not get number of captured frames\n");
        return 0;
    }

    if (!avail) {
        snd_pcm_state_t state;

        state = snd_pcm_state (alsa->handle);
        switch (state) {
        case SND_PCM_STATE_PREPARED:
            avail = hw->samples;
            break;
        case SND_PCM_STATE_SUSPENDED:
            /* stream is suspended and waiting for an application recovery */
            if (alsa_resume (alsa->handle)) {
                dolog ("Failed to resume suspended input stream\n");
                return 0;
            }
            trace_alsa_resume_in();
            break;
        default:
            trace_alsa_no_frames(state);
            return 0;
        }
    }

    decr = MIN(dead, avail);
    if (!decr) {
        return 0;
    }

    if (hw->wpos + decr > hw->samples) {
        bufs[0].len = (hw->samples - hw->wpos);
        bufs[1].len = (decr - (hw->samples - hw->wpos));
    }
    else {
        bufs[0].len = decr;
    }

    for (i = 0; i < 2; ++i) {
        void *src;
        struct st_sample *dst;
    while (len) {
        void *dst = advance(buf, pos);
        snd_pcm_sframes_t nread;
        snd_pcm_uframes_t len;

        len = bufs[i].len;

        src = advance (alsa->pcm_buf, bufs[i].add << hwshift);
        dst = hw->conv_buf + bufs[i].add;

        while (len) {
            nread = snd_pcm_readi (alsa->handle, src, len);
        nread = snd_pcm_readi(alsa->handle, dst, len >> hw->info.shift);

        if (nread <= 0) {
            switch (nread) {
            case 0:
                trace_alsa_read_zero(len);
                    goto exit;
                return pos;;

            case -EPIPE:
                if (alsa_recover(alsa->handle)) {
                        alsa_logerr (nread, "Failed to read %ld frames\n", len);
                        goto exit;
                    alsa_logerr(nread, "Failed to read %zu frames\n", len);
                    return pos;
                }
                trace_alsa_xrun_in();
                continue;

            case -EAGAIN:
                    goto exit;
                return pos;

            default:
                    alsa_logerr (
                        nread,
                        "Failed to read %ld frames from %p\n",
                        len,
                        src
                        );
                    goto exit;
                alsa_logerr(nread, "Failed to read %zu frames to %p\n",
                            len, dst);
                return pos;;
            }
        }

            hw->conv (dst, src, nread);

            src = advance (src, nread << hwshift);
            dst += nread;

            read_samples += nread;
            len -= nread;
        }
        pos += nread << hw->info.shift;
        len -= nread << hw->info.shift;
    }

 exit:
    hw->wpos = (hw->wpos + read_samples) % hw->samples;
    return read_samples;
    return pos;
}

static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
static void alsa_enable_in(HWVoiceIn *hw, bool enable)
{
    ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
    AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in;

    switch (cmd) {
    case VOICE_ENABLE:
        {
    if (enable) {
        bool poll_mode = apdo->try_poll;

        ldebug("enabling voice\n");
@@ -999,19 +849,15 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
        }
        hw->poll_mode = poll_mode;

            return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_START);
        }

    case VOICE_DISABLE:
        alsa_voice_ctl(alsa->handle, "capture", VOICE_CTL_START);
    } else {
        ldebug ("disabling voice\n");
        if (hw->poll_mode) {
            hw->poll_mode = 0;
            alsa_fini_poll(&alsa->pollhlp);
        }
        return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_PAUSE);
        alsa_voice_ctl(alsa->handle, "capture", VOICE_CTL_PAUSE);
    }

    return -1;
}

static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo)
@@ -1065,13 +911,13 @@ static void alsa_audio_fini (void *opaque)
static struct audio_pcm_ops alsa_pcm_ops = {
    .init_out = alsa_init_out,
    .fini_out = alsa_fini_out,
    .run_out  = alsa_run_out,
    .ctl_out  = alsa_ctl_out,
    .write    = alsa_write,
    .enable_out = alsa_enable_out,

    .init_in  = alsa_init_in,
    .fini_in  = alsa_fini_in,
    .run_in   = alsa_run_in,
    .ctl_in   = alsa_ctl_in,
    .read     = alsa_read,
    .enable_in = alsa_enable_in,
};

static struct audio_driver alsa_audio_driver = {
+305 −78

File changed.

Preview size limit exceeded, changes collapsed.

+52 −26
Original line number Diff line number Diff line
@@ -52,6 +52,11 @@ struct audio_pcm_info {
typedef struct AudioState AudioState;
typedef struct SWVoiceCap SWVoiceCap;

typedef struct STSampleBuffer {
    size_t pos, size;
    st_sample samples[];
} STSampleBuffer;

typedef struct HWVoiceOut {
    AudioState *s;
    int enabled;
@@ -60,16 +65,15 @@ typedef struct HWVoiceOut {
    struct audio_pcm_info info;

    f_sample *clip;

    size_t rpos;
    uint64_t ts_helper;

    struct st_sample *mix_buf;
    STSampleBuffer *mix_buf;
    void *buf_emul;
    size_t pos_emul, pending_emul, size_emul;

    size_t samples;
    QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
    QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
    int ctl_caps;
    struct audio_pcm_ops *pcm_ops;
    QLIST_ENTRY (HWVoiceOut) entries;
} HWVoiceOut;
@@ -82,15 +86,15 @@ typedef struct HWVoiceIn {

    t_sample *conv;

    size_t wpos;
    size_t total_samples_captured;
    uint64_t ts_helper;

    struct st_sample *conv_buf;
    STSampleBuffer *conv_buf;
    void *buf_emul;
    size_t pos_emul, pending_emul, size_emul;

    size_t samples;
    QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
    int ctl_caps;
    struct audio_pcm_ops *pcm_ops;
    QLIST_ENTRY (HWVoiceIn) entries;
} HWVoiceIn;
@@ -142,22 +146,46 @@ struct audio_driver {
    int max_voices_in;
    int voice_size_out;
    int voice_size_in;
    int ctl_caps;
    QLIST_ENTRY(audio_driver) next;
};

struct audio_pcm_ops {
    int  (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque);
    int    (*init_out)(HWVoiceOut *hw, audsettings *as, void *drv_opaque);
    void   (*fini_out)(HWVoiceOut *hw);
    size_t (*run_out)(HWVoiceOut *hw, size_t live);
    int  (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
    size_t (*write)   (HWVoiceOut *hw, void *buf, size_t size);
    /*
     * get a buffer that after later can be passed to put_buffer_out; optional
     * returns the buffer, and writes it's size to size (in bytes)
     * this is unrelated to the above buffer_size_out function
     */
    void  *(*get_buffer_out)(HWVoiceOut *hw, size_t *size);
    /*
     * put back the buffer returned by get_buffer_out; optional
     * buf must be equal the pointer returned by get_buffer_out,
     * size may be smaller
     */
    size_t (*put_buffer_out)(HWVoiceOut *hw, void *buf, size_t size);
    void   (*enable_out)(HWVoiceOut *hw, bool enable);
    void   (*volume_out)(HWVoiceOut *hw, struct mixeng_volume *vol);

    int  (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque);
    int    (*init_in) (HWVoiceIn *hw, audsettings *as, void *drv_opaque);
    void   (*fini_in) (HWVoiceIn *hw);
    size_t (*run_in)(HWVoiceIn *hw);
    int  (*ctl_in)  (HWVoiceIn *hw, int cmd, ...);
    size_t (*read)    (HWVoiceIn *hw, void *buf, size_t size);
    void  *(*get_buffer_in)(HWVoiceIn *hw, size_t *size);
    void   (*put_buffer_in)(HWVoiceIn *hw, void *buf, size_t size);
    void   (*enable_in)(HWVoiceIn *hw, bool enable);
    void   (*volume_in)(HWVoiceIn *hw, struct mixeng_volume *vol);
};

void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size);
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size);
size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
                                            size_t size);
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size);
size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size);

struct capture_callback {
    struct audio_capture_ops ops;
    void *opaque;
@@ -208,21 +236,19 @@ audio_driver *audio_driver_lookup(const char *name);
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);

size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw);

size_t audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf,
                             size_t live, size_t pending);

int audio_bug (const char *funcname, int cond);
void *audio_calloc (const char *funcname, int nmemb, size_t size);

void audio_run(AudioState *s, const char *msg);

#define VOICE_ENABLE 1
#define VOICE_DISABLE 2
#define VOICE_VOLUME 3
typedef struct RateCtl {
    int64_t start_ticks;
    int64_t bytes_sent;
} RateCtl;

#define VOICE_VOLUME_CAP (1 << VOICE_VOLUME)
void audio_rate_start(RateCtl *rate);
size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
                            size_t bytes_avail);

static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
{

audio/audio_pt_int.c

deleted100644 → 0
+0 −173
Original line number Diff line number Diff line
#include "qemu/osdep.h"
#include "audio.h"

#define AUDIO_CAP "audio-pt"

#include "audio_int.h"
#include "audio_pt_int.h"

static void GCC_FMT_ATTR(3, 4) logerr (struct audio_pt *pt, int err,
                                       const char *fmt, ...)
{
    va_list ap;

    va_start (ap, fmt);
    AUD_vlog (pt->drv, fmt, ap);
    va_end (ap);

    AUD_log (NULL, "\n");
    AUD_log (pt->drv, "Reason: %s\n", strerror (err));
}

int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
                   void *opaque, const char *drv, const char *cap)
{
    int err, err2;
    const char *efunc;
    sigset_t set, old_set;

    p->drv = drv;

    err = sigfillset (&set);
    if (err) {
        logerr(p, errno, "%s(%s): sigfillset failed", cap, __func__);
        return -1;
    }

    err = pthread_mutex_init (&p->mutex, NULL);
    if (err) {
        efunc = "pthread_mutex_init";
        goto err0;
    }

    err = pthread_cond_init (&p->cond, NULL);
    if (err) {
        efunc = "pthread_cond_init";
        goto err1;
    }

    err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
    if (err) {
        efunc = "pthread_sigmask";
        goto err2;
    }

    err = pthread_create (&p->thread, NULL, func, opaque);

    err2 = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
    if (err2) {
        logerr(p, err2, "%s(%s): pthread_sigmask (restore) failed",
               cap, __func__);
        /* We have failed to restore original signal mask, all bets are off,
           so terminate the process */
        exit (EXIT_FAILURE);
    }

    if (err) {
        efunc = "pthread_create";
        goto err2;
    }

    return 0;

 err2:
    err2 = pthread_cond_destroy (&p->cond);
    if (err2) {
        logerr(p, err2, "%s(%s): pthread_cond_destroy failed", cap, __func__);
    }

 err1:
    err2 = pthread_mutex_destroy (&p->mutex);
    if (err2) {
        logerr(p, err2, "%s(%s): pthread_mutex_destroy failed", cap, __func__);
    }

 err0:
    logerr(p, err, "%s(%s): %s failed", cap, __func__, efunc);
    return -1;
}

int audio_pt_fini (struct audio_pt *p, const char *cap)
{
    int err, ret = 0;

    err = pthread_cond_destroy (&p->cond);
    if (err) {
        logerr(p, err, "%s(%s): pthread_cond_destroy failed", cap, __func__);
        ret = -1;
    }

    err = pthread_mutex_destroy (&p->mutex);
    if (err) {
        logerr(p, err, "%s(%s): pthread_mutex_destroy failed", cap, __func__);
        ret = -1;
    }
    return ret;
}

int audio_pt_lock (struct audio_pt *p, const char *cap)
{
    int err;

    err = pthread_mutex_lock (&p->mutex);
    if (err) {
        logerr(p, err, "%s(%s): pthread_mutex_lock failed", cap, __func__);
        return -1;
    }
    return 0;
}

int audio_pt_unlock (struct audio_pt *p, const char *cap)
{
    int err;

    err = pthread_mutex_unlock (&p->mutex);
    if (err) {
        logerr(p, err, "%s(%s): pthread_mutex_unlock failed", cap, __func__);
        return -1;
    }
    return 0;
}

int audio_pt_wait (struct audio_pt *p, const char *cap)
{
    int err;

    err = pthread_cond_wait (&p->cond, &p->mutex);
    if (err) {
        logerr(p, err, "%s(%s): pthread_cond_wait failed", cap, __func__);
        return -1;
    }
    return 0;
}

int audio_pt_unlock_and_signal (struct audio_pt *p, const char *cap)
{
    int err;

    err = pthread_mutex_unlock (&p->mutex);
    if (err) {
        logerr(p, err, "%s(%s): pthread_mutex_unlock failed", cap, __func__);
        return -1;
    }
    err = pthread_cond_signal (&p->cond);
    if (err) {
        logerr(p, err, "%s(%s): pthread_cond_signal failed", cap, __func__);
        return -1;
    }
    return 0;
}

int audio_pt_join (struct audio_pt *p, void **arg, const char *cap)
{
    int err;
    void *ret;

    err = pthread_join (p->thread, &ret);
    if (err) {
        logerr(p, err, "%s(%s): pthread_join failed", cap, __func__);
        return -1;
    }
    *arg = ret;
    return 0;
}
Loading