|
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 |
};
|