|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
openal.c: audio output on OpenAL
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
copyright 1995-2016 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 |
initially written by Taihei Monma
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Need usleep(). */
|
|
Packit |
c32a2d |
#define _DEFAULT_SOURCE
|
|
Packit |
c32a2d |
#define _BSD_SOURCE
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "out123_int.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#ifdef OPENAL_SUBDIR_OPENAL
|
|
Packit |
c32a2d |
#include <OpenAL/al.h>
|
|
Packit |
c32a2d |
#include <OpenAL/alc.h>
|
|
Packit |
c32a2d |
#elif defined(OPENAL_SUBDIR_AL)
|
|
Packit |
c32a2d |
#include <AL/al.h>
|
|
Packit |
c32a2d |
#include <AL/alc.h>
|
|
Packit |
c32a2d |
#else
|
|
Packit |
c32a2d |
#include <al.h>
|
|
Packit |
c32a2d |
#include <alc.h>
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
#include <errno.h>
|
|
Packit |
c32a2d |
#include <unistd.h>
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "debug.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#define NUM_BUFFERS 16
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#ifndef AL_FORMAT_MONO_FLOAT32
|
|
Packit |
c32a2d |
#define AL_FORMAT_MONO_FLOAT32 0x10010
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
#ifndef AL_FORMAT_STEREO_FLOAT32
|
|
Packit |
c32a2d |
#define AL_FORMAT_STEREO_FLOAT32 0x10011
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
typedef struct
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
ALCdevice *device;
|
|
Packit |
c32a2d |
ALCcontext *context;
|
|
Packit |
c32a2d |
ALuint source, buffer;
|
|
Packit |
c32a2d |
ALenum format;
|
|
Packit |
c32a2d |
ALsizei rate;
|
|
Packit |
c32a2d |
} mpg123_openal_t;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int open_openal(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
mpg123_openal_t* al = (mpg123_openal_t*)ao->userptr;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
al->device = alcOpenDevice(NULL);
|
|
Packit |
c32a2d |
al->context = alcCreateContext(al->device, NULL);
|
|
Packit |
c32a2d |
alcMakeContextCurrent(al->context);
|
|
Packit |
c32a2d |
alGenSources(1, &al->source);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
al->rate = ao->rate;
|
|
Packit |
c32a2d |
if(ao->format == MPG123_ENC_SIGNED_16 && ao->channels == 2) al->format = AL_FORMAT_STEREO16;
|
|
Packit |
c32a2d |
else if(ao->format == MPG123_ENC_SIGNED_16 && ao->channels == 1) al->format = AL_FORMAT_MONO16;
|
|
Packit |
c32a2d |
else if(ao->format == MPG123_ENC_UNSIGNED_8 && ao->channels == 2) al->format = AL_FORMAT_STEREO8;
|
|
Packit |
c32a2d |
else if(ao->format == MPG123_ENC_UNSIGNED_8 && ao->channels == 1) al->format = AL_FORMAT_MONO8;
|
|
Packit |
c32a2d |
else if(ao->format == MPG123_ENC_FLOAT_32 && ao->channels == 2) al->format = AL_FORMAT_STEREO_FLOAT32;
|
|
Packit |
c32a2d |
else if(ao->format == MPG123_ENC_FLOAT_32 && ao->channels == 1) al->format = AL_FORMAT_MONO_FLOAT32;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int get_formats_openal(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
return MPG123_ENC_SIGNED_16|MPG123_ENC_UNSIGNED_8|((alIsExtensionPresent((ALubyte*)"AL_EXT_float32") == AL_TRUE) ? MPG123_ENC_FLOAT_32 : 0);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int write_openal(out123_handle *ao, unsigned char *buf, int len)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
ALint state, n;
|
|
Packit |
c32a2d |
mpg123_openal_t* al = (mpg123_openal_t*)ao->userptr;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
alGetSourcei(al->source, AL_BUFFERS_QUEUED, &n);
|
|
Packit |
c32a2d |
if(n < NUM_BUFFERS)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
alGenBuffers(1, &al->buffer);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
alGetSourcei(al->source, AL_SOURCE_STATE, &state);
|
|
Packit |
c32a2d |
if(state != AL_PLAYING)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
alSourcePlay(al->source);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
while(alGetSourcei(al->source, AL_BUFFERS_PROCESSED, &n), n == 0)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
usleep(10000);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
alSourceUnqueueBuffers(al->source, 1, &al->buffer);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
alBufferData(al->buffer, al->format, buf, len, al->rate);
|
|
Packit |
c32a2d |
alSourceQueueBuffers(al->source, 1, &al->buffer);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
return len;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int close_openal(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
ALint state, n;
|
|
Packit |
c32a2d |
mpg123_openal_t* al = (mpg123_openal_t*)ao->userptr;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if (al)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* wait until all buffers are consumed */
|
|
Packit |
c32a2d |
while(alGetSourcei(al->source, AL_SOURCE_STATE, &state), state == AL_PLAYING)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
usleep(10000);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
/* free all processed buffers */
|
|
Packit |
c32a2d |
while(alGetSourcei(al->source, AL_BUFFERS_PROCESSED, &n), n > 0)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
alSourceUnqueueBuffers(al->source, 1, &al->buffer);
|
|
Packit |
c32a2d |
alDeleteBuffers(1, &al->buffer);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
alDeleteSources(1, &al->source);
|
|
Packit |
c32a2d |
alcMakeContextCurrent(NULL);
|
|
Packit |
c32a2d |
alcDestroyContext(al->context);
|
|
Packit |
c32a2d |
alcCloseDevice(al->device);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void flush_openal(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
ALint n;
|
|
Packit |
c32a2d |
mpg123_openal_t* al = (mpg123_openal_t*)ao->userptr;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if (al)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* stop playing and flush all buffers */
|
|
Packit |
c32a2d |
alSourceStop(al->source);
|
|
Packit |
c32a2d |
while(alGetSourcei(al->source, AL_BUFFERS_PROCESSED, &n), n > 0)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
alSourceUnqueueBuffers(al->source, 1, &al->buffer);
|
|
Packit |
c32a2d |
alDeleteBuffers(1, &al->buffer);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int deinit_openal(out123_handle* ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* Free up memory */
|
|
Packit |
c32a2d |
if(ao->userptr)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
free( ao->userptr );
|
|
Packit |
c32a2d |
ao->userptr = NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Success */
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int init_openal(out123_handle* ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if (ao==NULL) return -1;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Set callbacks */
|
|
Packit |
c32a2d |
ao->open = open_openal;
|
|
Packit |
c32a2d |
ao->flush = flush_openal;
|
|
Packit |
c32a2d |
ao->write = write_openal;
|
|
Packit |
c32a2d |
ao->get_formats = get_formats_openal;
|
|
Packit |
c32a2d |
ao->close = close_openal;
|
|
Packit |
c32a2d |
ao->deinit = deinit_openal;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Allocate memory for data structure */
|
|
Packit |
c32a2d |
ao->userptr = malloc( sizeof( mpg123_openal_t ) );
|
|
Packit |
c32a2d |
if(ao->userptr==NULL)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("failed to malloc memory for 'mpg123_openal_t'");
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
memset( ao->userptr, 0, sizeof(mpg123_openal_t) );
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Success */
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
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 */ "openal",
|
|
Packit |
c32a2d |
/* description */ "Output audio using OpenAL.",
|
|
Packit |
c32a2d |
/* revision */ "$Rev:$",
|
|
Packit |
c32a2d |
/* handle */ NULL,
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* init_output */ init_openal,
|
|
Packit |
c32a2d |
};
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|