SuperAllStarPhotoStudio/nds/arm9/source/sound.cpp
RocketRobz f64f913f4e Play music on DS Phat/lite without a MEP
1st character is now written to the page file
2021-06-29 23:55:38 -06:00

255 lines
7.1 KiB
C++

#include "sound.h"
#include "soundbank.h"
#include "streamingaudio.h"
#include "string.h"
#include "tonccpy.h"
#include <algorithm>
extern volatile s16 fade_counter;
extern volatile bool fade_out;
extern volatile s16* play_stream_buf;
extern volatile s16* fill_stream_buf;
// Number of samples filled into the fill buffer so far.
extern volatile s32 filled_samples;
extern volatile bool fill_requested;
extern volatile s32 samples_left_until_next_fill;
extern volatile s32 streaming_buf_ptr;
#define SAMPLES_USED (STREAMING_BUF_LENGTH - samples_left)
#define REFILL_THRESHOLD STREAMING_BUF_LENGTH >> 2
#ifdef SOUND_DEBUG
extern char debug_buf[256];
#endif
extern volatile u32 sample_delay_count;
char* SFX_DATA = (char*)NULL;
mm_word SOUNDBANK[MSL_BANKSIZE] = {0};
SoundControl::SoundControl()
: stream_is_playing(false), stream_start_source(NULL), stream_source(NULL)
{
sys.mod_count = MSL_NSONGS;
sys.samp_count = MSL_NSAMPS;
sys.mem_bank = SOUNDBANK;
sys.fifo_channel = FIFO_MAXMOD;
FILE* soundbank_file;
soundbank_file = fopen("nitro:/soundbank.bin", "rb");
SFX_DATA = new char[0x9800];
fread(SFX_DATA, 1, 0x9800, soundbank_file);
fclose(soundbank_file);
// sprintf(debug_buf, "Read sample length %li for startup", startup_sample_length);
// nocashMessage(debug_buf);
mmInit(&sys);
mmSoundBankInMemory((mm_addr)SFX_DATA);
mmLoadEffect(SFX_SELECT);
mmLoadEffect(SFX_BACK);
mmLoadEffect(SFX_HIGHLIGHT);
snd_select = {
{ SFX_SELECT } , // id
(int)(1.0f * (1<<10)), // rate
0, // handle
255, // volume
128, // panning
};
snd_back = {
{ SFX_BACK } , // id
(int)(1.0f * (1<<10)), // rate
0, // handle
255, // volume
128, // panning
};
snd_highlight = {
{ SFX_HIGHLIGHT } , // id
(int)(1.0f * (1<<10)), // rate
0, // handle
255, // volume
128, // panning
};
init_streaming_buf();
}
mm_sfxhand SoundControl::playSelect() { return mmEffectEx(&snd_select); }
mm_sfxhand SoundControl::playBack() { return mmEffectEx(&snd_back); }
mm_sfxhand SoundControl::playHighlight() { return mmEffectEx(&snd_highlight); }
void SoundControl::loadStream(const char* path, const char* loopPath, u32 sampleRate, bool loop) {
if (stream_source) {
stream_is_playing = false;
mmStreamClose();
fclose(stream_source);
}
resetStreamSettings();
stream_start_source = fopen(path, "rb");
stream_source = fopen(loopPath, "rb");
if (!stream_source) return;
bool loopableMusic = stream_start_source ? true : false;
loopingPoint = false;
fseek(stream_source, 0, SEEK_SET);
stream.sampling_rate = sampleRate; // ?????Hz
stream.buffer_length = 0x1000; // should be adequate
stream.callback = on_stream_request;
stream.format = MM_STREAM_8BIT_STEREO; // select format
stream.timer = MM_TIMER0; // use timer0
stream.manual = false; // auto filling
looping = loop;
if (loopableMusic) {
fseek(stream_start_source, 0, SEEK_END);
size_t fileSize = ftell(stream_start_source);
fseek(stream_start_source, 0, SEEK_SET);
// Prep the first section of the stream
fread((void*)play_stream_buf, sizeof(s16), STREAMING_BUF_LENGTH, stream_start_source);
if (fileSize < STREAMING_BUF_LENGTH*sizeof(s16)) {
size_t fillerSize = 0;
while (fileSize+fillerSize < STREAMING_BUF_LENGTH*sizeof(s16)) {
fillerSize++;
}
fread((void*)play_stream_buf+fileSize, 1, fillerSize, stream_source);
// Fill the next section premptively
fread((void*)fill_stream_buf, sizeof(s16), STREAMING_BUF_LENGTH, stream_source);
loopingPoint = true;
} else {
// Fill the next section premptively
fread((void*)fill_stream_buf, sizeof(s16), STREAMING_BUF_LENGTH, stream_start_source);
fileSize -= STREAMING_BUF_LENGTH*sizeof(s16);
if (fileSize < STREAMING_BUF_LENGTH*sizeof(s16)) {
size_t fillerSize = 0;
while (fileSize+fillerSize < STREAMING_BUF_LENGTH*sizeof(s16)) {
fillerSize++;
}
fread((void*)fill_stream_buf+fileSize, 1, fillerSize, stream_source);
loopingPoint = true;
}
}
} else {
// Prep the first section of the stream
fread((void*)play_stream_buf, sizeof(s16), STREAMING_BUF_LENGTH, stream_source);
// Fill the next section premptively
fread((void*)fill_stream_buf, sizeof(s16), STREAMING_BUF_LENGTH, stream_source);
loopingPoint = true;
}
}
void SoundControl::beginStream() {
if (!stream_source) return;
// open the stream
stream_is_playing = true;
mmStreamOpen(&stream);
SetYtrigger(0);
}
void SoundControl::stopStream() {
if (!stream_source) return;
stream_is_playing = false;
mmStreamClose();
}
void SoundControl::fadeOutStream() {
fade_out = true;
}
void SoundControl::cancelFadeOutStream() {
fade_out = false;
fade_counter = FADE_STEPS;
}
// Samples remaining in the fill buffer.
#define SAMPLES_LEFT_TO_FILL (abs(STREAMING_BUF_LENGTH - filled_samples))
// Samples that were already streamed and need to be refilled into the buffer.
#define SAMPLES_TO_FILL (abs(streaming_buf_ptr - filled_samples))
// Updates the background music fill buffer
// Fill the amount of samples that were used up between the
// last fill request and this.
// Precondition Invariants:
// filled_samples <= STREAMING_BUF_LENGTH
// filled_samples <= streaming_buf_ptr
// Postcondition Invariants:
// filled_samples <= STREAMING_BUF_LENGTH
// filled_samples <= streaming_buf_ptr
// fill_requested == false
volatile void SoundControl::updateStream() {
if (!stream_is_playing) return;
if (fill_requested && filled_samples < STREAMING_BUF_LENGTH) {
// Reset the fill request
fill_requested = false;
int instance_filled = 0;
// Either fill the max amount, or fill up the buffer as much as possible.
int instance_to_fill = std::min(SAMPLES_LEFT_TO_FILL, SAMPLES_TO_FILL);
// If we don't read enough samples, loop from the beginning of the file.
instance_filled = fread((s16*)fill_stream_buf + filled_samples, sizeof(s16), instance_to_fill, loopingPoint ? stream_source : stream_start_source);
if (instance_filled < instance_to_fill) {
if (looping) {
fseek(stream_source, 0, SEEK_SET);
int i = fread((s16*)fill_stream_buf + filled_samples + instance_filled,
sizeof(s16), (instance_to_fill - instance_filled), stream_source);
if (i==0) {
toncset((s16*)fill_stream_buf + filled_samples + instance_filled, 0, (instance_to_fill - instance_filled)*sizeof(s16));
} else {
instance_filled += i;
}
loopingPoint = true;
} else {
toncset((s16*)fill_stream_buf + filled_samples + instance_filled, 0, (instance_to_fill - instance_filled)*sizeof(s16));
}
}
#ifdef SOUND_DEBUG
sprintf(debug_buf, "FC: SAMPLES_LEFT_TO_FILL: %li, SAMPLES_TO_FILL: %li, instance_filled: %i, filled_samples %li, to_fill: %i", SAMPLES_LEFT_TO_FILL, SAMPLES_TO_FILL, instance_filled, filled_samples, instance_to_fill);
nocashMessage(debug_buf);
#endif
// maintain invariant 0 < filled_samples <= STREAMING_BUF_LENGTH
filled_samples = std::min<s32>(filled_samples + instance_filled, STREAMING_BUF_LENGTH);
} else if (fill_requested && filled_samples >= STREAMING_BUF_LENGTH) {
// filled_samples == STREAMING_BUF_LENGTH is the only possible case
// but we'll keep it at gte to be safe.
filled_samples = 0;
// fill_count = 0;
}
}