RetroArch/audio/drivers/openal.c

278 lines
5.8 KiB
C
Raw Normal View History

2012-04-21 21:13:50 +00:00
/* RetroArch - A frontend for libretro.
2014-01-01 00:50:59 +00:00
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
2017-01-22 12:40:32 +00:00
* Copyright (C) 2011-2017 - Daniel De Matteis
*
2012-04-21 21:13:50 +00:00
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
2012-04-21 21:13:50 +00:00
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
2012-04-21 21:31:57 +00:00
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
2016-09-05 15:29:19 +00:00
#include <stdlib.h>
2015-09-14 01:44:06 +00:00
#include <string.h>
#include <time.h>
2011-02-04 13:42:26 +00:00
#ifdef __APPLE__
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
2011-02-04 13:42:26 +00:00
#endif
#ifdef _WIN32
#include <windows.h>
#endif
#include <retro_miscellaneous.h>
2017-06-28 02:41:38 +00:00
#include <retro_timers.h>
2019-06-17 10:49:21 +00:00
#include "../../retroarch.h"
2015-11-23 11:03:38 +00:00
#include "../../verbosity.h"
2015-09-14 01:44:06 +00:00
2013-02-11 19:14:12 +00:00
#define BUFSIZE 1024
typedef struct al
{
ALuint source;
ALuint *buffers;
ALuint *res_buf;
ALCdevice *handle;
ALCcontext *ctx;
2013-02-11 19:14:12 +00:00
size_t res_ptr;
size_t num_buffers;
size_t tmpbuf_ptr;
int rate;
ALenum format;
uint8_t tmpbuf[BUFSIZE];
bool nonblock;
bool is_paused;
} al_t;
2013-02-11 19:14:12 +00:00
static void al_free(void *data)
{
al_t *al = (al_t*)data;
2015-01-12 04:05:56 +00:00
2013-02-11 19:14:12 +00:00
if (!al)
return;
alSourceStop(al->source);
alDeleteSources(1, &al->source);
if (al->buffers)
alDeleteBuffers(al->num_buffers, al->buffers);
free(al->buffers);
free(al->res_buf);
alcMakeContextCurrent(NULL);
2013-02-11 19:38:16 +00:00
if (al->ctx)
alcDestroyContext(al->ctx);
if (al->handle)
alcCloseDevice(al->handle);
2013-02-11 19:14:12 +00:00
free(al);
}
static void *al_init(const char *device, unsigned rate, unsigned latency,
unsigned block_frames,
unsigned *new_rate)
{
2015-01-12 04:05:56 +00:00
al_t *al;
(void)device;
2015-01-12 04:05:56 +00:00
al = (al_t*)calloc(1, sizeof(al_t));
2011-10-15 10:54:47 +00:00
if (!al)
return NULL;
al->handle = alcOpenDevice(NULL);
2015-01-12 04:05:56 +00:00
if (!al->handle)
goto error;
al->ctx = alcCreateContext(al->handle, NULL);
2015-01-12 04:05:56 +00:00
if (!al->ctx)
goto error;
alcMakeContextCurrent(al->ctx);
al->rate = rate;
2015-01-12 04:05:56 +00:00
/* We already use one buffer for tmpbuf. */
2013-02-11 19:14:12 +00:00
al->num_buffers = (latency * rate * 2 * sizeof(int16_t)) / (1000 * BUFSIZE) - 1;
if (al->num_buffers < 2)
al->num_buffers = 2;
RARCH_LOG("[OpenAL]: Using %u buffers of %u bytes.\n", (unsigned)al->num_buffers, BUFSIZE);
2011-12-24 12:46:12 +00:00
al->buffers = (ALuint*)calloc(al->num_buffers, sizeof(ALuint));
al->res_buf = (ALuint*)calloc(al->num_buffers, sizeof(ALuint));
2020-01-04 09:39:33 +00:00
if (!al->buffers || !al->res_buf)
goto error;
2010-08-25 20:42:09 +00:00
alGenSources(1, &al->source);
alGenBuffers(al->num_buffers, al->buffers);
2010-08-25 20:53:52 +00:00
memcpy(al->res_buf, al->buffers, al->num_buffers * sizeof(ALuint));
al->res_ptr = al->num_buffers;
return al;
error:
2013-02-11 19:14:12 +00:00
al_free(al);
return NULL;
}
static bool al_unqueue_buffers(al_t *al)
{
ALint val;
alGetSourcei(al->source, AL_BUFFERS_PROCESSED, &val);
2015-01-12 04:05:56 +00:00
if (val <= 0)
return false;
2015-01-12 04:05:56 +00:00
alSourceUnqueueBuffers(al->source, val, &al->res_buf[al->res_ptr]);
al->res_ptr += val;
return true;
}
static bool al_get_buffer(al_t *al, ALuint *buffer)
{
2013-02-11 19:14:12 +00:00
if (!al->res_ptr)
{
2011-10-15 10:56:48 +00:00
for (;;)
{
if (al_unqueue_buffers(al))
break;
2010-08-25 20:42:09 +00:00
if (al->nonblock)
return false;
2015-06-29 19:39:00 +00:00
/* Must sleep as there is no proper blocking method. */
2015-09-22 16:55:14 +00:00
retro_sleep(1);
}
}
*buffer = al->res_buf[--al->res_ptr];
return true;
}
2011-11-02 18:31:36 +00:00
static size_t al_fill_internal_buf(al_t *al, const void *buf, size_t size)
{
2016-03-01 23:07:31 +00:00
size_t read_size = MIN(BUFSIZE - al->tmpbuf_ptr, size);
memcpy(al->tmpbuf + al->tmpbuf_ptr, buf, read_size);
al->tmpbuf_ptr += read_size;
return read_size;
}
static ssize_t al_write(void *data, const void *buf_, size_t size)
{
2016-09-08 09:41:58 +00:00
al_t *al = (al_t*)data;
2013-02-11 19:14:12 +00:00
const uint8_t *buf = (const uint8_t*)buf_;
2016-09-08 09:41:58 +00:00
size_t written = 0;
2015-01-12 04:05:56 +00:00
2013-02-11 19:14:12 +00:00
while (size)
{
2015-01-12 04:05:56 +00:00
ALint val;
ALuint buffer;
2013-02-11 19:14:12 +00:00
size_t rc = al_fill_internal_buf(al, buf, size);
2015-01-12 04:05:56 +00:00
written += rc;
2013-02-11 19:14:12 +00:00
buf += rc;
size -= rc;
if (al->tmpbuf_ptr != BUFSIZE)
break;
2010-08-25 20:53:52 +00:00
if (!al_get_buffer(al, &buffer))
2013-02-11 19:14:12 +00:00
break;
2010-08-25 20:53:52 +00:00
alBufferData(buffer, AL_FORMAT_STEREO16, al->tmpbuf, BUFSIZE, al->rate);
al->tmpbuf_ptr = 0;
alSourceQueueBuffers(al->source, 1, &buffer);
if (alGetError() != AL_NO_ERROR)
return -1;
alGetSourcei(al->source, AL_SOURCE_STATE, &val);
if (val != AL_PLAYING)
alSourcePlay(al->source);
if (alGetError() != AL_NO_ERROR)
return -1;
}
2013-02-11 19:14:12 +00:00
return written;
}
static bool al_stop(void *data)
{
al_t *al = (al_t*)data;
if (al)
al->is_paused = true;
return true;
}
static bool al_alive(void *data)
{
al_t *al = (al_t*)data;
2015-01-12 04:05:56 +00:00
if (!al)
return false;
return !al->is_paused;
}
static void al_set_nonblock_state(void *data, bool state)
{
2011-12-24 12:46:12 +00:00
al_t *al = (al_t*)data;
2015-01-12 04:05:56 +00:00
if (al)
al->nonblock = state;
}
static bool al_start(void *data, bool is_shutdown)
{
al_t *al = (al_t*)data;
if (al)
al->is_paused = false;
return true;
}
2013-02-11 19:14:12 +00:00
static size_t al_write_avail(void *data)
{
2011-12-24 12:46:12 +00:00
al_t *al = (al_t*)data;
2013-02-11 19:14:12 +00:00
al_unqueue_buffers(al);
return al->res_ptr * BUFSIZE + (BUFSIZE - al->tmpbuf_ptr);
}
static size_t al_buffer_size(void *data)
{
al_t *al = (al_t*)data;
2015-01-12 04:05:56 +00:00
return (al->num_buffers + 1) * BUFSIZE; /* Also got tmpbuf. */
}
static bool al_use_float(void *data)
{
(void)data;
return false;
}
2014-09-11 05:06:20 +00:00
audio_driver_t audio_openal = {
2011-12-24 12:46:12 +00:00
al_init,
al_write,
al_stop,
al_start,
al_alive,
2011-12-24 12:46:12 +00:00
al_set_nonblock_state,
al_free,
al_use_float,
2013-02-11 19:14:12 +00:00
"openal",
NULL,
NULL,
2013-02-11 19:14:12 +00:00
al_write_avail,
al_buffer_size,
};