📄 sndstream.c
字号:
/* Tryptonite
sndstream.c
(c)2000 Dan Potter
SH-4 support routines for SPU streaming sound driver
*/
static char id[] = "KOS $Id: sndstream.c,v 1.4 2001/02/05 00:23:46 bard Exp $";
#define NULL 0
//#define BUFLENGTH (65536/4)
#define BUFLENGTH 10000
#include <malloc.h>
#include <kos.h>
/*
Here we implement a very simple double-buffering, not the rather complex
circular queueing done in most DMA'd sound architectures. This is done for
simplicity's sake. At some point we may upgrade that the circular queue
system.
Basically this poll routine checks to see which buffer is playing, and
whether a new one is available or not. If not, we ask the user routine
for more sound data and load it up. That's about it.
*/
#include "sndstream.h"
/* What we think is the currently playing buffer */
static int curbuffer = 0;
/* Var to check for channel position */
static vuint32 *cmd = (vuint32*)(0xa0810000);
static volatile aica_channel *chans = (volatile aica_channel*)(0xa0810004);
/* Seperation buffers (for stereo) */
static int16 *sep_buffer[2] = {NULL, NULL};
/* "Get data" callback */
static void* (*str_get_data)(int cnt) = NULL;
/* SPU RAM mutex (to avoid queue overflow) */
static spinlock_t mutex;
//static thd_mutex_t mutex;
/* SPU RAM malloc pointer */
static uint32 ram_base, ram_top;
/* Stereo/mono flag for stream */
static int stereo;
/* In-ram copy of the stream.drv file in case the CD gets swapped */
static char *streamdrv = NULL;
static int streamdrv_size;
/* Set "get data" callback */
void stream_set_callback(void *(*func)(int)) {
str_get_data = func;
}
/* "Kicks" a channel command */
static void chn_kick(int chn) {
*cmd = AICA_CMD_KICK | (chn+1);
spu_write_wait();
}
/* Performs stereo seperation for the two channels; this routine
has been optimized for the SH-4. */
static void sep_data(void *buffer) {
register int16 *bufsrc, *bufdst;
register int x, y, cnt;
if (stereo) {
bufsrc = (int16*)buffer;
bufdst = sep_buffer[0];
x = 0; y = 0; cnt = BUFLENGTH/4;
do {
*bufdst = *bufsrc;
bufdst++; bufsrc+=2; cnt--;
} while (cnt > 0);
bufsrc = (int16*)buffer; bufsrc++;
bufdst = sep_buffer[1];
x = 1; y = 0; cnt = BUFLENGTH/4;
do {
*bufdst = *bufsrc;
bufdst++; bufsrc+=2; cnt--;
x+=2; y++;
} while (cnt > 0);
} else {
memcpy(sep_buffer[0], buffer, BUFLENGTH/2);
memcpy(sep_buffer[1], buffer, BUFLENGTH/2);
}
}
/* Load sample data from SH-4 ram into SPU ram (auto-allocate RAM) */
uint32 stream_load_sample(const uint16 *src, uint32 len) {
uint32 where;
spinlock_lock(&mutex);
where = ram_top;
spu_memload(where, (uint8*)src, len);
ram_top = (ram_top + len + 3) & (~3);
spinlock_unlock(&mutex);
return where;
}
/* Dump all loaded sample data */
void stream_dump_samples() {
spinlock_lock(&mutex);
ram_top = ram_base;
spinlock_unlock(&mutex);
}
/* Prefill buffers -- do this before calling start() */
void stream_prefill() {
void *buf;
if (!str_get_data) return;
/* Load first buffer */
if (stereo)
buf = str_get_data(BUFLENGTH);
else
buf = str_get_data(BUFLENGTH/2);
sep_data(buf);
spinlock_lock(&mutex);
spu_memload(0x11000 + (BUFLENGTH/2)*0, (uint8*)sep_buffer[0], (BUFLENGTH/2));
spu_memload(0x21000 + (BUFLENGTH/2)*0, (uint8*)sep_buffer[1], (BUFLENGTH/2));
spinlock_unlock(&mutex);
/* Load second buffer */
if (stereo)
buf = str_get_data(BUFLENGTH);
else
buf = str_get_data(BUFLENGTH/2);
sep_data(buf);
spinlock_lock(&mutex);
spu_memload(0x11000 + (BUFLENGTH/2)*1, (uint8*)sep_buffer[0], (BUFLENGTH/2));
spu_memload(0x21000 + (BUFLENGTH/2)*1, (uint8*)sep_buffer[1], (BUFLENGTH/2));
spinlock_unlock(&mutex);
/* Start with playing on buffer 0 */
curbuffer = 0;
}
/* Initialize stream system */
int kos_stream_init(void* (*callback)(int)) {
uint32 hnd, size;
/* Load the AICA driver */
if (!streamdrv) {
hnd = fs_open("/rd/stream.drv", O_RDONLY);
if (!hnd) {
hnd = fs_open("/cd/stream.drv", O_RDONLY);
if (!hnd) {
//printf("Can't open sound driver\r\n");
return -1;
}
}
streamdrv_size = fs_total(hnd);
streamdrv = malloc(fs_total(hnd));
fs_read(hnd, streamdrv, streamdrv_size);
fs_close(hnd);
}
/* Create stereo seperation buffers */
if (!sep_buffer[0]) {
sep_buffer[0] = malloc(BUFLENGTH/2);
sep_buffer[1] = malloc(BUFLENGTH/2);
}
/* Finish loading the stream driver */
spu_disable();
spu_memset(0, 0, 0x31000);
spu_memload(0, streamdrv, streamdrv_size);
spu_enable();
thd_sleep(10);
/* Setup a mem load mutex */
spinlock_init(&mutex);
ram_base = ram_top = 0x31000;
/* Setup the callback */
stream_set_callback(callback);
return 0;
}
/* Shut everything down and free mem */
void stream_shutdown() {
if (sep_buffer[0]) {
free(sep_buffer[0]); sep_buffer[0] = NULL;
free(sep_buffer[1]); sep_buffer[1] = NULL;
}
if (streamdrv) {
free(streamdrv);
streamdrv = NULL;
}
}
/* Start streaming */
void stream_start(uint32 freq, int st) {
if (!str_get_data) return;
/* Select "observation" channel */
/* *chsel = 0; */
stereo = st;
/* Prefill buffers */
stream_prefill();
/* Start streaming */
chans[0].cmd = AICA_CMD_START;
chans[0].freq = freq;
chn_kick(0);
}
/* Stop streaming */
void stream_stop() {
if (!str_get_data) return;
/* Stop stream */
chans[0].cmd = AICA_CMD_STOP;
chn_kick(0);
}
/* Poll streamer to load more data if neccessary */
int stream_poll() {
int realbuffer;
uint32 val;
void *data;
if (!str_get_data) return -1;
/* Get "real" buffer */
val = chans[0].pos;
spu_write_wait();
realbuffer = !(val < BUFLENGTH/4);
/* Has the channel moved on from the "current" buffer? */
if (curbuffer != realbuffer) {
/* Yep, adjust "current" buffer and initiate a load */
/*printf("Playing in buffer %d, loading %d\r\n",
realbuffer, curbuffer);*/
if (stereo)
data = str_get_data(BUFLENGTH);
else
data = str_get_data(BUFLENGTH/2);
if (data == NULL) {
/* Fill the "other" buffer with zeros */
spu_memset(0x11000 + (BUFLENGTH/2)*curbuffer, 0, (BUFLENGTH/2));
spu_memset(0x21000 + (BUFLENGTH/2)*curbuffer, 0, (BUFLENGTH/2));
/* Wait for the current buffer to complete */
do {
val = chans[0].pos;
spu_write_wait();
realbuffer = !(val < 0x6000/8);
if (realbuffer != curbuffer) thd_pass();
} while (curbuffer != realbuffer);
return -1;
}
sep_data(data);
spinlock_lock(&mutex);
spu_memload(0x11000 + (BUFLENGTH/2)*curbuffer, (uint8*)sep_buffer[0], (BUFLENGTH/2));
spu_memload(0x21000 + (BUFLENGTH/2)*curbuffer, (uint8*)sep_buffer[1], (BUFLENGTH/2));
spinlock_unlock(&mutex);
curbuffer = realbuffer;
}
return 0;
}
/* Start a sound sample on the given channel */
void stream_play_effect(int chn, uint32 src, uint32 freq, uint32 len, uint32 vol, uint32 pan) {
spinlock_lock(&mutex);
chans[chn].cmd = AICA_CMD_START;
chans[chn].pos = src;
chans[chn].length = len;
chans[chn].freq = freq;
chans[chn].vol = vol;
chans[chn].pan = pan;
chn_kick(chn);
spinlock_unlock(&mutex);
}
/* Stop a sound sample on the given channel */
void stream_stop_effect(int chn) {
spinlock_lock(&mutex);
chans[chn].cmd = AICA_CMD_STOP;
chn_kick(chn);
spinlock_unlock(&mutex);
}
/* Set the volume on the streaming channels */
void stream_volume(int vol) {
// spinlock_lock(&mutex);
// chans[0].cmd = AICA_CMD_VOL;
// chans[0].vol = vol;
// chn_kick(0);
// spinlock_unlock(&mutex);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -