Start work on sound effects
This commit is contained in:
parent
6e00caff73
commit
feccb13a58
|
@ -23,6 +23,7 @@ RUN apt install -y binutils-mips-n64 \
|
|||
root-compatibility-environment \
|
||||
build-essential \
|
||||
libmpc-dev \
|
||||
sfz2n64 \
|
||||
vtf2png \
|
||||
libxi6 \
|
||||
libxxf86vm-dev \
|
||||
|
|
27
Makefile
27
Makefile
|
@ -10,6 +10,7 @@ include /usr/include/n64/make/PRdefs
|
|||
|
||||
SKELATOOL64:=skelatool64/skeletool64
|
||||
VTF2PNG:=vtf2png
|
||||
SFZ2N64:=sfz2n64
|
||||
|
||||
$(SKELATOOL64):
|
||||
make -C skelatool64
|
||||
|
@ -185,6 +186,32 @@ build/src/levels/levels.o: build/assets/test_chambers/level_list.h build/assets/
|
|||
|
||||
.PHONY: levels
|
||||
|
||||
####################
|
||||
## Sounds
|
||||
####################
|
||||
|
||||
SOUND_ATTRIBUTES = $(shell find assets/ -type f -name '*.sox')
|
||||
|
||||
SOUND_CLIPS = $(SOUND_ATTRIBUTES:%.sox=build/%.wav)
|
||||
|
||||
build/%.wav: %.sox portal_pak_dir
|
||||
@mkdir -p $(@D)
|
||||
sox $(<:assets/%.sox=portal_pak_dir/%.wav) $(shell cat $<) $@
|
||||
|
||||
build/assets/sound/sounds.sounds build/assets/sound/sounds.sounds.tbl: $(SOUND_CLIPS)
|
||||
@mkdir -p $(@D)
|
||||
$(SFZ2N64) --compress -o $@ $^
|
||||
|
||||
|
||||
asm/sound_data.s: build/assets/sound/sounds.sounds \
|
||||
build/assets/sound/sounds.sounds.tbl
|
||||
|
||||
build/src/audio/clips.h: tools/generate_sound_ids.js $(SOUND_CLIPS)
|
||||
@mkdir -p $(@D)
|
||||
node tools/generate_sound_ids.js -o $@ -p SOUNDS_ $(SOUND_CLIPS)
|
||||
|
||||
build/src/audio/clips.o: build/src/audio/clips.h
|
||||
|
||||
####################
|
||||
## Linking
|
||||
####################
|
||||
|
|
|
@ -14,11 +14,11 @@ You will need to install python vpk
|
|||
pip install vpk
|
||||
```
|
||||
|
||||
Install vtf2png and skeletool64
|
||||
Install vtf2png, sfz2n64 and skeletool64
|
||||
|
||||
```
|
||||
echo "deb [trusted=yes] https://lambertjamesd.github.io/apt/ ./" | tee /etc/apt/sources.list.d/lambertjamesd.list
|
||||
sudo apt install vtf2png skeletool64
|
||||
sudo apt install vtf2png sfz2n64 skeletool64
|
||||
```
|
||||
|
||||
Install image magic
|
||||
|
|
13
asm/sound_data.s
Normal file
13
asm/sound_data.s
Normal file
|
@ -0,0 +1,13 @@
|
|||
.include "macros.inc"
|
||||
|
||||
.section .data
|
||||
|
||||
glabel _soundsSegmentRomStart
|
||||
.incbin "build/assets/sound/sounds.sounds"
|
||||
.balign 16
|
||||
glabel _soundsSegmentRomEnd
|
||||
|
||||
glabel _soundsTblSegmentRomStart
|
||||
.incbin "build/assets/sound/sounds.sounds.tbl"
|
||||
.balign 16
|
||||
glabel _soundsTblSegmentRomEnd
|
1
assets/sound/player/portal_enter1.sox
Normal file
1
assets/sound/player/portal_enter1.sox
Normal file
|
@ -0,0 +1 @@
|
|||
-c 1
|
Binary file not shown.
|
@ -63,6 +63,13 @@ SECTIONS
|
|||
|
||||
. = 0x80200000;
|
||||
|
||||
BEGIN_SEG(sound_data, __romPos)
|
||||
{
|
||||
build/asm/sound_data.o(.data);
|
||||
build/asm/sound_data.o(.bss);
|
||||
}
|
||||
END_SEG(sound_data)
|
||||
|
||||
/* Discard everything not specifically mentioned above. */
|
||||
/DISCARD/ :
|
||||
{
|
||||
|
|
34
src/audio/audio.c
Normal file
34
src/audio/audio.c
Normal file
|
@ -0,0 +1,34 @@
|
|||
|
||||
#include "audio.h"
|
||||
#include "util/rom.h"
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
/**** audio globals ****/
|
||||
u8* gAudioHeapBuffer;
|
||||
ALHeap gAudioHeap;
|
||||
|
||||
void initAudio(void)
|
||||
{
|
||||
ALSynConfig c;
|
||||
amConfig amc;
|
||||
|
||||
alHeapInit(&gAudioHeap, gAudioHeapBuffer, AUDIO_HEAP_SIZE);
|
||||
|
||||
/*
|
||||
* Create the Audio Manager
|
||||
*/
|
||||
c.maxVVoices = MAX_VOICES;
|
||||
c.maxPVoices = MAX_VOICES;
|
||||
c.maxUpdates = MAX_UPDATES;
|
||||
c.dmaproc = 0; /* audio mgr will fill this in */
|
||||
c.fxType = AL_FX_SMALLROOM;
|
||||
c.outputRate = 0; /* audio mgr will fill this in */
|
||||
c.heap = &gAudioHeap;
|
||||
|
||||
amc.outputRate = 44100;
|
||||
amc.framesPerField = NUM_FIELDS;
|
||||
amc.maxACMDSize = MAX_RSP_CMDS;
|
||||
|
||||
amCreateAudioMgr(&c, AUDIO_PRIORITY, &amc);
|
||||
}
|
60
src/audio/audio.h
Normal file
60
src/audio/audio.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
|
||||
#ifndef __audio__
|
||||
#define __audio__
|
||||
|
||||
#include <ultra64.h>
|
||||
#include <libaudio.h>
|
||||
#include <sched.h>
|
||||
|
||||
#define MAX_UPDATES 64
|
||||
#define MAX_EVENTS 128
|
||||
#define AUDIO_HEAP_SIZE 300000
|
||||
|
||||
#define MAX_VOICES 64
|
||||
#define EXTRA_SAMPLES 80
|
||||
#define NUM_OUTPUT_BUFFERS 3 /* Need three of these */
|
||||
#define OUTPUT_RATE 44100
|
||||
#define MAX_AUDIO_MESGS 32
|
||||
#define QUIT_MSG 10
|
||||
|
||||
#define DMA_BUFFER_LENGTH 0x800 /* Larger buffers result in fewer DMA' but more */
|
||||
/* memory being used. */
|
||||
|
||||
|
||||
#define NUM_ACMD_LISTS 2 /* two lists used by this example */
|
||||
#define MAX_RSP_CMDS 4096 /* max number of commands in any command list. */
|
||||
/* Mainly dependent on sequences used */
|
||||
|
||||
#define NUM_DMA_BUFFERS 24 /* max number of dma buffers needed. */
|
||||
/* Mainly dependent on sequences and sfx's */
|
||||
|
||||
#define NUM_DMA_MESSAGES 32 /* The maximum number of DMAs any one frame can */
|
||||
/* have. */
|
||||
|
||||
#define FRAME_LAG 1 /* The number of frames to keep a dma buffer. */
|
||||
/* Increasing this number causes buffers to not */
|
||||
/* be deleted as quickly. This results in fewer */
|
||||
/* DMA's but you need more buffers. */
|
||||
|
||||
|
||||
#define AUDIO_STACKSIZE 0x2000
|
||||
|
||||
#define MAX_SEQ_LENGTH 20000
|
||||
|
||||
|
||||
typedef struct {
|
||||
u32 outputRate;
|
||||
u32 framesPerField;
|
||||
u32 maxACMDSize;
|
||||
} amConfig;
|
||||
|
||||
|
||||
void amCreateAudioMgr(ALSynConfig *c, OSPri priority, amConfig *amc);
|
||||
void initAudio(void);
|
||||
|
||||
extern u64 audYieldBuf[];
|
||||
extern u8* gAudioHeapBuffer;
|
||||
extern ALHeap gAudioHeap;
|
||||
|
||||
#endif
|
||||
|
429
src/audio/audiomgr.c
Normal file
429
src/audio/audiomgr.c
Normal file
|
@ -0,0 +1,429 @@
|
|||
|
||||
#include <assert.h>
|
||||
#include "audio.h"
|
||||
#include "defs.h"
|
||||
|
||||
extern OSPiHandle *gPiHandle;
|
||||
|
||||
/**** type define's for structures unique to audiomgr ****/
|
||||
typedef union {
|
||||
|
||||
struct {
|
||||
short type;
|
||||
} gen;
|
||||
|
||||
struct {
|
||||
short type;
|
||||
struct AudioInfo_s *info;
|
||||
} done;
|
||||
|
||||
OSScMsg app;
|
||||
|
||||
} AudioMsg;
|
||||
|
||||
typedef struct AudioInfo_s {
|
||||
short *data; /* Output data pointer */
|
||||
short frameSamples; /* # of samples synthesized in this frame */
|
||||
OSScTask task; /* scheduler structure */
|
||||
AudioMsg msg; /* completion message */
|
||||
} AudioInfo;
|
||||
|
||||
typedef struct {
|
||||
Acmd *ACMDList[NUM_ACMD_LISTS];
|
||||
AudioInfo *audioInfo[NUM_OUTPUT_BUFFERS];
|
||||
OSThread thread;
|
||||
OSMesgQueue audioFrameMsgQ;
|
||||
OSMesg audioFrameMsgBuf[MAX_AUDIO_MESGS];
|
||||
OSMesgQueue audioReplyMsgQ;
|
||||
OSMesg audioReplyMsgBuf[MAX_AUDIO_MESGS];
|
||||
ALGlobals g;
|
||||
} AMAudioMgr;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ALLink node;
|
||||
u32 startAddr;
|
||||
u32 lastFrame;
|
||||
char *ptr;
|
||||
} AMDMABuffer;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 initialized;
|
||||
AMDMABuffer *firstUsed;
|
||||
AMDMABuffer *firstFree;
|
||||
} AMDMAState;
|
||||
|
||||
|
||||
/**** audio manager globals ****/
|
||||
extern OSSched scheduler;
|
||||
extern OSMesgQueue *schedulerCommandQueue;
|
||||
|
||||
AMAudioMgr __am;
|
||||
static u64 audioStack[AUDIO_STACKSIZE/sizeof(u64)];
|
||||
|
||||
AMDMAState dmaState;
|
||||
AMDMABuffer dmaBuffs[NUM_DMA_BUFFERS];
|
||||
u32 audFrameCt = 0;
|
||||
u32 nextDMA = 0;
|
||||
u32 curAcmdList = 0;
|
||||
u32 minFrameSize;
|
||||
u32 frameSize;
|
||||
u32 maxFrameSize;
|
||||
u32 maxRSPCmds;
|
||||
|
||||
/** Queues and storage for use with audio DMA's ****/
|
||||
OSIoMesg audDMAIOMesgBuf[NUM_DMA_MESSAGES];
|
||||
OSMesgQueue audDMAMessageQ;
|
||||
OSMesg audDMAMessageBuf[NUM_DMA_MESSAGES];
|
||||
|
||||
/**** private routines ****/
|
||||
static void __amMain(void *arg);
|
||||
static s32 __amDMA(s32 addr, s32 len, void *state);
|
||||
static ALDMAproc __amDmaNew(AMDMAState **state);
|
||||
static u32 __amHandleFrameMsg(AudioInfo *, AudioInfo *);
|
||||
static void __amHandleDoneMsg(AudioInfo *);
|
||||
static void __clearAudioDMA(void);
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Audio Manager API
|
||||
*****************************************************************************/
|
||||
void amCreateAudioMgr(ALSynConfig *c, OSPri pri, amConfig *amc)
|
||||
{
|
||||
u32 i;
|
||||
f32 fsize;
|
||||
|
||||
dmaState.initialized = FALSE; /* Reset this before the first call to __amDmaNew */
|
||||
|
||||
c->dmaproc = __amDmaNew;
|
||||
c->outputRate = osAiSetFrequency(amc->outputRate);
|
||||
|
||||
/*
|
||||
* Calculate the frame sample parameters from the
|
||||
* video field rate and the output rate
|
||||
*/
|
||||
fsize = (f32) amc->framesPerField * c->outputRate / (f32) 60;
|
||||
frameSize = (s32) fsize;
|
||||
if (frameSize < fsize)
|
||||
frameSize++;
|
||||
if (frameSize & 0xf)
|
||||
frameSize = (frameSize & ~0xf) + 0x10;
|
||||
minFrameSize = frameSize - 16;
|
||||
maxFrameSize = frameSize + EXTRA_SAMPLES + 16;
|
||||
|
||||
alInit(&__am.g, c);
|
||||
|
||||
dmaBuffs[0].node.prev = 0;
|
||||
dmaBuffs[0].node.next = 0;
|
||||
for (i=0; i<NUM_DMA_BUFFERS-1; i++)
|
||||
{
|
||||
alLink((ALLink*)&dmaBuffs[i+1],(ALLink*)&dmaBuffs[i]);
|
||||
dmaBuffs[i].ptr = alHeapAlloc(c->heap, 1, DMA_BUFFER_LENGTH);
|
||||
}
|
||||
/* last buffer already linked, but still needs buffer */
|
||||
dmaBuffs[i].ptr = alHeapAlloc(c->heap, 1, DMA_BUFFER_LENGTH);
|
||||
|
||||
for(i=0;i<NUM_ACMD_LISTS;i++)
|
||||
__am.ACMDList[i] = (Acmd*)alHeapAlloc(c->heap, 1,
|
||||
amc->maxACMDSize * sizeof(Acmd));
|
||||
|
||||
maxRSPCmds = amc->maxACMDSize;
|
||||
|
||||
/**** initialize the done messages ****/
|
||||
for (i = 0; i < NUM_OUTPUT_BUFFERS; i++)
|
||||
{
|
||||
__am.audioInfo[i] = (AudioInfo *)alHeapAlloc(c->heap, 1,
|
||||
sizeof(AudioInfo));
|
||||
__am.audioInfo[i]->msg.done.type = OS_SC_DONE_MSG;
|
||||
__am.audioInfo[i]->msg.done.info = __am.audioInfo[i];
|
||||
__am.audioInfo[i]->data = alHeapAlloc(c->heap, 1, 4*maxFrameSize);
|
||||
}
|
||||
|
||||
osCreateMesgQueue(&__am.audioReplyMsgQ, __am.audioReplyMsgBuf, MAX_AUDIO_MESGS);
|
||||
osCreateMesgQueue(&__am.audioFrameMsgQ, __am.audioFrameMsgBuf, MAX_AUDIO_MESGS);
|
||||
osCreateMesgQueue(&audDMAMessageQ, audDMAMessageBuf, NUM_DMA_MESSAGES);
|
||||
|
||||
osCreateThread(&__am.thread, 3, __amMain, 0,
|
||||
(void *)(audioStack+AUDIO_STACKSIZE/sizeof(u64)), pri);
|
||||
osStartThread(&__am.thread);
|
||||
}
|
||||
|
||||
static void __amMain(void *arg)
|
||||
{
|
||||
u32 validTask;
|
||||
u32 done = 0;
|
||||
AudioMsg *msg;
|
||||
AudioInfo *lastInfo = 0;
|
||||
OSScClient client;
|
||||
|
||||
|
||||
osScAddClient(&scheduler, &client, &__am.audioFrameMsgQ);
|
||||
|
||||
while (!done)
|
||||
{
|
||||
(void) osRecvMesg(&__am.audioFrameMsgQ, (OSMesg *)&msg, OS_MESG_BLOCK);
|
||||
|
||||
switch (msg->gen.type)
|
||||
{
|
||||
case (OS_SC_RETRACE_MSG):
|
||||
validTask = __amHandleFrameMsg(__am.audioInfo[audFrameCt % 3],
|
||||
lastInfo);
|
||||
if(validTask)
|
||||
{
|
||||
/* wait for done message */
|
||||
osRecvMesg(&__am.audioReplyMsgQ, (OSMesg *)&msg,
|
||||
OS_MESG_BLOCK);
|
||||
__amHandleDoneMsg(msg->done.info);
|
||||
lastInfo = msg->done.info;
|
||||
}
|
||||
break;
|
||||
|
||||
case (OS_SC_PRE_NMI_MSG):
|
||||
/* what should we really do here? quit? ramp down volume? */
|
||||
break;
|
||||
|
||||
case (QUIT_MSG):
|
||||
done = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
alClose(&__am.g);
|
||||
}
|
||||
|
||||
static u32 __amHandleFrameMsg(AudioInfo *info, AudioInfo *lastInfo)
|
||||
{
|
||||
s16 *audioPtr;
|
||||
Acmd *cmdp;
|
||||
s32 cmdLen;
|
||||
int samplesLeft = 0;
|
||||
OSScTask *t;
|
||||
|
||||
|
||||
__clearAudioDMA(); /* call once a frame, before doing alAudioFrame */
|
||||
|
||||
audioPtr = (s16 *) osVirtualToPhysical(info->data);
|
||||
|
||||
if (lastInfo)
|
||||
osAiSetNextBuffer(lastInfo->data, lastInfo->frameSamples<<2);
|
||||
|
||||
|
||||
/* calculate how many samples needed for this frame to keep the DAC full */
|
||||
/* this will vary slightly frame to frame, must recalculate every frame */
|
||||
samplesLeft = osAiGetLength() >> 2; /* divide by four, to convert bytes */
|
||||
/* to stereo 16 bit samples */
|
||||
info->frameSamples = (16 + (frameSize - samplesLeft + EXTRA_SAMPLES)) & ~0xf;
|
||||
if(info->frameSamples < minFrameSize)
|
||||
info->frameSamples = minFrameSize;
|
||||
|
||||
cmdp = alAudioFrame(__am.ACMDList[curAcmdList], &cmdLen, audioPtr,
|
||||
info->frameSamples);
|
||||
|
||||
assert(cmdLen <= maxRSPCmds);
|
||||
|
||||
if(cmdLen == 0) /* no task produced, return zero to show no valid task */
|
||||
return 0;
|
||||
|
||||
t = &info->task;
|
||||
|
||||
t->next = 0; /* paranoia */
|
||||
t->msgQ = &__am.audioReplyMsgQ; /* reply to when finished */
|
||||
t->msg = (OSMesg)&info->msg; /* reply with this message */
|
||||
t->flags = OS_SC_NEEDS_RSP;
|
||||
|
||||
t->list.t.data_ptr = (u64 *) __am.ACMDList[curAcmdList];
|
||||
t->list.t.data_size = (cmdp - __am.ACMDList[curAcmdList]) * sizeof(Acmd);
|
||||
t->list.t.type = M_AUDTASK;
|
||||
t->list.t.ucode_boot = (u64 *)rspbootTextStart;
|
||||
t->list.t.ucode_boot_size =
|
||||
((int) rspbootTextEnd - (int) rspbootTextStart);
|
||||
t->list.t.flags = 0;
|
||||
t->list.t.ucode = (u64 *) aspMainTextStart;
|
||||
t->list.t.ucode_data = (u64 *) aspMainDataStart;
|
||||
t->list.t.ucode_data_size = SP_UCODE_DATA_SIZE;
|
||||
t->list.t.dram_stack = (u64 *) NULL;
|
||||
t->list.t.dram_stack_size = 0;
|
||||
t->list.t.output_buff = (u64 *) NULL;
|
||||
t->list.t.output_buff_size = 0;
|
||||
t->list.t.yield_data_ptr = NULL;
|
||||
t->list.t.yield_data_size = 0;
|
||||
#ifndef STOP_AUDIO
|
||||
osSendMesg(schedulerCommandQueue, (OSMesg) t, OS_MESG_BLOCK);
|
||||
#endif
|
||||
curAcmdList ^= 1; /* swap which acmd list you use each frame */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* __amHandleDoneMsg. Really just debugging info in this frame. Checks
|
||||
* to make sure we completed before we were out of samples.
|
||||
*
|
||||
*****************************************************************************/
|
||||
static void __amHandleDoneMsg(AudioInfo *info)
|
||||
{
|
||||
s32 samplesLeft;
|
||||
static int firstTime = 1;
|
||||
|
||||
samplesLeft = osAiGetLength()>>2;
|
||||
if (samplesLeft == 0 && !firstTime)
|
||||
{
|
||||
PRINTF("audio: ai out of samples\n");
|
||||
firstTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
s32 __amDMA(s32 addr, s32 len, void *state)
|
||||
{
|
||||
void *foundBuffer;
|
||||
s32 delta, addrEnd, buffEnd;
|
||||
AMDMABuffer *dmaPtr, *lastDmaPtr;
|
||||
|
||||
lastDmaPtr = 0;
|
||||
dmaPtr = dmaState.firstUsed;
|
||||
addrEnd = addr+len;
|
||||
|
||||
/* first check to see if a currently existing buffer contains the
|
||||
sample that you need. */
|
||||
|
||||
while(dmaPtr)
|
||||
{
|
||||
buffEnd = dmaPtr->startAddr + DMA_BUFFER_LENGTH;
|
||||
if(dmaPtr->startAddr > addr) /* since buffers are ordered */
|
||||
break; /* abort if past possible */
|
||||
|
||||
else if(addrEnd <= buffEnd) /* yes, found a buffer with samples */
|
||||
{
|
||||
dmaPtr->lastFrame = audFrameCt; /* mark it used */
|
||||
foundBuffer = dmaPtr->ptr + addr - dmaPtr->startAddr;
|
||||
return (int) osVirtualToPhysical(foundBuffer);
|
||||
}
|
||||
lastDmaPtr = dmaPtr;
|
||||
dmaPtr = (AMDMABuffer*)dmaPtr->node.next;
|
||||
}
|
||||
|
||||
/* get here, and you didn't find a buffer, so dma a new one */
|
||||
|
||||
/* get a buffer from the free list */
|
||||
dmaPtr = dmaState.firstFree;
|
||||
|
||||
/*
|
||||
* if you get here and dmaPtr is null, send back the a bogus
|
||||
* pointer, it's better than nothing
|
||||
*/
|
||||
if(!dmaPtr)
|
||||
return osVirtualToPhysical(dmaState.firstUsed);
|
||||
|
||||
dmaState.firstFree = (AMDMABuffer*)dmaPtr->node.next;
|
||||
alUnlink((ALLink*)dmaPtr);
|
||||
|
||||
/* add it to the used list */
|
||||
if(lastDmaPtr) /* if you have other dmabuffers used, add this one */
|
||||
{ /* to the list, after the last one checked above */
|
||||
alLink((ALLink*)dmaPtr,(ALLink*)lastDmaPtr);
|
||||
}
|
||||
else if(dmaState.firstUsed) /* if this buffer is before any others */
|
||||
{ /* jam at begining of list */
|
||||
lastDmaPtr = dmaState.firstUsed;
|
||||
dmaState.firstUsed = dmaPtr;
|
||||
dmaPtr->node.next = (ALLink*)lastDmaPtr;
|
||||
dmaPtr->node.prev = 0;
|
||||
lastDmaPtr->node.prev = (ALLink*)dmaPtr;
|
||||
}
|
||||
else /* no buffers in list, this is the first one */
|
||||
{
|
||||
dmaState.firstUsed = dmaPtr;
|
||||
dmaPtr->node.next = 0;
|
||||
dmaPtr->node.prev = 0;
|
||||
}
|
||||
|
||||
foundBuffer = dmaPtr->ptr;
|
||||
delta = addr & 0x1;
|
||||
addr -= delta;
|
||||
dmaPtr->startAddr = addr;
|
||||
dmaPtr->lastFrame = audFrameCt; /* mark it */
|
||||
|
||||
audDMAIOMesgBuf[nextDMA].hdr.pri = OS_MESG_PRI_NORMAL;
|
||||
audDMAIOMesgBuf[nextDMA].hdr.retQueue = &audDMAMessageQ;
|
||||
audDMAIOMesgBuf[nextDMA].dramAddr = foundBuffer;
|
||||
audDMAIOMesgBuf[nextDMA].devAddr = (u32)addr;
|
||||
audDMAIOMesgBuf[nextDMA].size = DMA_BUFFER_LENGTH;
|
||||
|
||||
osEPiStartDma(gPiHandle, &audDMAIOMesgBuf[nextDMA++], OS_READ);
|
||||
|
||||
return (int) osVirtualToPhysical(foundBuffer) + delta;
|
||||
}
|
||||
|
||||
ALDMAproc __amDmaNew(AMDMAState **state)
|
||||
{
|
||||
if(!dmaState.initialized) /* only do this once */
|
||||
{
|
||||
dmaState.firstUsed = 0;
|
||||
dmaState.firstFree = &dmaBuffs[0];
|
||||
dmaState.initialized = 1;
|
||||
}
|
||||
|
||||
*state = &dmaState; /* state is never used in this case */
|
||||
|
||||
return __amDMA;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* __clearAudioDMA. Routine to move dma buffers back to the unused list.
|
||||
* First clear out your dma messageQ. Then check each buffer to see when
|
||||
* it was last used. If that was more than FRAME_LAG frames ago, move it
|
||||
* back to the unused list.
|
||||
*
|
||||
*****************************************************************************/
|
||||
static void __clearAudioDMA(void)
|
||||
{
|
||||
u32 i;
|
||||
OSIoMesg *iomsg;
|
||||
AMDMABuffer *dmaPtr,*nextPtr;
|
||||
|
||||
/*
|
||||
* Don't block here. If dma's aren't complete, you've had an audio
|
||||
* overrun. (Bad news, but go for it anyway, and try and recover.
|
||||
*/
|
||||
for (i=0; i<nextDMA; i++)
|
||||
{
|
||||
if (osRecvMesg(&audDMAMessageQ,(OSMesg *)&iomsg,OS_MESG_NOBLOCK) == -1)
|
||||
PRINTF("Dma not done\n");
|
||||
}
|
||||
|
||||
|
||||
dmaPtr = dmaState.firstUsed;
|
||||
while(dmaPtr)
|
||||
{
|
||||
nextPtr = (AMDMABuffer*)dmaPtr->node.next;
|
||||
|
||||
/* remove old dma's from list */
|
||||
/* Can change FRAME_LAG value. Should be at least one. */
|
||||
/* Larger values mean more buffers needed, but fewer DMA's */
|
||||
if(dmaPtr->lastFrame + FRAME_LAG < audFrameCt)
|
||||
{
|
||||
if(dmaState.firstUsed == dmaPtr)
|
||||
dmaState.firstUsed = (AMDMABuffer*)dmaPtr->node.next;
|
||||
alUnlink((ALLink*)dmaPtr);
|
||||
if(dmaState.firstFree)
|
||||
alLink((ALLink*)dmaPtr,(ALLink*)dmaState.firstFree);
|
||||
else
|
||||
{
|
||||
dmaState.firstFree = dmaPtr;
|
||||
dmaPtr->node.next = 0;
|
||||
dmaPtr->node.prev = 0;
|
||||
}
|
||||
}
|
||||
dmaPtr = nextPtr;
|
||||
}
|
||||
|
||||
nextDMA = 0; /* reset */
|
||||
audFrameCt++;
|
||||
}
|
||||
|
6
src/audio/clips.c
Normal file
6
src/audio/clips.c
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
#include "clips.h"
|
||||
|
||||
#include "../../build/src/audio/clips.h"
|
||||
|
||||
unsigned short soundsPortalEnter = SOUNDS_PORTAL_ENTER1;
|
6
src/audio/clips.h
Normal file
6
src/audio/clips.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#ifndef __AUDIO_CLIPS_H__
|
||||
#define __AUDIO_CLIPS_H__
|
||||
|
||||
extern unsigned short soundsPortalEnter;
|
||||
|
||||
#endif
|
47
src/audio/soundarray.c
Normal file
47
src/audio/soundarray.c
Normal file
|
@ -0,0 +1,47 @@
|
|||
|
||||
#include "soundarray.h"
|
||||
|
||||
void _bnkfPatchWaveTable(ALWaveTable *w, s32 offset, s32 table)
|
||||
{
|
||||
if (w->flags)
|
||||
return;
|
||||
|
||||
w->flags = 1;
|
||||
|
||||
w->base += table;
|
||||
|
||||
/* sct 2/14/96 - patch wavetable loop info based on type. */
|
||||
if (w->type == AL_ADPCM_WAVE)
|
||||
{
|
||||
w->waveInfo.adpcmWave.book = (ALADPCMBook *)((u8 *)w->waveInfo.adpcmWave.book + offset);
|
||||
if (w->waveInfo.adpcmWave.loop)
|
||||
w->waveInfo.adpcmWave.loop = (ALADPCMloop *)((u8 *)w->waveInfo.adpcmWave.loop + offset);
|
||||
}
|
||||
else if (w->type == AL_RAW16_WAVE)
|
||||
{
|
||||
if (w->waveInfo.rawWave.loop)
|
||||
w->waveInfo.rawWave.loop = (ALRawLoop *)((u8 *)w->waveInfo.rawWave.loop + offset);
|
||||
}
|
||||
}
|
||||
|
||||
void _bnkfPatchSound(ALSound *s, s32 offset, s32 table)
|
||||
{
|
||||
if (s->flags)
|
||||
return;
|
||||
|
||||
s->flags = 1;
|
||||
|
||||
s->envelope = (ALEnvelope *)((u8 *)s->envelope + offset);
|
||||
s->keyMap = (ALKeyMap *)((u8 *)s->keyMap + offset);
|
||||
|
||||
s->wavetable = (ALWaveTable *)((u8 *)s->wavetable + offset);
|
||||
_bnkfPatchWaveTable(s->wavetable, offset, table);
|
||||
}
|
||||
|
||||
void soundArrayInit(struct SoundArray* soundArray, void* tbl) {
|
||||
int i;
|
||||
for (i = 0; i < soundArray->soundCount; ++i) {
|
||||
soundArray->sounds[i] = (ALSound*)((u8*)soundArray->sounds[i] + (u32)soundArray);
|
||||
_bnkfPatchSound(soundArray->sounds[i], (u32)soundArray, (u32)tbl);
|
||||
}
|
||||
}
|
14
src/audio/soundarray.h
Normal file
14
src/audio/soundarray.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
#ifndef _AUDIO_SOUNDARRAY_H
|
||||
#define _AUDIO_SOUNDARRAY_H
|
||||
|
||||
#include <ultra64.h>
|
||||
|
||||
struct SoundArray {
|
||||
u32 soundCount;
|
||||
ALSound* sounds[];
|
||||
};
|
||||
|
||||
void soundArrayInit(struct SoundArray* soundArray, void* tbl);
|
||||
|
||||
#endif
|
80
src/audio/soundplayer.c
Normal file
80
src/audio/soundplayer.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
#include "audio.h"
|
||||
#include "soundplayer.h"
|
||||
#include "soundarray.h"
|
||||
#include "util/rom.h"
|
||||
#include "util/time.h"
|
||||
#include "math/mathf.h"
|
||||
|
||||
struct SoundArray* gSoundClipArray;
|
||||
ALSndPlayer gSoundPlayer;
|
||||
|
||||
#define MAX_ACTIVE_SOUNDS 16
|
||||
|
||||
struct ActiveSound {
|
||||
ALSndId soundId;
|
||||
u16 soundId3d;
|
||||
};
|
||||
|
||||
struct ActiveSound gActiveSounds[MAX_ACTIVE_SOUNDS];
|
||||
int gActiveSoundCount = 0;
|
||||
|
||||
void soundPlayerInit() {
|
||||
gSoundClipArray = alHeapAlloc(&gAudioHeap, 1, _soundsSegmentRomEnd - _soundsSegmentRomStart);
|
||||
romCopy(_soundsSegmentRomStart, (char*)gSoundClipArray, _soundsSegmentRomEnd - _soundsSegmentRomStart);
|
||||
soundArrayInit(gSoundClipArray, _soundsTblSegmentRomStart);
|
||||
|
||||
ALSndpConfig sndConfig;
|
||||
sndConfig.maxEvents = MAX_EVENTS;
|
||||
sndConfig.maxSounds = MAX_SOUNDS;
|
||||
sndConfig.heap = &gAudioHeap;
|
||||
alSndpNew(&gSoundPlayer, &sndConfig);
|
||||
|
||||
for (int i = 0; i < MAX_ACTIVE_SOUNDS; ++i) {
|
||||
gActiveSounds[i].soundId = SOUND_ID_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
ALSndId soundPlayerPlay(int soundClipId, float volume, float pitch) {
|
||||
if (gActiveSoundCount == MAX_ACTIVE_SOUNDS || soundClipId < 0 || soundClipId >= gSoundClipArray->soundCount) {
|
||||
return SOUND_ID_NONE;
|
||||
}
|
||||
|
||||
ALSndId result = alSndpAllocate(&gSoundPlayer, gSoundClipArray->sounds[soundClipId]);
|
||||
|
||||
if (result == SOUND_ID_NONE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
gActiveSounds[gActiveSoundCount].soundId = result;
|
||||
gActiveSounds[gActiveSoundCount].soundId3d = SOUND_ID_NONE;
|
||||
|
||||
alSndpSetSound(&gSoundPlayer, result);
|
||||
alSndpSetVol(&gSoundPlayer, (short)(32767 * volume));
|
||||
alSndpSetPitch(&gSoundPlayer, pitch);
|
||||
alSndpPlay(&gSoundPlayer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void soundPlayerUpdate() {
|
||||
int index = 0;
|
||||
int writeIndex = 0;
|
||||
|
||||
while (index < gActiveSoundCount) {
|
||||
alSndpSetSound(&gSoundPlayer, gActiveSounds[index].soundId);
|
||||
|
||||
if (alSndpGetState(&gSoundPlayer) == AL_STOPPED) {
|
||||
alSndpDeallocate(&gSoundPlayer, gActiveSounds[index].soundId);
|
||||
} else {
|
||||
++writeIndex;
|
||||
}
|
||||
|
||||
++index;
|
||||
|
||||
if (writeIndex != index) {
|
||||
gActiveSounds[writeIndex] = gActiveSounds[index];
|
||||
}
|
||||
}
|
||||
|
||||
gActiveSoundCount = writeIndex;
|
||||
}
|
23
src/audio/soundplayer.h
Normal file
23
src/audio/soundplayer.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef _SOUND_PLAYER_H
|
||||
#define _SOUND_PLAYER_H
|
||||
|
||||
#include <ultra64.h>
|
||||
#include "math/vector3.h"
|
||||
#include "math/quaternion.h"
|
||||
|
||||
#define MAX_SOUNDS 128
|
||||
|
||||
#define SOUND_SAMPLE_RATE 22500
|
||||
|
||||
#define SOUND_ID_NONE -1
|
||||
|
||||
extern char _soundsSegmentRomStart[];
|
||||
extern char _soundsSegmentRomEnd[];
|
||||
extern char _soundsTblSegmentRomStart[];
|
||||
extern char _soundsTblSegmentRomEnd[];
|
||||
|
||||
void soundPlayerInit();
|
||||
void soundPlayerUpdate();
|
||||
ALSndId soundPlayerPlay(int soundClipId, float volume, float pitch);
|
||||
|
||||
#endif
|
|
@ -10,7 +10,9 @@
|
|||
|
||||
#define INIT_PRIORITY 10
|
||||
#define GAME_PRIORITY 10
|
||||
#define AUDIO_PRIORITY 12
|
||||
#define SCHEDULER_PRIORITY 13
|
||||
#define NUM_FIELDS 1
|
||||
|
||||
#define DMA_QUEUE_SIZE 200
|
||||
|
||||
|
@ -18,6 +20,8 @@
|
|||
|
||||
#define SIMPLE_CONTROLLER_MSG (5)
|
||||
|
||||
#define PRINTF(a)
|
||||
|
||||
#endif /* _LANGUAGE_C */
|
||||
|
||||
#endif
|
||||
|
|
11
src/main.c
11
src/main.c
|
@ -11,6 +11,8 @@
|
|||
#include "string.h"
|
||||
#include "controls/controller.h"
|
||||
#include "scene/dynamic_scene.h"
|
||||
#include "audio/soundplayer.h"
|
||||
#include "audio/audio.h"
|
||||
|
||||
#include "levels/levels.h"
|
||||
|
||||
|
@ -36,7 +38,7 @@ static OSMesg gfxFrameMsgBuf[MAX_FRAME_BUFFER_MESGS];
|
|||
static OSScClient gfxClient;
|
||||
|
||||
|
||||
static OSSched scheduler;
|
||||
OSSched scheduler;
|
||||
u64 scheduleStack[OS_SC_STACKSIZE/8];
|
||||
OSMesgQueue *schedulerCommandQueue;
|
||||
|
||||
|
@ -127,6 +129,10 @@ static void gameProc(void* arg) {
|
|||
|
||||
u16* memoryEnd = graphicsLayoutScreenBuffers((u16*)PHYS_TO_K0(osMemSize));
|
||||
|
||||
gAudioHeapBuffer = (u8*)memoryEnd - AUDIO_HEAP_SIZE;
|
||||
|
||||
memoryEnd = (u16*)gAudioHeapBuffer;
|
||||
|
||||
heapInit(_heapStart, memoryEnd);
|
||||
romInit();
|
||||
|
||||
|
@ -135,6 +141,8 @@ static void gameProc(void* arg) {
|
|||
levelLoad(0);
|
||||
sceneInit(&gScene);
|
||||
controllersInit();
|
||||
initAudio();
|
||||
soundPlayerInit();
|
||||
#ifdef WITH_DEBUGGER
|
||||
OSThread* debugThreads[2];
|
||||
debugThreads[0] = &gameThread;
|
||||
|
@ -160,6 +168,7 @@ static void gameProc(void* arg) {
|
|||
controllersTriggerRead();
|
||||
sceneUpdate(&gScene);
|
||||
timeUpdateDelta();
|
||||
soundPlayerUpdate();
|
||||
|
||||
break;
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "../math/mathf.h"
|
||||
#include "./hud.h"
|
||||
#include "dynamic_scene.h"
|
||||
#include "../audio/soundplayer.h"
|
||||
#include "../audio/clips.h"
|
||||
|
||||
struct Vector3 gStartPosition = {5.0f, 1.2f, -5.0f};
|
||||
struct Vector3 gPortalGunOffset = {0.100957, -0.113587, -0.28916};
|
||||
|
@ -75,8 +77,6 @@ void sceneRenderWithProperties(void* data, struct RenderProps* properties, struc
|
|||
portalRender(&scene->portals[1 - closerPortal], &scene->portals[closerPortal], properties, sceneRenderWithProperties, data, renderState);
|
||||
}
|
||||
|
||||
gDPSetRenderMode(renderState->dl++, G_RM_ZB_OPA_SURF, G_RM_ZB_OPA_SURF2);
|
||||
|
||||
staticRender(&properties->cullingInfo, renderState);
|
||||
}
|
||||
|
||||
|
@ -210,6 +210,8 @@ int sceneOpenPortal(struct Scene* scene, struct Transform* at, int portalIndex,
|
|||
quatMultVector(&at->rotation, &gForward, &portalForward);
|
||||
// TODO remove once there is a hole in the wall
|
||||
vector3AddScaled(&at->position, &portalForward, (portalIndex == 0) ? -0.1f : 0.1f, &at->position);
|
||||
|
||||
soundPlayerPlay(soundsPortalEnter, 1.0f, 1.0f);
|
||||
|
||||
scene->portals[portalIndex].transform = *at;
|
||||
gCollisionScene.portalTransforms[portalIndex] = &scene->portals[portalIndex].transform;
|
||||
|
|
47
tools/generate_sound_ids.js
Normal file
47
tools/generate_sound_ids.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
let output = '';
|
||||
let inputs = [];
|
||||
let definePrefix = '';
|
||||
let lastCommand = '';
|
||||
|
||||
for (let i = 2; i < process.argv.length; ++i) {
|
||||
const arg = process.argv[i];
|
||||
if (lastCommand) {
|
||||
if (lastCommand == '-o') {
|
||||
output = arg;
|
||||
} else if (lastCommand == '-p') {
|
||||
definePrefix = arg;
|
||||
}
|
||||
lastCommand = '';
|
||||
} else if (arg[0] == '-') {
|
||||
lastCommand = arg;
|
||||
} else {
|
||||
inputs.push(arg);
|
||||
}
|
||||
}
|
||||
|
||||
inputs.push('TOTAL_COUNT');
|
||||
|
||||
const invalidCharactersRegex = /[^\w\d_]+/gm;
|
||||
|
||||
function formatSoundName(soundFilename, index) {
|
||||
const extension = path.extname(soundFilename);
|
||||
const lastPart = path.basename(soundFilename, extension);
|
||||
const defineName = definePrefix + lastPart.replace(invalidCharactersRegex, '_').toUpperCase();
|
||||
return `#define ${defineName} ${index}`;
|
||||
}
|
||||
|
||||
function formatFile(outputFilename, soundFilenames) {
|
||||
const defineName = outputFilename.replace(invalidCharactersRegex, '_').toUpperCase();
|
||||
return `#ifndef ${defineName}
|
||||
#define ${defineName}
|
||||
|
||||
${soundFilenames.map(formatSoundName).join('\n')}
|
||||
|
||||
#endif`
|
||||
}
|
||||
|
||||
fs.writeFileSync(output, formatFile(output, inputs));
|
Loading…
Reference in a new issue