Merge branch 'main' of https://git.dijkewijk.nl/hans/racket-sound-lib
This commit is contained in:
+133
-148
@@ -128,12 +128,21 @@
|
|||||||
#include <ao/ao.h>
|
#include <ao/ao.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
#ifdef AO_ASYNC_WINDOWS
|
||||||
|
#else
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
* Internal data structures
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PLAY = 1,
|
PLAY = 1,
|
||||||
STOP = 2
|
STOP = 2
|
||||||
} Command_t;
|
} Command_t;
|
||||||
|
|
||||||
|
|
||||||
typedef struct _queue_ {
|
typedef struct _queue_ {
|
||||||
Command_t command;
|
Command_t command;
|
||||||
void *buf;
|
void *buf;
|
||||||
@@ -189,6 +198,16 @@ typedef struct {
|
|||||||
int volume_in_10000; // volume in 10000 steps, i.e. 10000 equals 100%
|
int volume_in_10000; // volume in 10000 steps, i.e. 10000 equals 100%
|
||||||
} AO_Handle;
|
} AO_Handle;
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
* Forward function declarations
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
static void del_elem(Queue_t *q);
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
* Support functions, for internal state, elements and queuing
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
static inline int stopped(AO_Handle *h, int lock_mutex)
|
static inline int stopped(AO_Handle *h, int lock_mutex)
|
||||||
{
|
{
|
||||||
if (lock_mutex) MUTEX_LOCK(h->mutex);
|
if (lock_mutex) MUTEX_LOCK(h->mutex);
|
||||||
@@ -197,8 +216,6 @@ static inline int stopped(AO_Handle *h, int lock_mutex)
|
|||||||
return stopped;
|
return stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void del_elem(Queue_t *q);
|
|
||||||
|
|
||||||
static Queue_t *get(AO_Handle *h, int ms_wait)
|
static Queue_t *get(AO_Handle *h, int ms_wait)
|
||||||
{
|
{
|
||||||
Queue_t *q = NULL;
|
Queue_t *q = NULL;
|
||||||
@@ -328,57 +345,26 @@ static void clear(AO_Handle *h, int do_stop)
|
|||||||
MUTEX_UNLOCK(h->clear_mutex);
|
MUTEX_UNLOCK(h->clear_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
* Calculations on samples
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
static int inline littleEndian()
|
static int inline littleEndian()
|
||||||
{
|
{
|
||||||
int n = 1;
|
int n = 1;
|
||||||
return (*(char *)&n) == 1;
|
return (*(char *)&n) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static inline int ao_format_little_endian(int endianess)
|
||||||
static void inline adjustVolume(AO_Handle *handle, char *_buf, int buf_size, int volume_in_10000)
|
|
||||||
{
|
{
|
||||||
int bytes_per_sample = (handle->dev_bits_per_sample >> 3);
|
if (endianess == AO_FMT_LITTLE) return TRUE;
|
||||||
register int endianess = handle->dev_endianess;
|
if (endianess == AO_FMT_BIG) return FALSE;
|
||||||
register unsigned char *buf = (unsigned char *) _buf;
|
|
||||||
|
|
||||||
register int i;
|
/*
|
||||||
register long long sample;
|
AO_FMT_NATIVE, or anything treated as native.
|
||||||
register int k;
|
|
||||||
|
|
||||||
int little_endian = (endianess == AO_FMT_LITTLE);
|
|
||||||
if (!little_endian && endianess == AO_FMT_NATIVE) little_endian = littleEndian();
|
|
||||||
|
|
||||||
for(i = 0; i < buf_size; i += bytes_per_sample) {
|
|
||||||
if (little_endian) {
|
|
||||||
sample = buf[bytes_per_sample + i - 1] > 127 ? -1 : 0;
|
|
||||||
for(k = bytes_per_sample + i - 1; k >= i; k--) {
|
|
||||||
sample <<= 8;
|
|
||||||
sample |= buf[k];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sample = (buf[i] > 127) ? -1 : 0;
|
|
||||||
for(k = i; k < (i + bytes_per_sample); k++) {
|
|
||||||
sample <<= 8;
|
|
||||||
sample += buf[k];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sample *= volume_in_10000;
|
|
||||||
sample /= 10000;
|
|
||||||
if (little_endian) {
|
|
||||||
for(k = i; k < (i + bytes_per_sample); k++) {
|
|
||||||
buf[k] = sample&0xff;
|
|
||||||
sample >>= 8;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for(k = i + bytes_per_sample - 1; k >= i; k--) {
|
|
||||||
buf[k] = sample&0xff;
|
|
||||||
sample >>= 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
*/
|
||||||
|
return littleEndian();
|
||||||
|
}
|
||||||
|
|
||||||
static inline int32_t read_sample(unsigned char *mem, int req_bytes, int little_endian)
|
static inline int32_t read_sample(unsigned char *mem, int req_bytes, int little_endian)
|
||||||
{
|
{
|
||||||
@@ -408,6 +394,17 @@ static inline void store_sample(unsigned char *dst, int32_t in_sample, int out_b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**** Volume adjustment */
|
||||||
|
|
||||||
|
static inline int32_t limit_sample(int64_t sample, int bits)
|
||||||
|
{
|
||||||
|
int64_t min = -((int64_t)1 << (bits - 1));
|
||||||
|
int64_t max = ((int64_t)1 << (bits - 1)) - 1;
|
||||||
|
|
||||||
|
if (sample < min) return (int32_t) min;
|
||||||
|
if (sample > max) return (int32_t) max;
|
||||||
|
return (int32_t) sample;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void adjustVolume(AO_Handle *handle,
|
static inline void adjustVolume(AO_Handle *handle,
|
||||||
char *_buf,
|
char *_buf,
|
||||||
@@ -427,10 +424,80 @@ static inline void adjustVolume(AO_Handle *handle,
|
|||||||
for (int i = 0; i + bytes_per_sample <= buf_size; i += bytes_per_sample) {
|
for (int i = 0; i + bytes_per_sample <= buf_size; i += bytes_per_sample) {
|
||||||
int32_t sample = read_sample(buf + i, bytes_per_sample, little_endian);
|
int32_t sample = read_sample(buf + i, bytes_per_sample, little_endian);
|
||||||
int64_t scaled = ((int64_t) sample * volume_in_10000) / 10000;
|
int64_t scaled = ((int64_t) sample * volume_in_10000) / 10000;
|
||||||
store_sample(buf + i, (int32_t) scaled, bytes_per_sample, little_endian);
|
int32_t clipped = limit_sample(scaled, handle->dev_bits_per_sample);
|
||||||
|
store_sample(buf + i, clipped, bytes_per_sample, little_endian);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**** Bit conversion from input samples to device bits */
|
||||||
|
|
||||||
|
static inline int32_t convert_bits(int32_t sample, int req_bits, int out_bits)
|
||||||
|
{
|
||||||
|
if (req_bits == out_bits) return sample;
|
||||||
|
|
||||||
|
int shift = req_bits > out_bits
|
||||||
|
? req_bits - out_bits
|
||||||
|
: out_bits - req_bits;
|
||||||
|
|
||||||
|
if (req_bits > out_bits) {
|
||||||
|
return sample / (1 << shift);
|
||||||
|
} else {
|
||||||
|
return (int32_t)((int64_t)sample * ((int64_t)1 << shift));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *convert_req_to_real(AO_Handle *h, void *mem, int mem_size, BufferInfo_t *info, int *out_size)
|
||||||
|
{
|
||||||
|
if (mem_size <= 0) {
|
||||||
|
*out_size = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int output_little_endian = ao_format_little_endian(h->dev_endianess);
|
||||||
|
int input_little_endian = ao_format_little_endian(info->endiannes);
|
||||||
|
|
||||||
|
int requested_bits = info->sample_bits;
|
||||||
|
int output_bits = h->dev_bits_per_sample;
|
||||||
|
|
||||||
|
int req_bytes = (requested_bits / 8);
|
||||||
|
int out_bytes = (output_bits / 8);
|
||||||
|
|
||||||
|
int samples = (mem_size / req_bytes);
|
||||||
|
|
||||||
|
unsigned char *buf_out = (unsigned char *) malloc((samples + 1) * out_bytes);
|
||||||
|
if (buf_out == NULL) {
|
||||||
|
fprintf(stderr, "Allocation of output buffer of %d samples of %d bits gives NULL", samples, output_bits);
|
||||||
|
*out_size = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requested_bits == output_bits && input_little_endian == output_little_endian) {
|
||||||
|
*out_size = mem_size;
|
||||||
|
memcpy(buf_out, mem, mem_size);
|
||||||
|
} else {
|
||||||
|
// convert samples.
|
||||||
|
|
||||||
|
int sample;
|
||||||
|
unsigned char *p_in = (unsigned char *) mem;
|
||||||
|
unsigned char *p_out = buf_out;
|
||||||
|
|
||||||
|
for(sample = 0; sample < samples; sample++, p_in += req_bytes, p_out += out_bytes) {
|
||||||
|
register int32_t smpl;
|
||||||
|
smpl = read_sample(p_in, req_bytes, input_little_endian);
|
||||||
|
int32_t out_smpl = convert_bits(smpl, requested_bits, output_bits);
|
||||||
|
store_sample(p_out, out_smpl, out_bytes, output_little_endian);
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_size = samples * out_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
* Play thread of queues samples
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
#if defined(USE_PTHREADS) || defined(USE_DISPATCH)
|
#if defined(USE_PTHREADS) || defined(USE_DISPATCH)
|
||||||
static void *run(void *arg)
|
static void *run(void *arg)
|
||||||
#endif
|
#endif
|
||||||
@@ -488,15 +555,18 @@ static DWORD run(LPVOID arg)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
* External API
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
/**** ffi version for ao_play_async and ffmpeg_audio */
|
||||||
|
|
||||||
int ao_async_version()
|
int ao_async_version()
|
||||||
{
|
{
|
||||||
return ffi_version();
|
return ffi_version();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef AO_ASYNC_WINDOWS
|
/**** ao initialization and shutdown */
|
||||||
#else
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef AO_ASYNC_WINDOWS
|
#ifdef AO_ASYNC_WINDOWS
|
||||||
static void at_exit_shutdown_ao(void)
|
static void at_exit_shutdown_ao(void)
|
||||||
@@ -517,6 +587,7 @@ static void init_ao()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**** Opening ao_devices */
|
||||||
|
|
||||||
static ao_device *try_open_device(int bits, int rate, int channels, int byte_format,
|
static ao_device *try_open_device(int bits, int rate, int channels, int byte_format,
|
||||||
const char *wav_file_output,
|
const char *wav_file_output,
|
||||||
@@ -556,84 +627,6 @@ static ao_device *try_open_device(int bits, int rate, int channels, int byte_for
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
static inline int32_t convert_bits(int32_t sample, int req_bits, int out_bits)
|
|
||||||
{
|
|
||||||
if (req_bits > out_bits) {
|
|
||||||
sample >>= (req_bits - out_bits);
|
|
||||||
} else if (req_bits < out_bits) {
|
|
||||||
sample <<= (out_bits - req_bits);
|
|
||||||
}
|
|
||||||
return sample;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
static inline int32_t convert_bits(int32_t sample, int req_bits, int out_bits)
|
|
||||||
{
|
|
||||||
if (req_bits == out_bits) return sample;
|
|
||||||
|
|
||||||
int shift = req_bits > out_bits
|
|
||||||
? req_bits - out_bits
|
|
||||||
: out_bits - req_bits;
|
|
||||||
|
|
||||||
if (req_bits > out_bits) {
|
|
||||||
return sample / (1 << shift);
|
|
||||||
} else {
|
|
||||||
return (int32_t)((int64_t)sample * ((int64_t)1 << shift));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void *convert_req_to_real(AO_Handle *h, void *mem, int mem_size, BufferInfo_t *info, int *out_size)
|
|
||||||
{
|
|
||||||
if (mem_size <= 0) {
|
|
||||||
*out_size = 0;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int endianess = h->dev_endianess;
|
|
||||||
int little_endian = (endianess == AO_FMT_LITTLE);
|
|
||||||
if (!little_endian && endianess == AO_FMT_NATIVE) little_endian = littleEndian();
|
|
||||||
|
|
||||||
int requested_bits = info->sample_bits; // h->requested_bits_per_sample;
|
|
||||||
int output_bits = h->dev_bits_per_sample;
|
|
||||||
|
|
||||||
int req_bytes = (requested_bits / 8);
|
|
||||||
int out_bytes = (output_bits / 8);
|
|
||||||
|
|
||||||
int samples = (mem_size / req_bytes);
|
|
||||||
|
|
||||||
unsigned char *buf_out = (unsigned char *) malloc((samples + 1) * out_bytes);
|
|
||||||
if (buf_out == NULL) {
|
|
||||||
fprintf(stderr, "Allocation of output buffer of %d samples of %d bits gives NULL", samples, output_bits);
|
|
||||||
*out_size = 0;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requested_bits == output_bits) {
|
|
||||||
*out_size = mem_size;
|
|
||||||
memcpy(buf_out, mem, mem_size);
|
|
||||||
} else {
|
|
||||||
// convert samples.
|
|
||||||
|
|
||||||
int sample;
|
|
||||||
unsigned char *p_in = (unsigned char *) mem;
|
|
||||||
unsigned char *p_out = buf_out;
|
|
||||||
|
|
||||||
for(sample = 0; sample < samples; sample++, p_in += req_bytes, p_out += out_bytes) {
|
|
||||||
register int32_t smpl;
|
|
||||||
smpl = read_sample(p_in, req_bytes, little_endian);
|
|
||||||
int32_t out_smpl = convert_bits(smpl, requested_bits, output_bits);
|
|
||||||
store_sample(p_out, out_smpl, out_bytes, little_endian);
|
|
||||||
}
|
|
||||||
|
|
||||||
*out_size = samples * out_bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *ao_create_async(int bits, int rate, int channels, int byte_format, const char *wav_file_output)
|
void *ao_create_async(int bits, int rate, int channels, int byte_format, const char *wav_file_output)
|
||||||
{
|
{
|
||||||
init_ao();
|
init_ao();
|
||||||
@@ -758,27 +751,8 @@ void ao_stop_async(void *ao_handle)
|
|||||||
fprintf(stderr, "async handle freed\n");
|
fprintf(stderr, "async handle freed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
static inline void make_sample_bytes(int32_t sample, int bytes_per_sample, int big_endian, unsigned char b[4])
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < bytes_per_sample; i++) {
|
|
||||||
b[i] = sample&0xff;
|
|
||||||
sample >>= 8;
|
|
||||||
}
|
|
||||||
if (big_endian) {
|
|
||||||
unsigned char b1[4] = { 0, 0, 0, 0 };
|
|
||||||
for(i = 0; i < bytes_per_sample; i++) {
|
|
||||||
b1[bytes_per_sample - i - 1] = b[i];
|
|
||||||
}
|
|
||||||
for(i = 0; i < bytes_per_sample; i++) {
|
|
||||||
b[i] = b1[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
/**** play a sample */
|
||||||
|
|
||||||
void ao_play_async(void *ao_handle, int music_id, double at_second, double music_duration, int buf_size, void *mem, BufferInfo_t info)
|
void ao_play_async(void *ao_handle, int music_id, double at_second, double music_duration, int buf_size, void *mem, BufferInfo_t info)
|
||||||
{
|
{
|
||||||
@@ -804,6 +778,8 @@ void ao_play_async(void *ao_handle, int music_id, double at_second, double music
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**** clear the sample queue */
|
||||||
|
|
||||||
void ao_clear_async(void *ao_handle)
|
void ao_clear_async(void *ao_handle)
|
||||||
{
|
{
|
||||||
AO_Handle *h = (AO_Handle *) ao_handle;
|
AO_Handle *h = (AO_Handle *) ao_handle;
|
||||||
@@ -811,6 +787,8 @@ void ao_clear_async(void *ao_handle)
|
|||||||
clear(h, FALSE);
|
clear(h, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**** Information on the current samples being played by the player thread */
|
||||||
|
|
||||||
double ao_is_at_second_async(void *ao_handle)
|
double ao_is_at_second_async(void *ao_handle)
|
||||||
{
|
{
|
||||||
AO_Handle *h = (AO_Handle *) ao_handle;
|
AO_Handle *h = (AO_Handle *) ao_handle;
|
||||||
@@ -847,6 +825,8 @@ int ao_bufsize_async(void *ao_handle)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**** Volume related functions */
|
||||||
|
|
||||||
void ao_set_volume_async(void *ao_handle, double percentage)
|
void ao_set_volume_async(void *ao_handle, double percentage)
|
||||||
{
|
{
|
||||||
AO_Handle *h = (AO_Handle *) ao_handle;
|
AO_Handle *h = (AO_Handle *) ao_handle;
|
||||||
@@ -869,6 +849,8 @@ double ao_volume_async(void *ao_handle)
|
|||||||
return volume;
|
return volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**** Pausing playback */
|
||||||
|
|
||||||
void ao_pause_async(void *ao_handle, int paused)
|
void ao_pause_async(void *ao_handle, int paused)
|
||||||
{
|
{
|
||||||
AO_Handle *h = (AO_Handle *) ao_handle;
|
AO_Handle *h = (AO_Handle *) ao_handle;
|
||||||
@@ -889,6 +871,8 @@ void ao_pause_async(void *ao_handle, int paused)
|
|||||||
MUTEX_UNLOCK(h->mutex);
|
MUTEX_UNLOCK(h->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**** Information about the output device */
|
||||||
|
|
||||||
int ao_real_output_bits_async(void *handle)
|
int ao_real_output_bits_async(void *handle)
|
||||||
{
|
{
|
||||||
AO_Handle *h = (AO_Handle *) handle;
|
AO_Handle *h = (AO_Handle *) handle;
|
||||||
@@ -896,3 +880,4 @@ int ao_real_output_bits_async(void *handle)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,7 @@
|
|||||||
#define AOPLAYASYNC_EXPORT extern
|
#define AOPLAYASYNC_EXPORT extern
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define VERSION_MAJOR 1
|
#include "../ffi_version.h"
|
||||||
#define VERSION_MINOR 0
|
|
||||||
#define VERSION_PATCH 0
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ao = 1,
|
ao = 1,
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user