Skip to content

Commit

Permalink
(audio) Fixes and improvements
Browse files Browse the repository at this point in the history
- Fixes segfault by implementing thread safe audio.
- Makes the audio code a little more valgrind friendly by reusing
  allocated memory in mixer_render()
- Fix a memory/fd leak.
  • Loading branch information
heuripedes committed Nov 11, 2015
1 parent 9e39edf commit d897212
Show file tree
Hide file tree
Showing 13 changed files with 1,468 additions and 17 deletions.
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -51,7 +51,7 @@ ifeq ($(platform), unix)
SHARED := -shared -Wl,--no-as-needed,--no-undefined
LUA_SYSCFLAGS := -DLUA_USE_POSIX
HAVE_INOTIFY=1
LDFLAGS += -Wl,-E
LDFLAGS += -Wl,-E -pthread

ifeq ($(ARCH), $(filter $(ARCH), intel))
WANT_JIT = 1
Expand Down
3 changes: 2 additions & 1 deletion Makefile.common
Expand Up @@ -20,7 +20,8 @@ SOURCES_C += $(CORE_DIR)/libretro.c \
SOURCES_C += $(CORE_DIR)/libretro-common/formats/png/rpng_decode.c \
$(CORE_DIR)/libretro-common/formats/png/rpng_fbio.c \
$(CORE_DIR)/libretro-common/file/file_path.c \
$(CORE_DIR)/libretro-common/compat/compat.c
$(CORE_DIR)/libretro-common/compat/compat.c \
$(CORE_DIR)/libretro-common/rthreads/rthreads.c

ifeq ($(WANT_ZLIB),1)
SOURCES_C += $(CORE_DIR)/deps/zlib/adler32.c \
Expand Down
104 changes: 91 additions & 13 deletions audio.c
@@ -1,29 +1,83 @@
#include "audio.h"
#include "lutro.h"
#include "compat/strl.h"
#include "rthreads/rthreads.h"

#include <stdlib.h>
#include <string.h>

/* TODO/FIXME - no sound on big-endian */

static unsigned num_sources = 0;
static audio_Source** sources = NULL;
static audio_Source** sources = NULL;
static size_t sources_count = 0;
static size_t sources_allocated = 0;
static slock_t* sources_lock = NULL;

static float volume = 1.0;

static void add_source(audio_Source *src)
{
slock_lock(sources_lock);

if (sources_count == sources_allocated)
{
size_t new_size = (sources_allocated + 1) * 2;
sources = (audio_Source**)realloc(sources, new_size * sizeof(audio_Source*));
memset(&sources[sources_allocated], 0, (new_size - sources_allocated) * sizeof(audio_Source*));

sources_allocated = new_size;
}

sources[sources_count++] = src;

slock_unlock(sources_lock);
}

static void remove_source(const audio_Source *src)
{
unsigned i;

slock_lock(sources_lock);

for (i = 0; i < sources_count; ++i)
{
if (sources[i] == src)
{
memmove(&sources[i], sources[i+1], (sources_count-i-1) * sizeof(audio_Source*));
sources_count--;
break;
}
}

slock_unlock(sources_lock);
}

void mixer_render(int16_t *buffer)
{
uint8_t* rawsamples8 = NULL;
size_t rawsamples8_bps = 0;

// Clear buffer
memset(buffer, 0, AUDIO_FRAMES * 2 * sizeof(int16_t));

/* Audio subsystem not ready */
if (!sources_count || !sources)
return;

slock_lock(sources_lock);

// Loop over audio sources
for (unsigned i = 0; i < num_sources; i++)
for (unsigned i = 0; i < sources_count; i++)
{
if (sources[i]->state == AUDIO_STOPPED)
continue;

uint8_t* rawsamples8 = calloc(
AUDIO_FRAMES * sources[i]->bps, sizeof(uint8_t));
if (sources[i]->bps > rawsamples8_bps)
{
rawsamples8_bps = sources[i]->bps;
rawsamples8 = realloc(rawsamples8, rawsamples8_bps * AUDIO_FRAMES);
memset(rawsamples8, 0, rawsamples8_bps * AUDIO_FRAMES);
}

bool end = ! fread(rawsamples8,
sizeof(uint8_t),
Expand Down Expand Up @@ -51,8 +105,12 @@ void mixer_render(int16_t *buffer)
fseek(sources[i]->sndta.fp, WAV_HEADER_SIZE, SEEK_SET);
}

free(rawsamples8);
}

if (rawsamples8)
free(rawsamples8);

slock_unlock(sources_lock);
}

int lutro_audio_preload(lua_State *L)
Expand All @@ -77,15 +135,28 @@ int lutro_audio_preload(lua_State *L)

void lutro_audio_init()
{
sources_lock = slock_new();
}

void lutro_audio_deinit()
{
if (sources_lock)
slock_lock(sources_lock);

if (sources)
{
free(sources);
sources = NULL;
num_sources = 0;

sources = NULL;
sources_count = 0;
sources_allocated = 0;

if (sources_lock)
{
slock_t *tmp = sources_lock;
sources_lock = NULL;

slock_unlock(tmp);
slock_free(tmp);
}
}

Expand Down Expand Up @@ -126,9 +197,7 @@ int audio_newSource(lua_State *L)
self->state = AUDIO_STOPPED;
fseek(self->sndta.fp, 0, SEEK_END);

num_sources++;
sources = (audio_Source**)realloc(sources, num_sources * sizeof(audio_Source));
sources[num_sources-1] = self;
add_source(self);

if (luaL_newmetatable(L, "Source") != 0)
{
Expand Down Expand Up @@ -266,7 +335,16 @@ int source_getPitch(lua_State *L)
int source_gc(lua_State *L)
{
audio_Source* self = (audio_Source*)luaL_checkudata(L, 1, "Source");
(void)self;
remove_source(self);

self->state = AUDIO_STOPPED;

/* TODO: do this at SoundData's gc method */
if (self->sndta.fp)
fclose(self->sndta.fp);

self->sndta.fp = NULL;

return 0;
}

Expand Down
35 changes: 35 additions & 0 deletions libretro-common/include/rthreads/async_job.h
@@ -0,0 +1,35 @@
/* Copyright (C) 2010-2015 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (async_job.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef __LIBRETRO_SDK_ASYNC_JOB_H
#define __LIBRETRO_SDK_ASYNC_JOB_H

typedef struct async_job async_job_t;
typedef void (*async_task_t)(void *payload);

async_job_t *async_job_new(void);

void async_job_free(async_job_t *ajob);

int async_job_add(async_job_t *ajob, async_task_t task, void *payload);

#endif /* __LIBRETRO_SDK_ASYNC_JOB_H */
44 changes: 44 additions & 0 deletions libretro-common/include/rthreads/rsemaphore.h
@@ -0,0 +1,44 @@
/* Copyright (C) 2010-2015 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (rsemaphore.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef __LIBRETRO_SDK_SEMAPHORE_H
#define __LIBRETRO_SDK_SEMAPHORE_H

typedef struct ssem ssem_t;

/**
* ssem_create:
* @value : initial value for the semaphore
*
* Create a new semaphore.
*
* Returns: pointer to new semaphore if successful, otherwise NULL.
*/
ssem_t *ssem_new(int value);

void ssem_free(ssem_t *semaphore);

void ssem_wait(ssem_t *semaphore);

void ssem_signal(ssem_t *semaphore);

#endif /* __LIBRETRO_SDK_SEMAPHORE_H */

0 comments on commit d897212

Please sign in to comment.