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 "segaerr.h"
|
||||
#include "dqueue.h"
|
||||
// https://github.com/cameron314/concurrentqueue
|
||||
#include "concurrentqueue.h"
|
||||
|
||||
//#define _DEBUG
|
||||
|
||||
@ -135,7 +136,7 @@ __inline float FAudioSemitonesToFrequencyRatio(float Semitones)
|
||||
class FAudio_BufferNotify : public FAudioVoiceCallback {
|
||||
public:
|
||||
OPEN_segaapiBuffer_t* buffer = NULL;
|
||||
queue_t * defers = NULL;
|
||||
moodycamel::ConcurrentQueue<uint32_t> defers;
|
||||
FAudioSourceVoice* xaVoice;
|
||||
|
||||
FAudio_BufferNotify() {
|
||||
@ -152,29 +153,27 @@ public:
|
||||
private:
|
||||
void SignalBufferEnd()
|
||||
{
|
||||
dbgprint("SignalBufferEnd()");
|
||||
FAudioSourceVoice* returned_voice;
|
||||
dbgprint("SignalBufferEnd() size = %ld", defers.size_approx());
|
||||
uint32_t returned_samplerate;
|
||||
|
||||
while (!queue_isempty(defers))
|
||||
{
|
||||
FAudioVoiceState vs;
|
||||
while (defers.size_approx() > 0)
|
||||
{
|
||||
FAudioVoiceState vs;
|
||||
|
||||
FAudioSourceVoice_GetState(xaVoice, &vs, 0);
|
||||
FAudioSourceVoice_GetState(xaVoice, &vs, 0);
|
||||
|
||||
if (vs.BuffersQueued > 0)
|
||||
{
|
||||
FAudioSourceVoice_FlushSourceBuffers(xaVoice);
|
||||
return;
|
||||
}
|
||||
|
||||
returned_voice = (FAudioSourceVoice*)queue_pop(defers);
|
||||
if (returned_voice)
|
||||
{
|
||||
dbgprint("SignalBufferEnd: voice = %08x", returned_voice);
|
||||
FAudioSourceVoice_FlushSourceBuffers(returned_voice);
|
||||
}
|
||||
}
|
||||
if (vs.BuffersQueued > 0)
|
||||
{
|
||||
FAudioSourceVoice_FlushSourceBuffers(xaVoice);
|
||||
return;
|
||||
}
|
||||
|
||||
if (defers.try_dequeue(returned_samplerate))
|
||||
{
|
||||
dbgprint("SignalBufferEnd: samplerate: %d", returned_samplerate);
|
||||
FAudioSourceVoice_SetSourceSampleRate(xaVoice, returned_samplerate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void StaticOnBufferEnd(FAudioVoiceCallback* callback, void*)
|
||||
@ -221,20 +220,17 @@ struct OPEN_segaapiBuffer_t
|
||||
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()");
|
||||
if (voice)
|
||||
{
|
||||
FAudioVoiceState vs;
|
||||
dbgprint("defer_buffer_call: call FAudioSourceVoice_GetState");
|
||||
FAudioSourceVoice_GetState(voice, &vs, 0);
|
||||
dbgprint("defer_buffer_call: call complete: %i", vs.BuffersQueued);
|
||||
if (vs.BuffersQueued > 0)
|
||||
{
|
||||
dbgprint("defer_buffer_call: call queue_push voice = %08x", voice);
|
||||
queue_push((const void*)voice, defers);
|
||||
dbgprint("defer_buffer_call: call complete");
|
||||
dbgprint("defer_buffer_call: push samplerate: %d", samplerate);
|
||||
notify->defers.enqueue(samplerate);
|
||||
|
||||
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)
|
||||
{
|
||||
size_t count = 0;
|
||||
dbgprint("updateBufferNew voice: %08x data: %p size: %d", buffer->xaCallback.xaVoice, buffer->data, buffer->size);
|
||||
|
||||
// don't update with pending defers
|
||||
if (!queue_isempty(buffer->xaCallback.defers))
|
||||
count = buffer->xaCallback.defers.size_approx();
|
||||
if (count > 0)
|
||||
{
|
||||
dbgprint("updateBufferNew: DEFER!");
|
||||
return;
|
||||
@ -573,10 +571,6 @@ extern "C" {
|
||||
pConfig->dwPriority,
|
||||
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->callback = pCallback;
|
||||
buffer->synthesizer = dwFlags & OPEN_HABUF_SYNTH_BUFFER;
|
||||
@ -742,7 +736,7 @@ extern "C" {
|
||||
OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -939,7 +933,6 @@ extern "C" {
|
||||
OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
|
||||
|
||||
FAudioVoice_DestroyVoice(buffer->xaCallback.xaVoice);
|
||||
queue_destroy(buffer->xaCallback.defers);
|
||||
delete buffer;
|
||||
return OPEN_SEGA_SUCCESS;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user