Blame src/libout123/modules/qsa.c

Packit c32a2d
/*
Packit c32a2d
    qsa: sound output with QNX Sound Architecture 0.5.2 API
Packit c32a2d
Packit c32a2d
    copyright 2013 by the mpg123 project - free software under the terms of the LGPL 2.1
Packit c32a2d
    see COPYING and AUTHORS files in distribution or http://mpg123.org
Packit c32a2d
Packit c32a2d
    written by Mike Gorchak <mike.gorchak.qnx@gmail.com>
Packit c32a2d
*/
Packit c32a2d
Packit c32a2d
#include "out123_int.h"
Packit c32a2d
#include <errno.h>
Packit c32a2d
Packit c32a2d
#include <stdint.h>
Packit c32a2d
#include <sys/asoundlib.h>
Packit c32a2d
Packit c32a2d
#include "debug.h"
Packit c32a2d
Packit c32a2d
typedef struct _qsa_mp_map
Packit c32a2d
{
Packit c32a2d
    uint32_t qsa_format;
Packit c32a2d
    int      mp_format;
Packit c32a2d
} qsa_mp_map_t;
Packit c32a2d
Packit c32a2d
/* in order best format first */
Packit c32a2d
qsa_mp_map_t format_map[]=
Packit c32a2d
{
Packit c32a2d
#ifdef WORDS_BIGENDIAN
Packit c32a2d
    {SND_PCM_SFMT_FLOAT64_BE, MPG123_ENC_FLOAT_64     },
Packit c32a2d
    {SND_PCM_SFMT_FLOAT_BE,   MPG123_ENC_FLOAT_32     },
Packit c32a2d
    {SND_PCM_SFMT_S32_BE,     MPG123_ENC_SIGNED_32    },
Packit c32a2d
    {SND_PCM_SFMT_U32_BE,     MPG123_ENC_UNSIGNED_32  },
Packit c32a2d
    {SND_PCM_SFMT_S24_BE,     MPG123_ENC_SIGNED_24    },
Packit c32a2d
    {SND_PCM_SFMT_U24_BE,     MPG123_ENC_UNSIGNED_24  },
Packit c32a2d
    {SND_PCM_SFMT_S16_BE,     MPG123_ENC_SIGNED_16    },
Packit c32a2d
    {SND_PCM_SFMT_U16_BE,     MPG123_ENC_UNSIGNED_16  },
Packit c32a2d
#else
Packit c32a2d
    {SND_PCM_SFMT_FLOAT64_LE, MPG123_ENC_FLOAT_64     },
Packit c32a2d
    {SND_PCM_SFMT_FLOAT_LE,   MPG123_ENC_FLOAT_32     },
Packit c32a2d
    {SND_PCM_SFMT_S32_LE,     MPG123_ENC_SIGNED_32    },
Packit c32a2d
    {SND_PCM_SFMT_U32_LE,     MPG123_ENC_UNSIGNED_32  },
Packit c32a2d
    {SND_PCM_SFMT_S24_LE,     MPG123_ENC_SIGNED_24    },
Packit c32a2d
    {SND_PCM_SFMT_U24_LE,     MPG123_ENC_UNSIGNED_24  },
Packit c32a2d
    {SND_PCM_SFMT_S16_LE,     MPG123_ENC_SIGNED_16    },
Packit c32a2d
    {SND_PCM_SFMT_U16_LE,     MPG123_ENC_UNSIGNED_16  },
Packit c32a2d
#endif
Packit c32a2d
    {SND_PCM_SFMT_U8,         MPG123_ENC_UNSIGNED_8   },
Packit c32a2d
    {SND_PCM_SFMT_S8,         MPG123_ENC_SIGNED_8     },
Packit c32a2d
    {SND_PCM_SFMT_A_LAW,      MPG123_ENC_ALAW_8       },
Packit c32a2d
    {SND_PCM_SFMT_MU_LAW,     MPG123_ENC_ULAW_8       },
Packit c32a2d
    {0,                       0                       },
Packit c32a2d
};
Packit c32a2d
Packit c32a2d
typedef struct _qsa_internal
Packit c32a2d
{
Packit c32a2d
    int cardno;
Packit c32a2d
    int deviceno;
Packit c32a2d
    snd_pcm_t* audio_handle;
Packit c32a2d
    snd_pcm_channel_params_t cpars;
Packit c32a2d
} qsa_internal_t;
Packit c32a2d
Packit c32a2d
static int open_qsa(out123_handle* ao)
Packit c32a2d
{
Packit c32a2d
    int status;
Packit c32a2d
    int cardno;
Packit c32a2d
    int deviceno;
Packit c32a2d
    int it;
Packit c32a2d
    snd_pcm_t* audio_handle;
Packit c32a2d
    qsa_internal_t* userptr;
Packit c32a2d
Packit c32a2d
    ao->userptr=NULL;
Packit c32a2d
Packit c32a2d
    status=snd_pcm_open_preferred(&audio_handle, &cardno, &deviceno, SND_PCM_OPEN_PLAYBACK);
Packit c32a2d
    if (status<0)
Packit c32a2d
    {
Packit c32a2d
        return FALSE;
Packit c32a2d
    }
Packit c32a2d
Packit c32a2d
    status=snd_pcm_plugin_set_disable(audio_handle, PLUGIN_DISABLE_MMAP);
Packit c32a2d
    if (status<0)
Packit c32a2d
    {
Packit c32a2d
        return FALSE;
Packit c32a2d
    }
Packit c32a2d
Packit c32a2d
    userptr=calloc(1, sizeof(qsa_internal_t));
Packit c32a2d
    if (userptr==NULL)
Packit c32a2d
    {
Packit c32a2d
        return FALSE;
Packit c32a2d
    }
Packit c32a2d
    ao->userptr=userptr;
Packit c32a2d
    userptr->audio_handle=audio_handle;
Packit c32a2d
    userptr->cardno=cardno;
Packit c32a2d
    userptr->deviceno=deviceno;
Packit c32a2d
Packit c32a2d
    memset(&userptr->cpars, 0, sizeof(userptr->cpars));
Packit c32a2d
Packit c32a2d
    userptr->cpars.channel=SND_PCM_CHANNEL_PLAYBACK;
Packit c32a2d
    userptr->cpars.mode=SND_PCM_MODE_BLOCK;
Packit c32a2d
    userptr->cpars.start_mode=SND_PCM_START_DATA;
Packit c32a2d
    userptr->cpars.stop_mode=SND_PCM_STOP_STOP;
Packit c32a2d
    userptr->cpars.format.format=0;
Packit c32a2d
    it=0;
Packit c32a2d
    do {
Packit c32a2d
        if ((format_map[it].qsa_format==0) && (format_map[it].mp_format==0))
Packit c32a2d
        {
Packit c32a2d
            break;
Packit c32a2d
        }
Packit c32a2d
        if (ao->format==format_map[it].mp_format)
Packit c32a2d
        {
Packit c32a2d
            userptr->cpars.format.format=format_map[it].qsa_format;
Packit c32a2d
            break;
Packit c32a2d
        }
Packit c32a2d
        it++;
Packit c32a2d
    } while(1);
Packit c32a2d
    userptr->cpars.format.interleave=1;
Packit c32a2d
    userptr->cpars.format.rate=ao->rate;
Packit c32a2d
    userptr->cpars.format.voices=ao->channels;
Packit c32a2d
    userptr->cpars.buf.block.frag_size=4096;
Packit c32a2d
    userptr->cpars.buf.block.frags_min=8;
Packit c32a2d
    userptr->cpars.buf.block.frags_max=16;
Packit c32a2d
Packit c32a2d
    if ((ao->channels!=-1) && (ao->rate!=-1))
Packit c32a2d
    {
Packit c32a2d
        status=snd_pcm_plugin_params(userptr->audio_handle, &userptr->cpars);
Packit c32a2d
        if (status<0)
Packit c32a2d
        {
Packit c32a2d
            return FALSE;
Packit c32a2d
        }
Packit c32a2d
Packit c32a2d
        status=snd_pcm_plugin_prepare(userptr->audio_handle, SND_PCM_CHANNEL_PLAYBACK);
Packit c32a2d
        if (status<0)
Packit c32a2d
        {
Packit c32a2d
            return FALSE;
Packit c32a2d
        }
Packit c32a2d
    }
Packit c32a2d
Packit c32a2d
    return TRUE;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static int get_formats_qsa(out123_handle* ao)
Packit c32a2d
{
Packit c32a2d
    qsa_internal_t* userptr;
Packit c32a2d
    int status;
Packit c32a2d
    int it=0;
Packit c32a2d
Packit c32a2d
    userptr=ao->userptr;
Packit c32a2d
    if (userptr!=NULL);
Packit c32a2d
    {
Packit c32a2d
        userptr->cpars.format.rate=ao->rate;
Packit c32a2d
        userptr->cpars.format.voices=ao->channels;
Packit c32a2d
        it=0;
Packit c32a2d
        do {
Packit c32a2d
            if ((format_map[it].qsa_format==0) && (format_map[it].mp_format==0))
Packit c32a2d
            {
Packit c32a2d
                break;
Packit c32a2d
            }
Packit c32a2d
            userptr->cpars.format.format=format_map[it].qsa_format;
Packit c32a2d
            status=snd_pcm_plugin_params(userptr->audio_handle, &userptr->cpars);
Packit c32a2d
            if (status<0)
Packit c32a2d
            {
Packit c32a2d
                it++;
Packit c32a2d
            }
Packit c32a2d
            else
Packit c32a2d
            {
Packit c32a2d
                return format_map[it].mp_format;
Packit c32a2d
            }
Packit c32a2d
        } while(1);
Packit c32a2d
    }
Packit c32a2d
Packit c32a2d
    return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static int write_qsa(out123_handle* ao, unsigned char* buf, int bytes)
Packit c32a2d
{
Packit c32a2d
    int written;
Packit c32a2d
    int status;
Packit c32a2d
    snd_pcm_channel_status_t cstatus;
Packit c32a2d
    qsa_internal_t* userptr;
Packit c32a2d
Packit c32a2d
    userptr=ao->userptr;
Packit c32a2d
    if (userptr!=NULL);
Packit c32a2d
    {
Packit c32a2d
        written=snd_pcm_plugin_write(userptr->audio_handle, buf, bytes);
Packit c32a2d
        if (written!=bytes)
Packit c32a2d
        {
Packit c32a2d
            /* Check if samples playback got stuck somewhere in hardware or in */
Packit c32a2d
            /* the audio device driver */
Packit c32a2d
            if ((errno==EAGAIN) && (written==0))
Packit c32a2d
            {
Packit c32a2d
                return 0;
Packit c32a2d
            }
Packit c32a2d
            if ((errno==EINVAL) || (errno==EIO))
Packit c32a2d
            {
Packit c32a2d
                memset(&cstatus, 0, sizeof(cstatus));
Packit c32a2d
                cstatus.channel=SND_PCM_CHANNEL_PLAYBACK;
Packit c32a2d
                status=snd_pcm_plugin_status(userptr->audio_handle, &cstatus);
Packit c32a2d
                if (status>0)
Packit c32a2d
                {
Packit c32a2d
                    return 0;
Packit c32a2d
                }
Packit c32a2d
                if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) ||
Packit c32a2d
                    (cstatus.status == SND_PCM_STATUS_READY))
Packit c32a2d
                {
Packit c32a2d
                    status=snd_pcm_plugin_prepare(userptr->audio_handle, SND_PCM_CHANNEL_PLAYBACK);
Packit c32a2d
                    if (status<0)
Packit c32a2d
                    {
Packit c32a2d
                        return 0;
Packit c32a2d
                    }
Packit c32a2d
                }
Packit c32a2d
            }
Packit c32a2d
        }
Packit c32a2d
    }
Packit c32a2d
Packit c32a2d
    return written;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static void flush_qsa(out123_handle* ao)
Packit c32a2d
{
Packit c32a2d
    qsa_internal_t* userptr;
Packit c32a2d
Packit c32a2d
    userptr=ao->userptr;
Packit c32a2d
    if (userptr!=NULL);
Packit c32a2d
    {
Packit c32a2d
        snd_pcm_playback_flush(userptr->audio_handle);
Packit c32a2d
    }
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static int close_qsa(out123_handle* ao)
Packit c32a2d
{
Packit c32a2d
    qsa_internal_t* userptr;
Packit c32a2d
Packit c32a2d
    userptr=ao->userptr;
Packit c32a2d
    if (userptr!=NULL);
Packit c32a2d
    {
Packit c32a2d
        snd_pcm_close(userptr->audio_handle);
Packit c32a2d
        free(ao->userptr);
Packit c32a2d
    }
Packit c32a2d
Packit c32a2d
    return TRUE;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static int deinit_qsa(out123_handle* ao)
Packit c32a2d
{
Packit c32a2d
    return TRUE;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static int init_qsa(out123_handle* ao)
Packit c32a2d
{
Packit c32a2d
    if (ao==NULL) return -1;
Packit c32a2d
Packit c32a2d
    /* Set callbacks */
Packit c32a2d
    ao->open = open_qsa;
Packit c32a2d
    ao->flush = flush_qsa;
Packit c32a2d
    ao->write = write_qsa;
Packit c32a2d
    ao->get_formats = get_formats_qsa;
Packit c32a2d
    ao->close = close_qsa;
Packit c32a2d
    ao->deinit = deinit_qsa;
Packit c32a2d
Packit c32a2d
    /* Success */
Packit c32a2d
    return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/*
Packit c32a2d
    Module information data structure
Packit c32a2d
*/
Packit c32a2d
mpg123_module_t mpg123_output_module_info = {
Packit c32a2d
    /* api_version */    MPG123_MODULE_API_VERSION,
Packit c32a2d
    /* name */           "qsa",
Packit c32a2d
    /* description */    "Output audio using QNX Sound Architecture (QSA).",
Packit c32a2d
    /* revision */       "$Rev:$",
Packit c32a2d
    /* handle */         NULL,
Packit c32a2d
    /* init_output */    init_qsa,
Packit c32a2d
};