diff --git a/Dockerfile b/Dockerfile index d91320b5..1be35489 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM devkitpro/devkitarm:latest as build RUN apt update -RUN apt -y install build-essential bsdmainutils +RUN apt -y install build-essential bsdmainutils sox RUN mkdir /sm64 WORKDIR /sm64 diff --git a/Makefile b/Makefile index 3b49224a..a32ca315 100644 --- a/Makefile +++ b/Makefile @@ -236,6 +236,7 @@ ULTRA_BIN_DIRS := lib/bin ifeq ($(TARGET_NDS),1) SRC_DIRS += src/nds + ARM7_SRC_DIRS := src/nds/arm7 else SRC_DIRS += asm ULTRA_SRC_DIRS += lib/asm @@ -269,14 +270,21 @@ ifeq ($(TARGET_NDS),1) guScaleF.c \ guTranslateF.c ULTRA_C_FILES := $(addprefix lib/src/,$(ULTRA_C_FILES)) + + ARM7_C_FILES := $(foreach dir,$(ARM7_SRC_DIRS),$(wildcard $(dir)/*.c)) + ARM7_O_FILES := $(foreach file,$(ARM7_C_FILES),$(BUILD_DIR)/arm7/$(file:.c=.o)) endif # Sound files 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)) +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)) +endif SOUND_SEQUENCE_DIRS := sound/sequences sound/sequences/$(VERSION) # all .m64 files in SOUND_SEQUENCE_DIRS, plus all .m64 files that are generated from .s files in SOUND_SEQUENCE_DIRS SOUND_SEQUENCE_FILES := \ @@ -298,6 +306,10 @@ GODDARD_O_FILES := $(foreach file,$(GODDARD_C_FILES),$(BUILD_DIR)/$(file:.c=.o)) # Automatic dependency files DEP_FILES := $(O_FILES:.o=.d) $(ULTRA_O_FILES:.o=.d) $(GODDARD_O_FILES:.o=.d) $(BUILD_DIR)/$(LD_SCRIPT).d +ifeq ($(TARGET_NDS),1) + DEP_FILES += $(ARM7_O_FILES:.o=.d) +endif + # Files with GLOBAL_ASM blocks ifeq ($(NON_MATCHING),0) ifeq ($(VERSION),sh) @@ -381,14 +393,18 @@ ifeq ($(TARGET_NDS),1) LIBDIRS := $(DEVKITPRO)/libnds TARGET_CFLAGS := -march=armv5te -mtune=arm946e-s -fomit-frame-pointer -ffast-math $(foreach dir,$(LIBDIRS),-I$(dir)/include) -DTARGET_NDS -DARM9 -D_LANGUAGE_C -DNO_SEGMENTED_MEMORY -DLIBFAT -TARGET_LDFLAGS := -lfat -lnds9 -specs=dsi_arm9.specs -g -mthumb -mthumb-interwork $(foreach dir,$(LIBDIRS),-L$(dir)/lib) +ARM7_TARGET_CFLAGS := -mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer -ffast-math $(foreach dir,$(LIBDIRS),-I$(dir)/include) -DTARGET_NDS -DARM7 CC_CHECK := $(CC) CC_CHECK_CFLAGS := -fsyntax-only -fsigned-char $(CC_CFLAGS) $(TARGET_CFLAGS) -Wall -Wextra -Wno-format-security -DNON_MATCHING -DAVOID_UB $(DEF_INC_CFLAGS) +ARM7_CC_CHECK_CFLAGS := -fsyntax-only -fsigned-char $(CC_CFLAGS) $(ARM7_TARGET_CFLAGS) -Wall -Wextra -Wno-format-security $(DEF_INC_CFLAGS) ASFLAGS := $(foreach i,$(INCLUDE_DIRS),-I$(i)) $(foreach d,$(DEFINES),--defsym $(d)) CFLAGS := -fno-strict-aliasing -fwrapv $(OPT_FLAGS) $(TARGET_CFLAGS) $(DEF_INC_CFLAGS) -LDFLAGS := $(TARGET_LDFLAGS) +LDFLAGS := -lfat -lnds9 -specs=dsi_arm9.specs -g -mthumb -mthumb-interwork $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +ARM7_CFLAGS := -fno-strict-aliasing -fwrapv $(OPT_FLAGS) $(ARM7_TARGET_CFLAGS) $(DEF_INC_CFLAGS) +ARM7_LDFLAGS := -lnds7 -specs=ds_arm7.specs -g -mthumb-interwork $(foreach dir,$(LIBDIRS),-L$(dir)/lib) else @@ -537,6 +553,10 @@ endif ALL_DIRS := $(BUILD_DIR) $(addprefix $(BUILD_DIR)/,$(SRC_DIRS) $(GODDARD_SRC_DIRS) $(ULTRA_SRC_DIRS) $(ULTRA_BIN_DIRS) $(BIN_DIRS) $(TEXTURE_DIRS) $(TEXT_DIRS) $(SOUND_SAMPLE_DIRS) $(addprefix levels/,$(LEVEL_DIRS)) rsp include) $(MIO0_DIR) $(addprefix $(MIO0_DIR)/,$(VERSION)) $(SOUND_BIN_DIR) $(SOUND_BIN_DIR)/sequences/$(VERSION) +ifeq ($(TARGET_NDS),1) + ALL_DIRS += $(addprefix $(BUILD_DIR)/arm7/,$(ARM7_SRC_DIRS)) +endif + # Make sure build directory exists before compiling anything DUMMY != mkdir -p $(ALL_DIRS) @@ -611,6 +631,11 @@ endif # Sound File Generation # #==============================================================================# +ifdef TARGET_NDS +$(BUILD_DIR)/%.ima: %.aiff + $(call print,Encoding IMA:,$<,$@) + $(V)sox $^ $@ +else $(BUILD_DIR)/%.table: %.aiff $(call print,Extracting codebook:,$<,$@) $(V)$(AIFF_EXTRACT_CODEBOOK) $< >$@ @@ -618,6 +643,7 @@ $(BUILD_DIR)/%.table: %.aiff $(BUILD_DIR)/%.aifc: $(BUILD_DIR)/%.table %.aiff $(call print,Encoding VADPCM:,$<,$@) $(V)$(VADPCM_ENC) -c $^ $@ +endif $(ENDIAN_BITWIDTH): $(TOOLS_DIR)/determine-endian-bitwidth.c @$(PRINT) "$(GREEN)Generating endian-bitwidth $(NO_COL)\n" @@ -720,6 +746,13 @@ $(BUILD_DIR)/%.o: $(BUILD_DIR)/%.c @$(CC_CHECK) $(CC_CHECK_CFLAGS) -MMD -MP -MT $@ -MF $(BUILD_DIR)/$*.d $< $(V)$(CC) -c $(CFLAGS) -o $@ $< +ifeq ($(TARGET_NDS),1) +$(BUILD_DIR)/arm7/%.o: %.c + $(call print,Compiling:,$<,$@) + @$(CC_CHECK) $(ARM7_CC_CHECK_CFLAGS) -MMD -MP -MT $@ -MF $(BUILD_DIR)/arm7/$*.d $< + $(V)$(CC) -c $(ARM7_CFLAGS) -o $@ $< +endif + # Alternate compiler flags needed for matching ifeq ($(COMPILER),ido) $(BUILD_DIR)/levels/%/leveldata.o: OPT_FLAGS := -g @@ -797,9 +830,10 @@ $(BUILD_DIR)/rsp/%.bin $(BUILD_DIR)/rsp/%_data.bin: rsp/%.s # Build NDS ROM ifeq ($(TARGET_NDS),1) -$(ROM): $(O_FILES) $(MIO0_FILES:.mio0=.o) $(ULTRA_O_FILES) $(GODDARD_O_FILES) - $(LD) -L $(BUILD_DIR) -o $@.elf $(O_FILES) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(LDFLAGS) - ndstool -c $@ -9 $@.elf +$(ROM): $(O_FILES) $(ARM7_O_FILES) $(MIO0_FILES:.mio0=.o) $(ULTRA_O_FILES) $(GODDARD_O_FILES) + $(LD) -L $(BUILD_DIR) -o $@.arm9.elf $(O_FILES) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(LDFLAGS) + $(LD) -L $(BUILD_DIR) -o $@.arm7.elf $(ARM7_O_FILES) $(ARM7_LDFLAGS) + ndstool -c $@ -9 $@.arm9.elf -7 $@.arm7.elf else # Run linker script through the C preprocessor diff --git a/README.md b/README.md index c94fb712..eeb176be 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,9 @@ A prior copy of the game is required to extract the assets. ## Linux installation * First follow [the guide for installing devkitPro packages](https://devkitpro.org/wiki/Getting_Started), also installing the `nds-dev` group as mentioned * Install the needed tools - * Debian/Ubuntu: `sudo apt install -y build-essential git python` - * Fedora: `sudo dnf install gcc make git python` - * Arch/derivatives like Manjaro: `sudo pacman -S base-devel git python` + * Debian/Ubuntu: `sudo apt install -y build-essential git python sox` + * Fedora: `sudo dnf install gcc make git python sox` + * Arch/derivatives like Manjaro: `sudo pacman -S base-devel git python sox` * Clone this repository and change to its directory ``` git clone https://github.com/Hydr8gon/sm64.git diff --git a/src/audio/external.c b/src/audio/external.c index 1b3e1016..aa0f6cf2 100644 --- a/src/audio/external.c +++ b/src/audio/external.c @@ -433,7 +433,6 @@ extern void func_802ad74c(u32 bits, u32 arg); extern void func_802ad770(u32 bits, s8 arg); static void update_background_music_after_sound(u8 bank, u8 soundIndex); -static void update_game_sound(void); static void fade_channel_volume_scale(u8 player, u8 channelId, u8 targetScale, u16 fadeTimer); void process_level_music_dynamics(void); static u8 begin_background_music_fade(u16 fadeDuration); @@ -1341,7 +1340,7 @@ void audio_signal_game_loop_tick(void) { /** * Called from threads: thread4_sound, thread5_game_loop (EU and SH only) */ -static void update_game_sound(void) { +void update_game_sound(void) { u8 soundStatus; u8 i; u8 soundId; diff --git a/src/audio/external.h b/src/audio/external.h index ea33ceb7..a9edaedc 100644 --- a/src/audio/external.h +++ b/src/audio/external.h @@ -32,6 +32,7 @@ struct SPTask *func_sh_802f5a80(void); #endif void play_sound(s32 soundBits, f32 *pos); void audio_signal_game_loop_tick(void); +void update_game_sound(void); void seq_player_fade_out(u8 player, u16 fadeDuration); void fade_volume_scale(u8 player, u8 targetScale, u16 fadeDuration); void seq_player_lower_volume(u8 player, u16 fadeDuration, u8 percentage); diff --git a/src/audio/heap.c b/src/audio/heap.c index b830f154..5e9ac322 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1314,6 +1314,10 @@ void audio_reset_session(void) { #endif gNotes = soundAlloc(&gNotesAndBuffersPool, gMaxSimultaneousNotes * sizeof(struct Note)); +#ifdef TARGET_NDS + // Point to the uncached RAM mirror so both CPUs can access the data reliably + gNotes = (struct Note*)((u32)gNotes + 0xA000000); +#endif note_init_all(); init_note_free_list(); diff --git a/src/audio/seqplayer.c b/src/audio/seqplayer.c index a5a92b84..cbf40e93 100644 --- a/src/audio/seqplayer.c +++ b/src/audio/seqplayer.c @@ -2750,7 +2750,7 @@ void process_sequences(UNUSED s32 iterationsRemaining) { #endif } } -#if defined(VERSION_JP) || defined(VERSION_US) +#if (defined(VERSION_JP) || defined(VERSION_US)) && !defined(TARGET_NDS) reclaim_notes(); #endif process_notes(); diff --git a/src/nds/arm7/main.c b/src/nds/arm7/main.c new file mode 100644 index 00000000..01b56240 --- /dev/null +++ b/src/nds/arm7/main.c @@ -0,0 +1,53 @@ +#include "../nds_include.h" + +#include "nds_audio.h" + +struct Note *gNotes; +static bool running; + +static void send_input(void) { + inputGetAndSend(); +} + +static void update_audio(void) { + // Request an audio update from the ARM9 + IPC_SendSync(0); + swiIntrWait(0, IRQ_IPC_SYNC); + + // Play the current notes + play_notes(gNotes); +} + +static void power_down(void) { + running = false; +} + +int main(void) { + irqInit(); + fifoInit(); + touchInit(); + + readUserSettings(); + installSystemFIFO(); + setPowerButtonCB(power_down); + + SetYtrigger(80); + irqSet(IRQ_VCOUNT, send_input); + irqEnable(IRQ_VCOUNT | IRQ_IPC_SYNC); + + // Get a pointer to the audio data from the ARM9 + while (!fifoCheckValue32(FIFO_USER_01)); + gNotes = (struct Note*)fifoGetValue32(FIFO_USER_01); + + // Prepare to update the audio at 240 Hz + enableSound(); + timerStart(0, ClockDivider_64, TIMER_FREQ_64(240), update_audio); + running = true; + + // Wait idly for interrupts + while (running) { + swiWaitForVBlank(); + } + + return 0; +} diff --git a/src/nds/arm7/nds_audio.c b/src/nds/arm7/nds_audio.c new file mode 100644 index 00000000..d62df0f4 --- /dev/null +++ b/src/nds/arm7/nds_audio.c @@ -0,0 +1,48 @@ +#include "../nds_include.h" + +#include "nds_audio.h" + +static u32 calculate_vol_pan(struct Note *note) { + // Calculate the DS volume and pan values for a note + u32 vol = (note->targetVolLeft + note->targetVolRight) / 2; + u32 pan = (vol << 14) / note->targetVolLeft; + vol >>= 7; + pan >>= 8; + if (vol > 127) vol = 127; + if (pan > 127) pan = 127; + return SOUND_VOL(vol) | SOUND_PAN(pan); +} + +void play_notes(struct Note *notes) { + // Play notes on the 16 sound channels of the DS + // The samples are converted to DS ADPCM at compile time, so they can be played directly + for (int i = 0; i < 16; i++) { + struct Note *note = ¬es[i]; + + if (note->enabled && note->sound != NULL) { + if (note->needsInit) { + const struct AudioBankSample *sample = note->sound->sample; + const u32 loop = (sample->loop->count ? SOUND_REPEAT : SOUND_ONE_SHOT); + + // 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) = SOUND_FREQ((u16)(note->frequency * 32768)); + 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) = SOUND_FREQ((u16)(note->frequency * 32768)); + SCHANNEL_CR(i) = (SCHANNEL_CR(i) & ~(SOUND_VOL(127) | SOUND_PAN(127))) | calculate_vol_pan(note); + } + } else { + // Disable the channel if no note should play + SCHANNEL_CR(i) &= ~SCHANNEL_ENABLE; + } + } +} diff --git a/src/nds/arm7/nds_audio.h b/src/nds/arm7/nds_audio.h new file mode 100644 index 00000000..809bc972 --- /dev/null +++ b/src/nds/arm7/nds_audio.h @@ -0,0 +1,8 @@ +#ifndef NDS_AUDIO_H +#define NDS_AUDIO_H + +#include "audio/load.h" + +extern void play_notes(struct Note *notes); + +#endif // NDS_AUDIO_H diff --git a/src/nds/main.c b/src/nds/main.c new file mode 100644 index 00000000..45c71925 --- /dev/null +++ b/src/nds/main.c @@ -0,0 +1,86 @@ +#include + +#include "nds_include.h" +#include + +#include "audio/data.h" +#include "audio/external.h" +#include "audio/load.h" +#include "audio/seqplayer.h" +#include "game/game_init.h" +#include "nds_renderer.h" + +OSMesg D_80339BEC; +OSMesgQueue gSIEventMesgQueue; + +s8 gResetTimer; +s8 D_8032C648; +s8 gDebugLevelSelect; +s8 gShowProfiler; +s8 gShowDebugText; + +u8 audio_state; +static u8 audio_step; + +void set_vblank_handler(UNUSED s32 index, UNUSED struct VblankHandler *handler, UNUSED OSMesgQueue *queue, UNUSED OSMesg *msg) { +} + +void dispatch_audio_sptask(UNUSED struct SPTask *spTask) { +} + +void send_display_list(struct SPTask *spTask) { + draw_frame((Gfx*)spTask->task.t.data_ptr); +} + +static void update_audio(void) { + // Update audio at the ARM7's request + if (audio_state == 0) { + // Update the audio logic at 30 Hz + if ((audio_step = (audio_step + 1) & 7) == 0) { + update_game_sound(); + gAudioFrameCount += 2; + gAudioRandom = ((gAudioRandom + gAudioFrameCount) * gAudioFrameCount); + } + + // Update the sequences at 240 Hz + process_sequences(0); + } else if (audio_state == 1) { + // Disable audio + for (int i = 0; i < 16; i++) { + gNotes[i].enabled = false; + } + audio_state = 2; + } + + // Tell the ARM7 it can go ahead + IPC_SendSync(0); +} + +int main(void) { + static u64 pool[0x165000 / sizeof(u64)]; + main_pool_init(pool, pool + sizeof(pool) / sizeof(pool[0])); + gEffectsMemoryPool = mem_pool_init(0x4000, MEMORY_POOL_LEFT); + + renderer_init(); + +#ifdef LIBFAT + if (!fatInitDefault()) { + printf("Failed to initialize libfat!\n"); + } +#endif + + audio_init(); + sound_init(); + + // Set up audio on the ARM9 side + irqSet(IRQ_IPC_SYNC, update_audio); + irqEnable(IRQ_IPC_SYNC); + + // Give the ARM7 a pointer to the audio data + fifoSendValue32(FIFO_USER_01, (u32)gNotes); + + // Run the game + thread5_game_loop(NULL); + + return 0; +} diff --git a/src/nds/nds_controller.c b/src/nds/nds_controller.c index b7aecbfb..2c66da91 100644 --- a/src/nds/nds_controller.c +++ b/src/nds/nds_controller.c @@ -2,6 +2,8 @@ #include "lib/src/osContInternal.h" +extern u8 audio_state; + s32 osContInit(UNUSED OSMesgQueue *mq, u8 *controllerBits, UNUSED OSContStatus *status) { *controllerBits = 1; return 0; @@ -59,4 +61,8 @@ void osContGetReadData(OSContPad *pad) { touchRead(&pos); pad->button |= (pos.px < 128) ? L_CBUTTONS : R_CBUTTONS; } + + if (keysDown() & KEY_SELECT) { + audio_state = !audio_state; + } } diff --git a/src/nds/nds_main.c b/src/nds/nds_main.c deleted file mode 100644 index 9e553283..00000000 --- a/src/nds/nds_main.c +++ /dev/null @@ -1,48 +0,0 @@ -#include - -#include "nds_include.h" -#include - -#include "audio/external.h" -#include "game/game_init.h" -#include "nds_renderer.h" - -OSMesg D_80339BEC; -OSMesgQueue gSIEventMesgQueue; - -s8 gResetTimer; -s8 D_8032C648; -s8 gDebugLevelSelect; -s8 gShowProfiler; -s8 gShowDebugText; - -void set_vblank_handler(UNUSED s32 index, UNUSED struct VblankHandler *handler, UNUSED OSMesgQueue *queue, UNUSED OSMesg *msg) { -} - -void dispatch_audio_sptask(UNUSED struct SPTask *spTask) { -} - -void send_display_list(struct SPTask *spTask) { - draw_frame((Gfx*)spTask->task.t.data_ptr); -} - -int main(void) { - static u64 pool[0x165000 / sizeof(u64)]; - main_pool_init(pool, pool + sizeof(pool) / sizeof(pool[0])); - gEffectsMemoryPool = mem_pool_init(0x4000, MEMORY_POOL_LEFT); - - renderer_init(); - -#ifdef LIBFAT - if (!fatInitDefault()) { - printf("Failed to initialize libfat!\n"); - } -#endif - - audio_init(); - sound_init(); - - thread5_game_loop(NULL); - - return 0; -} diff --git a/tools/assemble_sound.py b/tools/assemble_sound.py index 9c6598f1..1a63114e 100755 --- a/tools/assemble_sound.py +++ b/tools/assemble_sound.py @@ -165,6 +165,31 @@ def parse_aifc(data, name, fname): loop = parse_aifc_loop(vadpcm_loops) if vadpcm_loops is not None else None return Aifc(name, fname, audio_data, sample_rate, book, loop) +def parse_ima(data, name, fname, bank_name): + sample_rate = None + loop = None + + with open("sound/samples/" + bank_name + "/" + name + ".aiff", "rb") as aiff: + aiff_data = aiff.read() + i = 12 + sections = [] + while i < len(aiff_data): + tp = aiff_data[i : i + 4] + (le,) = struct.unpack(">I", aiff_data[i + 4 : i + 8]) + i += 8 + sections.append((tp, aiff_data[i : i + le])) + i = align(i + le, 2) + + for (tp, aiff_data) in sections: + 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 + loop = Loop(start, end, 1, None) + + return Aifc(name, fname, b"\0\0\0\0" + data, sample_rate, None, loop) + class ReserveSerializer: def __init__(self): @@ -566,22 +591,31 @@ def serialize_ctl(bank, base_ser, is_shindou): # Book book_addr_buf.append(pack("P", ser.size)) - ser.add(pack("ii", aifc.book.order, aifc.book.npredictors)) - for x in aifc.book.table: - ser.add(pack("h", x)) + if aifc.book is None: + ser.add(pack("ii", 0, 0)) + else: + ser.add(pack("ii", aifc.book.order, aifc.book.npredictors)) + for x in aifc.book.table: + ser.add(pack("h", x)) ser.align(16) # Loop loop_addr_buf.append(pack("P", ser.size)) - if aifc.loop is None: - assert sample_len % 9 in [0, 1] - end = sample_len // 9 * 16 + (sample_len % 2) + (sample_len % 9) - ser.add(pack("IIiI", 0, end, 0, 0)) + if aifc.fname.endswith(".ima"): + if aifc.loop is None: + ser.add(pack("IIiI", 0, sample_len, 0, 0)) + else: + ser.add(pack("IIiI", aifc.loop.start, aifc.loop.end, aifc.loop.count, 0)) else: - ser.add(pack("IIiI", aifc.loop.start, aifc.loop.end, aifc.loop.count, 0)) - assert aifc.loop.count != 0 - for x in aifc.loop.state: - ser.add(pack("h", x)) + if aifc.loop is None: + assert sample_len % 9 in [0, 1] + end = sample_len // 9 * 16 + (sample_len % 2) + (sample_len % 9) + ser.add(pack("IIiI", 0, end, 0, 0)) + else: + ser.add(pack("IIiI", aifc.loop.start, aifc.loop.end, aifc.loop.count, 0)) + assert aifc.loop.count != 0 + for x in aifc.loop.state: + ser.add(pack("h", x)) ser.align(16) env_name_to_addr = {} @@ -992,14 +1026,15 @@ def main(): entries = [] for f in sorted(os.listdir(dir)): fname = os.path.join(dir, f) - if not f.endswith(".aifc"): - continue try: with open(fname, "rb") as inf: data = inf.read() - entries.append(parse_aifc(data, f[:-5], fname)) + if f.endswith(".aifc"): + entries.append(parse_aifc(data, f[:-5], fname)) + elif f.endswith(".ima"): + entries.append(parse_ima(data, f[:-4], fname, name)) except Exception as e: - fail("malformed AIFC file " + fname + ": " + str(e)) + fail("malformed audio file " + fname + ": " + str(e)) if entries: sample_bank = SampleBank(name, entries) sample_banks.append(sample_bank)