Replaced queue with thread-safe version.
This commit is contained in:
parent
37f7d74c38
commit
1ed2ad182b
3747
src/libsegaapi/concurrentqueue.h
Normal file
3747
src/libsegaapi/concurrentqueue.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,192 +0,0 @@
|
|||||||
/*
|
|
||||||
* Filename: dqueue.c
|
|
||||||
* Date: 13/10/17
|
|
||||||
* Licence: GNU GPL V3
|
|
||||||
*
|
|
||||||
* Library for a generic, dynamically allocated queue
|
|
||||||
*
|
|
||||||
* Functions:
|
|
||||||
* queue_t * queue_init(unsigned int block_num, size_t block_size, size_t element_size); - Initialise the queue data structure and return a pointer to the first element
|
|
||||||
* void * queue_pop(queue_t * queue); - Pop an element from the front of the queue
|
|
||||||
* int queue_push(const void * const element, queue_t * queue); - Push an element to the back of the queue
|
|
||||||
* int queue_debug(const queue_t * const queue); - Dump information about the queue
|
|
||||||
* void queue_destroy(queue_t * queue); - Destroy the queue data structure
|
|
||||||
*
|
|
||||||
* Return/exit codes:
|
|
||||||
* QUEUE_OK - No error
|
|
||||||
* SIZE_ERROR - Queue size error (invalid block size or number of elements)
|
|
||||||
* MEM_ERROR - Memory allocation error
|
|
||||||
* INDEX_ERROR - Couldn't pop data from the queue
|
|
||||||
*
|
|
||||||
* Todo:
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dqueue.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
//#define _DEBUG
|
|
||||||
|
|
||||||
queue_t * queue_init(unsigned int block_num, size_t block_size, size_t element_width)
|
|
||||||
{
|
|
||||||
queue_t * queue;
|
|
||||||
unsigned int i, j;
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
printf("queue_init() block_num:%d block_size:%d element_width:%d\r\n",
|
|
||||||
block_num, block_size, element_width);
|
|
||||||
#endif
|
|
||||||
if(block_size == 0)
|
|
||||||
block_size = DEFAULT_BLOCK;
|
|
||||||
|
|
||||||
if(!(queue = (queue_t*)malloc(sizeof(queue_t))))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if((queue->block_size = block_size) <= 0 || (queue->total_blocks = block_num) <= 0 || (queue->element_width = element_width) <= 0) {
|
|
||||||
queue->status = SIZE_ERROR;
|
|
||||||
printf("queue_init() SIZE_ERROR\r\n");
|
|
||||||
return queue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!(queue->base_p = (char**)malloc(queue->total_blocks * sizeof(char *)))) {
|
|
||||||
queue->status = MEM_ERROR;
|
|
||||||
printf("queue_init() MEM_ERROR\r\n");
|
|
||||||
return queue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(i = 0; i < queue->total_blocks; i++) {
|
|
||||||
if(!(queue->base_p[i] = (char*)malloc(queue->block_size * queue->element_width))) {
|
|
||||||
fprintf(stderr, "Error: Could not allocate memory!\n");
|
|
||||||
|
|
||||||
for(j = 0; j < i; j++)
|
|
||||||
free(queue->base_p[i]);
|
|
||||||
|
|
||||||
free(queue->base_p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
queue->cur_block = queue->last_block = 0;
|
|
||||||
queue->cur_block_pos = queue->last_block_pos = 0;
|
|
||||||
queue->status = QUEUE_OK;
|
|
||||||
|
|
||||||
return queue;
|
|
||||||
}
|
|
||||||
|
|
||||||
void queue_destroy(queue_t * queue)
|
|
||||||
{
|
|
||||||
while(queue->cur_block < queue->total_blocks)
|
|
||||||
free(queue->base_p[queue->cur_block++]);
|
|
||||||
|
|
||||||
queue->cur_block = 0;
|
|
||||||
queue->cur_block_pos = 0;
|
|
||||||
queue->last_block = 0;
|
|
||||||
queue->last_block_pos = 0;
|
|
||||||
queue->total_blocks = 0;
|
|
||||||
queue->block_size = 0;
|
|
||||||
queue->element_width = 0;
|
|
||||||
queue->status = 0;
|
|
||||||
|
|
||||||
free(queue->base_p);
|
|
||||||
queue->base_p = NULL;
|
|
||||||
free(queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
int queue_push(const void * const element, queue_t * queue)
|
|
||||||
{
|
|
||||||
memcpy(queue->base_p[queue->last_block] + queue->last_block_pos * queue->element_width, element, queue->element_width);
|
|
||||||
|
|
||||||
if(queue->last_block == (queue->total_blocks - queue->cur_block) - 1 && queue->last_block_pos == queue->block_size - 1) {
|
|
||||||
queue->total_blocks++;
|
|
||||||
queue->last_block++;
|
|
||||||
queue->last_block_pos = 0;
|
|
||||||
|
|
||||||
if(!(queue->base_p = (char**)realloc(queue->base_p, (queue->total_blocks - queue->cur_block) * sizeof(void *)))) {
|
|
||||||
fprintf(stderr, "Error: Could not reallocate memory!\n");
|
|
||||||
queue->status = MEM_ERROR;
|
|
||||||
queue->total_blocks--;
|
|
||||||
queue->last_block--;
|
|
||||||
queue->last_block_pos = queue->block_size - 1;
|
|
||||||
return MEM_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!(queue->base_p[queue->last_block] = (char*)malloc(queue->block_size * queue->element_width))) {
|
|
||||||
fprintf(stderr, "Error: Could not allocate memory!\n");
|
|
||||||
queue->total_blocks--;
|
|
||||||
queue->last_block--;
|
|
||||||
queue->last_block_pos = queue->block_size - 1;
|
|
||||||
queue->status = MEM_ERROR;
|
|
||||||
return MEM_ERROR;
|
|
||||||
}
|
|
||||||
} else if(queue->last_block_pos == queue->block_size - 1) {
|
|
||||||
queue->last_block++;
|
|
||||||
queue->last_block_pos = 0;
|
|
||||||
} else {
|
|
||||||
queue->last_block_pos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return QUEUE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void * queue_pop(queue_t * queue)
|
|
||||||
{
|
|
||||||
void * data;
|
|
||||||
|
|
||||||
if(queue->last_block == queue->cur_block && queue->cur_block_pos == queue->last_block_pos) {
|
|
||||||
fprintf(stderr, "Error: Queue empty!\n");
|
|
||||||
queue->status = INDEX_ERROR;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!(data = malloc(queue->element_width))) {
|
|
||||||
fprintf(stderr, "Error: Could not allocate memory!\n");
|
|
||||||
queue->status = MEM_ERROR;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(queue->cur_block_pos == queue->block_size - 1) {
|
|
||||||
memcpy(data, queue->base_p[queue->cur_block] + queue->cur_block_pos * queue->element_width, queue->element_width);
|
|
||||||
free(queue->base_p[queue->cur_block]);
|
|
||||||
|
|
||||||
queue->cur_block++;
|
|
||||||
queue->cur_block_pos = 0;
|
|
||||||
} else {
|
|
||||||
memcpy(data, queue->base_p[queue->cur_block] + queue->cur_block_pos * queue->element_width, queue->element_width);
|
|
||||||
queue->cur_block_pos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
int queue_isempty(const queue_t * const queue)
|
|
||||||
{
|
|
||||||
int result = 1;
|
|
||||||
if(queue == NULL) {
|
|
||||||
printf("Error: Invalid queue pointer!\n");
|
|
||||||
return MEM_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = (queue->total_blocks - queue->cur_block) == queue->total_blocks;
|
|
||||||
#ifdef _DEBUG
|
|
||||||
printf("queue_isempty() %i - %i -> %i\r\n",
|
|
||||||
queue->total_blocks, queue->cur_block, result);
|
|
||||||
#endif
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int queue_debug(const queue_t * const queue)
|
|
||||||
{
|
|
||||||
if(queue == NULL) {
|
|
||||||
printf("Error: Invalid queue pointer!\n");
|
|
||||||
return MEM_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(queue->status == QUEUE_OK)
|
|
||||||
printf("Queue has %d blocks of size %d and each element is %d bytes wide!\n", (queue->total_blocks - queue->cur_block), (int)queue->block_size, (int)queue->element_width);
|
|
||||||
else if(queue->status == MEM_ERROR)
|
|
||||||
printf("Memory error in queue!\n");
|
|
||||||
else if(queue->status == SIZE_ERROR)
|
|
||||||
printf("Size error in queue");
|
|
||||||
|
|
||||||
return queue->status;
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
#ifndef DQUEUE_H
|
|
||||||
#define DQUEUE_H
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define QUEUE_OK 0
|
|
||||||
#define MEM_ERROR -1 /* Memory allocation error */
|
|
||||||
#define SIZE_ERROR -2 /* Queue dimension error */
|
|
||||||
#define INDEX_ERROR -3 /* No data at index */
|
|
||||||
|
|
||||||
#define DEFAULT_BLOCK 256 /* By default use 256 bytes per block */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char ** base_p; /* Base pointer of the queue */
|
|
||||||
unsigned int cur_block; /* Index of the block containing the first element */
|
|
||||||
unsigned int cur_block_pos; /* Position of the first element within the block */
|
|
||||||
unsigned int last_block; /* Index of the block containing the last element */
|
|
||||||
unsigned int last_block_pos; /* Position of the last element within the block */
|
|
||||||
unsigned int total_blocks; /* Total number of blocks ever allocated to the queue */
|
|
||||||
size_t block_size; /* Number of elements in each block */
|
|
||||||
size_t element_width; /* Size of each element */
|
|
||||||
int status; /* Status of the queue */
|
|
||||||
} queue_t;
|
|
||||||
|
|
||||||
queue_t * queue_init(unsigned int block_num, size_t block_size, size_t element_size); /* Initialise the queue data structure and return a pointer to the first element */
|
|
||||||
void * queue_pop(queue_t * queue); /* Pop an element from the front of the queue */
|
|
||||||
int queue_push(const void * const element, queue_t * queue); /* Push an element to the back of the queue */
|
|
||||||
int queue_debug(const queue_t * const queue); /* Dump information about the queue */
|
|
||||||
void queue_destroy(queue_t * queue); /* Destroy the queue data structure */
|
|
||||||
int queue_isempty(const queue_t * const queue);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // DQUEUE_H
|
|
@ -15,7 +15,8 @@
|
|||||||
|
|
||||||
#include "opensegaapi.h"
|
#include "opensegaapi.h"
|
||||||
#include "segaerr.h"
|
#include "segaerr.h"
|
||||||
#include "dqueue.h"
|
// https://github.com/cameron314/concurrentqueue
|
||||||
|
#include "concurrentqueue.h"
|
||||||
|
|
||||||
//#define _DEBUG
|
//#define _DEBUG
|
||||||
|
|
||||||
@ -135,7 +136,7 @@ __inline float FAudioSemitonesToFrequencyRatio(float Semitones)
|
|||||||
class FAudio_BufferNotify : public FAudioVoiceCallback {
|
class FAudio_BufferNotify : public FAudioVoiceCallback {
|
||||||
public:
|
public:
|
||||||
OPEN_segaapiBuffer_t* buffer = NULL;
|
OPEN_segaapiBuffer_t* buffer = NULL;
|
||||||
queue_t * defers = NULL;
|
moodycamel::ConcurrentQueue<uint32_t> defers;
|
||||||
FAudioSourceVoice* xaVoice;
|
FAudioSourceVoice* xaVoice;
|
||||||
|
|
||||||
FAudio_BufferNotify() {
|
FAudio_BufferNotify() {
|
||||||
@ -152,29 +153,27 @@ public:
|
|||||||
private:
|
private:
|
||||||
void SignalBufferEnd()
|
void SignalBufferEnd()
|
||||||
{
|
{
|
||||||
dbgprint("SignalBufferEnd()");
|
dbgprint("SignalBufferEnd() size = %ld", defers.size_approx());
|
||||||
FAudioSourceVoice* returned_voice;
|
uint32_t returned_samplerate;
|
||||||
|
|
||||||
while (!queue_isempty(defers))
|
while (defers.size_approx() > 0)
|
||||||
{
|
{
|
||||||
FAudioVoiceState vs;
|
FAudioVoiceState vs;
|
||||||
|
|
||||||
FAudioSourceVoice_GetState(xaVoice, &vs, 0);
|
FAudioSourceVoice_GetState(xaVoice, &vs, 0);
|
||||||
|
|
||||||
if (vs.BuffersQueued > 0)
|
if (vs.BuffersQueued > 0)
|
||||||
{
|
{
|
||||||
FAudioSourceVoice_FlushSourceBuffers(xaVoice);
|
FAudioSourceVoice_FlushSourceBuffers(xaVoice);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
returned_voice = (FAudioSourceVoice*)queue_pop(defers);
|
|
||||||
if (returned_voice)
|
|
||||||
{
|
|
||||||
dbgprint("SignalBufferEnd: voice = %08x", returned_voice);
|
|
||||||
FAudioSourceVoice_FlushSourceBuffers(returned_voice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (defers.try_dequeue(returned_samplerate))
|
||||||
|
{
|
||||||
|
dbgprint("SignalBufferEnd: samplerate: %d", returned_samplerate);
|
||||||
|
FAudioSourceVoice_SetSourceSampleRate(xaVoice, returned_samplerate);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void StaticOnBufferEnd(FAudioVoiceCallback* callback, void*)
|
static void StaticOnBufferEnd(FAudioVoiceCallback* callback, void*)
|
||||||
@ -221,20 +220,17 @@ struct OPEN_segaapiBuffer_t
|
|||||||
FAudio_BufferNotify xaCallback; // buffer end notification
|
FAudio_BufferNotify xaCallback; // buffer end notification
|
||||||
};
|
};
|
||||||
|
|
||||||
void defer_buffer_call(FAudioSourceVoice* voice, queue_t* defers, uint32_t samplerate)
|
void defer_buffer_call(FAudioSourceVoice* voice, FAudio_BufferNotify* notify, uint32_t samplerate)
|
||||||
{
|
{
|
||||||
dbgprint("defer_buffer_call()");
|
dbgprint("defer_buffer_call()");
|
||||||
if (voice)
|
if (voice)
|
||||||
{
|
{
|
||||||
FAudioVoiceState vs;
|
FAudioVoiceState vs;
|
||||||
dbgprint("defer_buffer_call: call FAudioSourceVoice_GetState");
|
|
||||||
FAudioSourceVoice_GetState(voice, &vs, 0);
|
FAudioSourceVoice_GetState(voice, &vs, 0);
|
||||||
dbgprint("defer_buffer_call: call complete: %i", vs.BuffersQueued);
|
|
||||||
if (vs.BuffersQueued > 0)
|
if (vs.BuffersQueued > 0)
|
||||||
{
|
{
|
||||||
dbgprint("defer_buffer_call: call queue_push voice = %08x", voice);
|
dbgprint("defer_buffer_call: push samplerate: %d", samplerate);
|
||||||
queue_push((const void*)voice, defers);
|
notify->defers.enqueue(samplerate);
|
||||||
dbgprint("defer_buffer_call: call complete");
|
|
||||||
|
|
||||||
FAudioSourceVoice_FlushSourceBuffers(voice);
|
FAudioSourceVoice_FlushSourceBuffers(voice);
|
||||||
|
|
||||||
@ -497,10 +493,12 @@ static FAudioSubmixVoice* g_submixVoices[6];
|
|||||||
|
|
||||||
static void updateBufferNew(OPEN_segaapiBuffer_t* buffer, unsigned int offset, size_t length)
|
static void updateBufferNew(OPEN_segaapiBuffer_t* buffer, unsigned int offset, size_t length)
|
||||||
{
|
{
|
||||||
|
size_t count = 0;
|
||||||
dbgprint("updateBufferNew voice: %08x data: %p size: %d", buffer->xaCallback.xaVoice, buffer->data, buffer->size);
|
dbgprint("updateBufferNew voice: %08x data: %p size: %d", buffer->xaCallback.xaVoice, buffer->data, buffer->size);
|
||||||
|
|
||||||
// don't update with pending defers
|
// don't update with pending defers
|
||||||
if (!queue_isempty(buffer->xaCallback.defers))
|
count = buffer->xaCallback.defers.size_approx();
|
||||||
|
if (count > 0)
|
||||||
{
|
{
|
||||||
dbgprint("updateBufferNew: DEFER!");
|
dbgprint("updateBufferNew: DEFER!");
|
||||||
return;
|
return;
|
||||||
@ -573,10 +571,6 @@ extern "C" {
|
|||||||
pConfig->dwPriority,
|
pConfig->dwPriority,
|
||||||
pConfig->dwSampleFormat);
|
pConfig->dwSampleFormat);
|
||||||
|
|
||||||
buffer->xaCallback.defers = queue_init(20, 20, sizeof(FAudioSourceVoice*));
|
|
||||||
if (buffer->xaCallback.defers == NULL)
|
|
||||||
printf("queue_init failed!\r\n");
|
|
||||||
|
|
||||||
buffer->playing = false;
|
buffer->playing = false;
|
||||||
buffer->callback = pCallback;
|
buffer->callback = pCallback;
|
||||||
buffer->synthesizer = dwFlags & OPEN_HABUF_SYNTH_BUFFER;
|
buffer->synthesizer = dwFlags & OPEN_HABUF_SYNTH_BUFFER;
|
||||||
@ -742,7 +736,7 @@ extern "C" {
|
|||||||
OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
|
OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
|
||||||
buffer->sampleRate = dwSampleRate;
|
buffer->sampleRate = dwSampleRate;
|
||||||
|
|
||||||
defer_buffer_call(buffer->xaCallback.xaVoice, buffer->xaCallback.defers, dwSampleRate);
|
defer_buffer_call(buffer->xaCallback.xaVoice, &buffer->xaCallback, dwSampleRate);
|
||||||
return OPEN_SEGA_SUCCESS;
|
return OPEN_SEGA_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -939,7 +933,6 @@ extern "C" {
|
|||||||
OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
|
OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
|
||||||
|
|
||||||
FAudioVoice_DestroyVoice(buffer->xaCallback.xaVoice);
|
FAudioVoice_DestroyVoice(buffer->xaCallback.xaVoice);
|
||||||
queue_destroy(buffer->xaCallback.defers);
|
|
||||||
delete buffer;
|
delete buffer;
|
||||||
return OPEN_SEGA_SUCCESS;
|
return OPEN_SEGA_SUCCESS;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user