Clean up audio a bit. Less use of nasty statics and VLAs.

This commit is contained in:
Themaister 2011-01-14 15:34:38 +01:00
parent becfeb57eb
commit 6b21e6b8a3
5 changed files with 72 additions and 51 deletions

View File

@ -29,8 +29,7 @@
#include <string.h>
#include <assert.h>
#define FRAMES(x) (x / (sizeof(int16_t) * 2))
#define SAMPLES(x) (x / sizeof(int16_t))
#define FRAMES(x) (x / (sizeof(float) * 2))
typedef struct jack
{
@ -61,13 +60,6 @@ static int process_cb(jack_nframes_t nframes, void *data)
if (min_avail > nframes)
min_avail = nframes;
//static int underrun = 0;
//if (min_avail < nframes)
//{
// SSNES_LOG("JACK: Underrun count: %d\n", underrun++);
// fprintf(stderr, "required %d frames, got %d.\n", (int)nframes, (int)min_avail);
//}
for (int i = 0; i < 2; i++)
{
jack_default_audio_sample_t *out = jack_port_get_buffer(jd->ports[i], nframes);
@ -90,12 +82,6 @@ static void shutdown_cb(void *data)
pthread_cond_signal(&jd->cond);
}
static inline void s16_to_float(jack_default_audio_sample_t * restrict out, const int16_t * restrict in, size_t samples)
{
for (int i = 0; i < samples; i++)
out[i] = (float)in[i] / 0x8000;
}
static void parse_ports(const char **dest_ports, const char **jports)
{
int parsed = 0;
@ -142,7 +128,7 @@ static void* __jack_init(const char* device, int rate, int latency)
bufsize = (latency * g_settings.audio.out_rate / 1000) > jack_bufsize * 2 ? (latency * g_settings.audio.out_rate / 1000) : jack_bufsize * 2;
bufsize *= sizeof(jack_default_audio_sample_t);
//fprintf(stderr, "jack buffer size: %d\n", (int)bufsize);
SSNES_LOG("Jack buffer size: %d bytes: (~%.2f msec latency)\n", (int)bufsize, (float)bufsize * 1000 / (g_settings.audio.out_rate * sizeof(jack_default_audio_sample_t)));
for (int i = 0; i < 2; i++)
{
jd->buffer[i] = jack_ringbuffer_create(bufsize);
@ -181,7 +167,6 @@ static void* __jack_init(const char* device, int rate, int latency)
pthread_cond_init(&jd->cond, NULL);
pthread_mutex_init(&jd->cond_lock, NULL);
jack_free(jports);
return jd;
@ -191,17 +176,13 @@ error:
return NULL;
}
static size_t write_buffer(jack_t *jd, const void *buf, size_t size)
static size_t write_buffer(jack_t *jd, const float *buf, size_t size)
{
//fprintf(stderr, "write_buffer: size: %zu\n", size);
// Convert our data to float, deinterleave and write.
jack_default_audio_sample_t out_buffer[size / sizeof(int16_t)];
jack_default_audio_sample_t out_deinterleaved_buffer[2][FRAMES(size)];
s16_to_float(out_buffer, buf, SAMPLES(size));
for (int i = 0; i < 2; i++)
for (size_t j = 0; j < FRAMES(size); j++)
out_deinterleaved_buffer[i][j] = out_buffer[j * 2 + i];
out_deinterleaved_buffer[i][j] = buf[j * 2 + i];
for(;;)
{
@ -216,13 +197,11 @@ static size_t write_buffer(jack_t *jd, const void *buf, size_t size)
if (jd->nonblock)
{
if (min_avail < FRAMES(size) * sizeof(jack_default_audio_sample_t))
size = min_avail * 2 * sizeof(int16_t) / sizeof(jack_default_audio_sample_t);
size = min_avail * 2;
break;
}
else
{
//fprintf(stderr, "Write avail is: %d\n", (int)min_avail);
if (min_avail >= FRAMES(size) * sizeof(jack_default_audio_sample_t))
break;
}
@ -290,6 +269,7 @@ const audio_driver_t audio_jack = {
.start = __jack_start,
.set_nonblock_state = __jack_set_nonblock_state,
.free = __jack_free,
.float_samples = true,
.ident = "jack"
};

View File

@ -21,6 +21,7 @@
#include <stdio.h>
#include <string.h>
#include "hqflt/filters.h"
#include <assert.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
@ -128,6 +129,9 @@ void uninit_drivers(void)
uninit_audio();
}
#define AUDIO_CHUNK_SIZE_BLOCKING 64
#define AUDIO_CHUNK_SIZE_NONBLOCKING 2048 // So we don't get complete line-noise when fast-forwarding audio.
#define AUDIO_MAX_RATIO 16
void init_audio(void)
{
if (!g_settings.audio.enable)
@ -138,17 +142,35 @@ void init_audio(void)
find_audio_driver();
g_extern.audio_data.block_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING;
g_extern.audio_data.nonblock_chunk_size = AUDIO_CHUNK_SIZE_NONBLOCKING;
driver.audio_data = driver.audio->init(strlen(g_settings.audio.device) ? g_settings.audio.device : NULL, g_settings.audio.out_rate, g_settings.audio.latency);
if ( driver.audio_data == NULL )
g_extern.audio_active = false;
if (!g_settings.audio.sync && g_extern.audio_active)
{
driver.audio->set_nonblock_state(driver.audio_data, true);
g_extern.audio_data.chunk_size = g_extern.audio_data.nonblock_chunk_size;
}
else
g_extern.audio_data.chunk_size = g_extern.audio_data.block_chunk_size;
int err;
g_extern.source = src_new(g_settings.audio.src_quality, 2, &err);
if (!g_extern.source)
g_extern.audio_active = false;
size_t max_bufsamples = g_extern.audio_data.block_chunk_size > g_extern.audio_data.nonblock_chunk_size ?
g_extern.audio_data.block_chunk_size : g_extern.audio_data.nonblock_chunk_size;
assert(g_settings.audio.out_rate < g_settings.audio.in_rate * AUDIO_MAX_RATIO);
assert((g_extern.audio_data.data = malloc(max_bufsamples * sizeof(float))));
g_extern.audio_data.data_ptr = 0;
assert((g_extern.audio_data.outsamples = malloc(max_bufsamples * sizeof(float) * AUDIO_MAX_RATIO)));
assert((g_extern.audio_data.conv_outsamples = malloc(max_bufsamples * sizeof(int16_t) * AUDIO_MAX_RATIO)));
}
void uninit_audio(void)
@ -164,6 +186,10 @@ void uninit_audio(void)
if ( g_extern.source )
src_delete(g_extern.source);
free(g_extern.audio_data.data); g_extern.audio_data.data = NULL;
free(g_extern.audio_data.outsamples); g_extern.audio_data.outsamples = NULL;
free(g_extern.audio_data.conv_outsamples); g_extern.audio_data.conv_outsamples = NULL;
}
void init_video_input(void)

View File

@ -61,6 +61,7 @@ typedef struct audio_driver
bool (*start)(void* data);
void (*set_nonblock_state)(void* data, bool toggle); // Should we care about blocking in audio thread? Fast forwarding.
void (*free)(void* data);
bool float_samples; // Defines if driver will take standard floating point samples, or int16_t samples.
const char *ident;
} audio_driver_t;

View File

@ -117,6 +117,18 @@ struct global
char savefile_name_bsrm[512];
char savestate_name[256];
struct
{
float *data;
size_t data_ptr;
size_t chunk_size;
size_t nonblock_chunk_size;
size_t block_chunk_size;
float *outsamples;
int16_t *conv_outsamples;
} audio_data;
#ifdef HAVE_FFMPEG
ffemu_t *rec;
char record_path[256];

48
ssnes.c
View File

@ -40,10 +40,6 @@ struct global g_extern = {
};
// To avoid continous switching if we hold the button down, we require that the button must go from pressed, unpressed back to pressed to be able to toggle between then.
#define AUDIO_CHUNK_SIZE_BLOCKING 64
#define AUDIO_CHUNK_SIZE_NONBLOCKING 2048 // So we don't get complete line-noise when fast-forwarding audio.
static size_t audio_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING;
static void set_fast_forward_button(bool new_button_state)
{
static bool old_button_state = false;
@ -55,10 +51,11 @@ static void set_fast_forward_button(bool new_button_state)
driver.video->set_nonblock_state(driver.video_data, syncing_state);
if (g_extern.audio_active)
driver.audio->set_nonblock_state(driver.audio_data, (g_settings.audio.sync) ? syncing_state : true);
if (syncing_state)
audio_chunk_size = AUDIO_CHUNK_SIZE_NONBLOCKING;
g_extern.audio_data.chunk_size = g_extern.audio_data.nonblock_chunk_size;
else
audio_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING;
g_extern.audio_data.chunk_size = g_extern.audio_data.block_chunk_size;
}
old_button_state = new_button_state;
}
@ -163,37 +160,42 @@ static void audio_sample(uint16_t left, uint16_t right)
}
#endif
static float data[AUDIO_CHUNK_SIZE_NONBLOCKING];
static int data_ptr = 0;
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(*(int16_t*)&left)/0x8000;
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(*(int16_t*)&right)/0x8000;
data[data_ptr++] = (float)(*(int16_t*)&left)/0x8000;
data[data_ptr++] = (float)(*(int16_t*)&right)/0x8000;
if ( data_ptr >= audio_chunk_size )
if (g_extern.audio_data.data_ptr >= g_extern.audio_data.chunk_size)
{
float outsamples[audio_chunk_size * 16];
int16_t temp_outsamples[audio_chunk_size * 16];
SRC_DATA src_data;
src_data.data_in = data;
src_data.data_out = outsamples;
src_data.input_frames = audio_chunk_size / 2;
src_data.output_frames = audio_chunk_size * 8;
src_data.data_in = g_extern.audio_data.data;
src_data.data_out = g_extern.audio_data.outsamples;
src_data.input_frames = g_extern.audio_data.chunk_size / 2;
src_data.output_frames = g_extern.audio_data.chunk_size * 8;
src_data.end_of_input = 0;
src_data.src_ratio = (double)g_settings.audio.out_rate / (double)g_settings.audio.in_rate;
src_process(g_extern.source, &src_data);
src_float_to_short_array(outsamples, temp_outsamples, src_data.output_frames_gen * 2);
if ( driver.audio->write(driver.audio_data, temp_outsamples, src_data.output_frames_gen * 4) < 0 )
if (driver.audio->float_samples)
{
if (driver.audio->write(driver.audio_data, g_extern.audio_data.outsamples, src_data.output_frames_gen * sizeof(float) * 2) < 0)
{
fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n");
g_extern.audio_active = false;
}
}
else
{
src_float_to_short_array(g_extern.audio_data.outsamples, g_extern.audio_data.conv_outsamples, src_data.output_frames_gen * 2);
data_ptr = 0;
if (driver.audio->write(driver.audio_data, g_extern.audio_data.conv_outsamples, src_data.output_frames_gen * sizeof(int16_t) * 2) < 0)
{
fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n");
g_extern.audio_active = false;
}
}
g_extern.audio_data.data_ptr = 0;
}
}