Start work on sound effects

This commit is contained in:
James Lambert 2022-05-19 19:26:54 -06:00
parent 6e00caff73
commit feccb13a58
20 changed files with 815 additions and 5 deletions

View file

@ -23,6 +23,7 @@ RUN apt install -y binutils-mips-n64 \
root-compatibility-environment \
build-essential \
libmpc-dev \
sfz2n64 \
vtf2png \
libxi6 \
libxxf86vm-dev \

View file

@ -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
####################

View file

@ -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
View 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

View file

@ -0,0 +1 @@
-c 1

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View file

@ -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

View file

@ -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;

View file

@ -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);
}
@ -211,6 +211,8 @@ int sceneOpenPortal(struct Scene* scene, struct Transform* at, int portalIndex,
// 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;
return 1;

View 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));