⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sndstream.c

📁 DC的SEGA_GG模拟器源代码
💻 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 + -