mirror of
https://github.com/sjlongland/polyphonic-synthesizer.git
synced 2025-09-13 08:42:22 +10:00
209 lines
4.9 KiB
C
209 lines
4.9 KiB
C
#ifndef _UTIL_FIFO_H
|
|
#define _UTIL_FIFO_H
|
|
|
|
/*!
|
|
* Simple ring FIFO buffer.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program (see COPYING); if not, write to the Free
|
|
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
|
|
/*!
|
|
* Empty event. Indicates that the buffer is now empty and the next read
|
|
* will generate an underrun.
|
|
*/
|
|
#define FIFO_EVT_EMPTY (1 << 0)
|
|
|
|
/*!
|
|
* Underrun event flag, indicates that the consumer tried to read when the
|
|
* buffer was empty.
|
|
*/
|
|
#define FIFO_EVT_UNDERRUN (1 << 1)
|
|
|
|
/*!
|
|
* Data arrived event. Indicates that new data has arrived.
|
|
*/
|
|
#define FIFO_EVT_NEW (1 << 2)
|
|
|
|
/*!
|
|
* Buffer full event. Indicates that the buffer is now full and the next
|
|
* write will generate an overrun.
|
|
*/
|
|
#define FIFO_EVT_FULL (1 << 3)
|
|
|
|
/*!
|
|
* Overrun event flag, indicates that the producer tried to write when the
|
|
* buffer was full.
|
|
*/
|
|
#define FIFO_EVT_OVERRUN (1 << 4)
|
|
|
|
/*!
|
|
* FIFO Buffer interface.
|
|
*/
|
|
struct fifo_t {
|
|
/*! FIFO producer event handler */
|
|
void (*producer_evth)(struct fifo_t* const fifo, uint8_t events);
|
|
|
|
/*! FIFO consumer event handler */
|
|
void (*consumer_evth)(struct fifo_t* const fifo, uint8_t events);
|
|
|
|
volatile uint8_t* buffer; /*!< Buffer storage location */
|
|
uint8_t total_sz; /*!< Buffer total size */
|
|
volatile uint8_t stored_sz; /*!< Buffer usage size */
|
|
volatile uint8_t read_ptr; /*!< Read pointer location */
|
|
volatile uint8_t write_ptr; /*!< Write pointer location */
|
|
|
|
uint8_t producer_evtm; /*!< Producer event mask */
|
|
uint8_t consumer_evtm; /*!< Consumer event mask */
|
|
|
|
void* producer_data; /*!< Producer data pointer */
|
|
void* consumer_data; /*!< Consumer data pointer */
|
|
};
|
|
|
|
/*!
|
|
* Execute one or more FIFO events.
|
|
*/
|
|
static void fifo_exec(struct fifo_t* const fifo, uint8_t events) {
|
|
if (fifo->producer_evth && (fifo->producer_evtm & events))
|
|
fifo->producer_evth(fifo, events);
|
|
if (fifo->consumer_evth && (fifo->consumer_evtm & events))
|
|
fifo->consumer_evth(fifo, events);
|
|
}
|
|
|
|
/*!
|
|
* Empty the buffer.
|
|
*/
|
|
static void fifo_empty(struct fifo_t* const fifo) {
|
|
fifo->stored_sz = 0;
|
|
fifo->read_ptr = 0;
|
|
fifo->write_ptr = 0;
|
|
}
|
|
|
|
/*!
|
|
* Initialise the buffer
|
|
*/
|
|
static void fifo_init(struct fifo_t* const fifo,
|
|
volatile uint8_t* buffer, uint8_t sz) {
|
|
fifo_empty(fifo);
|
|
fifo->buffer = buffer;
|
|
fifo->total_sz = sz;
|
|
}
|
|
|
|
/*!
|
|
* Read a byte from the buffer. Returns the byte read, or -1 if no
|
|
* data is available.
|
|
*/
|
|
static int16_t fifo_read_one(struct fifo_t* const fifo) {
|
|
if (!fifo->stored_sz) {
|
|
fifo_exec(fifo, FIFO_EVT_UNDERRUN);
|
|
return -1;
|
|
}
|
|
|
|
uint8_t byte = fifo->buffer[fifo->read_ptr];
|
|
fifo->stored_sz--;
|
|
fifo->read_ptr = (fifo->read_ptr + 1) % fifo->total_sz;
|
|
if (!fifo->stored_sz)
|
|
fifo_exec(fifo, FIFO_EVT_EMPTY);
|
|
return byte;
|
|
}
|
|
|
|
/*!
|
|
* Read a byte from the buffer without consuming it.
|
|
* Returns the byte read, or -1 if no data is available.
|
|
*/
|
|
static int16_t fifo_peek_one(struct fifo_t* const fifo) {
|
|
if (!fifo->stored_sz)
|
|
return -1;
|
|
|
|
return fifo->buffer[fifo->read_ptr];
|
|
}
|
|
|
|
/*!
|
|
* Write a byte to the buffer. Returns 1 on success,
|
|
* 0 if no space available.
|
|
*/
|
|
static uint8_t fifo_write_one(struct fifo_t* const fifo, uint8_t byte) {
|
|
if (fifo->stored_sz >= fifo->total_sz) {
|
|
fifo_exec(fifo, FIFO_EVT_OVERRUN);
|
|
return 0;
|
|
}
|
|
|
|
fifo->buffer[fifo->write_ptr] = byte;
|
|
fifo->stored_sz++;
|
|
fifo->write_ptr = (fifo->write_ptr + 1) % fifo->total_sz;
|
|
|
|
if (fifo->stored_sz)
|
|
fifo_exec(fifo, FIFO_EVT_NEW);
|
|
|
|
if (fifo->stored_sz == fifo->total_sz)
|
|
fifo_exec(fifo, FIFO_EVT_FULL);
|
|
return 1;
|
|
}
|
|
|
|
/*!
|
|
* Read bytes from the buffer
|
|
*/
|
|
static uint8_t fifo_read(struct fifo_t* const fifo,
|
|
uint8_t* buffer, uint8_t sz) {
|
|
uint8_t count = 0;
|
|
int16_t byte = fifo_read_one(fifo);
|
|
while(sz && (byte >= 0)) {
|
|
*buffer = byte;
|
|
sz--;
|
|
buffer++;
|
|
count++;
|
|
byte = fifo_read_one(fifo);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/*!
|
|
* Read bytes from the buffer without consuming them.
|
|
*/
|
|
static uint8_t fifo_peek(struct fifo_t* const fifo,
|
|
uint8_t* buffer, uint8_t sz) {
|
|
uint8_t count = 0;
|
|
uint8_t ptr = fifo->read_ptr;
|
|
if (sz > fifo->stored_sz)
|
|
sz = fifo->stored_sz;
|
|
|
|
while(sz) {
|
|
*buffer = fifo->buffer[ptr];
|
|
sz--;
|
|
buffer++;
|
|
count++;
|
|
ptr = (ptr + 1) % fifo->total_sz;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/*!
|
|
* Write bytes to the buffer
|
|
*/
|
|
static uint8_t fifo_write(struct fifo_t* const fifo,
|
|
const uint8_t* buffer, uint8_t sz) {
|
|
uint8_t count = 0;
|
|
while(sz && fifo_write_one(fifo, *buffer)) {
|
|
buffer++;
|
|
sz--;
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
#endif
|