Fix high frequency notes by using downsampled versions

This commit is contained in:
Sean Maas 2021-03-04 20:10:21 -05:00
parent 00bf4294e8
commit 3573b75b4b
3 changed files with 42 additions and 14 deletions

View File

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

View File

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

View File

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