From 3573b75b4bebb3368bb538d2afd1a83afd849472 Mon Sep 17 00:00:00 2001 From: Sean Maas Date: Thu, 4 Mar 2021 20:10:21 -0500 Subject: [PATCH] Fix high frequency notes by using downsampled versions --- Makefile | 7 ++++++- src/nds/arm7/nds_audio.c | 31 ++++++++++++++++++++++--------- tools/assemble_sound.py | 18 ++++++++++++++---- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 1103ef3a..93fd41cd 100644 --- a/Makefile +++ b/Makefile @@ -282,7 +282,8 @@ SOUND_BANK_FILES := $(wildcard sound/sound_banks/*.json) SOUND_SAMPLE_DIRS := $(wildcard sound/samples/*) SOUND_SAMPLE_AIFFS := $(foreach dir,$(SOUND_SAMPLE_DIRS),$(wildcard $(dir)/*.aiff)) ifdef TARGET_NDS -SOUND_SAMPLE_AIFCS := $(foreach file,$(SOUND_SAMPLE_AIFFS),$(BUILD_DIR)/$(file:.aiff=.ima)) +SOUND_SAMPLE_HALFS := $(wildcard sound/samples/instruments/*.aiff) sound/samples/sfx_9/03.aiff +SOUND_SAMPLE_AIFCS := $(foreach file,$(SOUND_SAMPLE_AIFFS),$(BUILD_DIR)/$(file:.aiff=.ima)) $(foreach file,$(SOUND_SAMPLE_HALFS),$(BUILD_DIR)/$(file:.aiff=.half.ima)) else SOUND_SAMPLE_TABLES := $(foreach file,$(SOUND_SAMPLE_AIFFS),$(BUILD_DIR)/$(file:.aiff=.table)) SOUND_SAMPLE_AIFCS := $(foreach file,$(SOUND_SAMPLE_AIFFS),$(BUILD_DIR)/$(file:.aiff=.aifc)) @@ -639,6 +640,10 @@ endif #==============================================================================# ifdef TARGET_NDS +$(BUILD_DIR)/%.half.wav: %.aiff + $(call print,Converting AIFF:,$<,$@) + $(V)sox $^ -r $(shell echo $(shell sox --i -r $^) / 2 | bc) $@ + $(BUILD_DIR)/%.wav: %.aiff $(call print,Converting AIFF:,$<,$@) $(V)sox $^ $@ diff --git a/src/nds/arm7/nds_audio.c b/src/nds/arm7/nds_audio.c index f709a0bb..30217423 100644 --- a/src/nds/arm7/nds_audio.c +++ b/src/nds/arm7/nds_audio.c @@ -6,10 +6,11 @@ #undef SOUND_FREQ #define SOUND_FREQ(n) (-(BUS_CLOCK >> 1) / (n)) -static u16 calculate_freq(const struct Note *note) { +static u16 high_freqs; + +static u16 calculate_freq(f32 frequency) { // Calculate the DS frequency for a note - // Some frequencies are too high, but clipping at least lets them get as close as possible - u32 freq = note->frequency * 32000; + u32 freq = frequency * 32000; if (freq > 0xFFFF) freq = 0xFFFF; return SOUND_FREQ((u16)freq); } @@ -39,17 +40,29 @@ void play_notes(struct Note *notes) { // Ensure the channel is properly reset SCHANNEL_CR(i) &= ~SCHANNEL_ENABLE; - // Start playing a note on the current channel - SCHANNEL_SOURCE(i) = (u32)sample->sampleAddr; - SCHANNEL_REPEAT_POINT(i) = sample->loop->start / sizeof(u32); - SCHANNEL_LENGTH(i) = (sample->loop->end - sample->loop->start) / sizeof(u32); - SCHANNEL_TIMER(i) = calculate_freq(note); + if (note->frequency >= 2.0f && *(u32*)sample->sampleAddr != 0) { + // If the frequency is too high, play the downsampled version at half frequency + SCHANNEL_SOURCE(i) = (u32)sample->sampleAddr + *(u32*)sample->sampleAddr + 4; + SCHANNEL_REPEAT_POINT(i) = sample->loop->start / 2 / sizeof(u32) + 1; + SCHANNEL_LENGTH(i) = (sample->loop->end - sample->loop->start) / 2 / sizeof(u32) + 1; + SCHANNEL_TIMER(i) = calculate_freq(note->frequency / 2); + high_freqs |= BIT(i); + } else { + // Play the normal version at full frequency + SCHANNEL_SOURCE(i) = (u32)sample->sampleAddr + 4; + SCHANNEL_REPEAT_POINT(i) = sample->loop->start / sizeof(u32) + 1; + SCHANNEL_LENGTH(i) = (sample->loop->end - sample->loop->start) / sizeof(u32) + 1; + SCHANNEL_TIMER(i) = calculate_freq(note->frequency); + high_freqs &= ~BIT(i); + } + + // Start the channel SCHANNEL_CR(i) = SCHANNEL_ENABLE | SOUND_FORMAT_ADPCM | calculate_vol_pan(note) | loop; note->needsInit = false; } else if (SCHANNEL_CR(i) & SCHANNEL_ENABLE) { // Update the parameters of a currently playing note - SCHANNEL_TIMER(i) = calculate_freq(note); + SCHANNEL_TIMER(i) = calculate_freq(note->frequency / ((high_freqs & BIT(i)) ? 2 : 1)); SCHANNEL_CR(i) = (SCHANNEL_CR(i) & ~(SOUND_VOL(127) | SOUND_PAN(127))) | calculate_vol_pan(note); } } else { diff --git a/tools/assemble_sound.py b/tools/assemble_sound.py index 8a34b7d7..0e5f184e 100755 --- a/tools/assemble_sound.py +++ b/tools/assemble_sound.py @@ -184,8 +184,8 @@ def parse_ima(data, name, fname, bank_name): if tp == b"COMM": sample_rate = parse_f80(aiff_data[8:18]) elif tp == b"MARK": - start = (struct.unpack('>I', aiff_data[4:8])[0] >> 1) + 4 - end = (struct.unpack('>I', aiff_data[16:20])[0] >> 1) + 4 + start = struct.unpack('>I', aiff_data[4:8])[0] >> 1 + end = struct.unpack('>I', aiff_data[16:20])[0] >> 1 loop = Loop(start, end, 1, None) return Aifc(name, fname, data, sample_rate, None, loop) @@ -705,7 +705,17 @@ def serialize_tbl(sample_bank, ser, is_shindou): continue ser.align(16) aifc.offset = ser.size - base_addr - ser.add(aifc.data) + if aifc.fname.endswith(".ima"): + if os.path.exists(aifc.fname[:-4] + ".half.ima"): + ser.add(len(aifc.data).to_bytes(4, 'little')) + ser.add(aifc.data) + with open(aifc.fname[:-4] + ".half.ima", "rb") as half: + ser.add(half.read()) + else: + ser.add(b"\0\0\0\0") + ser.add(aifc.data) + else: + ser.add(aifc.data) ser.align(2) if is_shindou and sample_bank.index not in [4, 10]: ser.align(16) @@ -1031,7 +1041,7 @@ def main(): data = inf.read() if f.endswith(".aifc"): entries.append(parse_aifc(data, f[:-5], fname)) - elif f.endswith(".ima"): + elif not f.endswith(".half.ima") and f.endswith(".ima"): entries.append(parse_ima(data, f[:-4], fname, name)) except Exception as e: fail("malformed audio file " + fname + ": " + str(e))