singed 8bitは素のデータ構造であって、保存形式としては全然向いていません。もし30分の音楽データを入れたいなと思った場合は30分*60秒=1800秒、1800*16384バイト=29,491,200バイトとなって圧迫感が半端ないです。そこで圧縮しつつもCPU使用率を取らないライブラリを紹介します。
https://pineight.com/gba/#8ad
ADPCM techniques and 6 percent of the GBA's CPU time.がウリです。非常に扱いやすくソースコードも短かった為、サンプルプログラムNO.108 diabo_gbaやNO.106 kanon_gbaに採用しました。オススメの1つです。
https://maxmod.devkitpro.org/
https://github.com/devkitPro/maxmod
devkitProに標準装備されていて非常に有名です。数少ないdevkitProのexamplesプログラムの1つです。libmm.aを組み込みも方法となるmakefileもあるので簡単な方ではあります。MOD, S3M, XM, and ITとwavも対応しています。
https://github.com/stuij/apex-audio-system
当時有名でしたけれど詳しくは不明です。MOD playing routines and support for up to 16 channels.
https://github.com/sebknzl/krawall
当時有名でしたけれど詳しくは不明です。XM/S3M Modplayer.
https://github.com/AntonioND/gbt-player
DirectSoundと関係ないですけれどゲームボーイサウンドとしてならこちらが有名です。GB Studioに組み込まれているのでサウンドエディタはGB Stdioで編集して、再生はライブラリで行うということが可能になりそうです。詳しくは未調査です。
海外圏はmidi文化ではなくmod文化の為、ということなんだと個人的に解釈しています。まーしょうがないですよ。mod編集エディタで作る方が簡単だと思います。ここではMaxModのサンプルプログラムを用意しました。libmm.aを組み込むだけですと味気ないので、MaxModソースコードをコンパイルして、libgccもお世話にならない作りをしています。
ASFLAGS = -DSYS_GBA
// 2023/05/18 reject libgcc. // patched -> _call_via_r7, _call_via_r1, malloc .align 0 .global _call_via_r7 .thumb_func _call_via_r7: bx r7 .align 0 .global _call_via_r1 .thumb_func _call_via_r1: bx r1
mov r6, r1 // r6=#channels ldr r0,=MM_SIZEOF_MODCH+MM_SIZEOF_ACTCH+MM_SIZEOF_MIXCH mul r0, r6 ldr r4,=mixlen add r0, r4 // bl malloc patched 2023/05/18 bl MallocPatch
#include "malloc.h" //--------------------------------------------------------------------------- u8 MallocMem[2000]; //--------------------------------------------------------------------------- EWRAM_CODE u32* MallocPatch(u32 size) { TRACE("MallocPatch: size=%d\n", size); return (u32*)MallocMem; }
#include "libmm/maxmod.h" #include "lib/gba.h" #include "irq.arm.h" #include "bg.h" #include "key.h" #include "res.h" //--------------------------------------------------------------------------- // maxmodライブラリ内の非公開関数 u32 mmMixerChannelActive(mm_word channel); //--------------------------------------------------------------------------- // 効果音の登録(id, rate, handle, volume, panning) mm_sound_effect ambulance = { {SFX_AMBULANCE}, (int)(1.0f * (1<<10)), 0, 255, 0 }; mm_sound_effect boom = { {SFX_BOOM}, (int)(1.0f * (1<<10)), 0, 255, 255 }; // sound effect handle (for cancelling it later) mm_sfxhand amb = 0; //--------------------------------------------------------------------------- IWRAM_CODE int main(void) { REG_WSCNT = 0x4317; BgInit(); KeyInit(); IrqInit(); mmInitDefault((mm_addr)soundbank_bin, 8); mmStart(MOD_FLATOUTLIES, MM_PLAY_LOOP); BgDrawPrintf(0, 0, "MaxMod Audio demo"); BgDrawPrintf(1, 2, "Hold A for ambulance sound"); BgDrawPrintf(1, 3, "Press B for boom sound"); BgDrawPrintf(1, 4, "Press L for music stop"); BgDrawPrintf(1, 5, "Press R for music start"); BgDrawPrintf(1, 8, "CPU Usage"); BgDrawPrintf(1, 9, " +2.6 percent BASE"); BgDrawPrintf(1, 10, " +2 percent 1ch (max 8ch)"); for(;;) { VBlankIntrWait(); // start vcount 0 while(*(vu16*)0x4000006 != 0) {}; // パレット0を変更してcpu使用率の可視化をします // mmVBlank関数は本来、vblank割り込み中に処理を行います *(vu16*)0x5000000 = RGB5(31, 0, 0); mmVBlank(); *(vu16*)0x5000000 = RGB5(0, 0, 31); mmFrame(); *(vu16*)0x5000000 = RGB5( 0, 0, 0); vu32 i, ch = 0; for(i=0; i<8; i++) { if(mmMixerChannelActive(i) != 0) { ch++; } } BgDrawPrintf(1, 12, "Channel Active:%d", ch); KeyExec(); u32 off = KeyGetOff(); u32 trg = KeyGetTrg(); if(trg & KEY_A) amb = mmEffectEx(&ambulance); if(off & KEY_A) mmEffectCancel(amb); if(trg & KEY_B) mmEffectEx(&boom); if(trg & KEY_L) mmStop(); if(trg & KEY_R) mmStart(MOD_FLATOUTLIES, MM_PLAY_LOOP); } }
音楽データを加工するにはmakefileで以下のようにするといいと思います。mmutil.exeと呼ばれる専用ツールが使われています。
soundbank.s : $(WAVFILES) $(MODFILES) @echo \# converting $(WAVFILES) $(MODFILES) @mmutil $^ -osoundbank.bin -hsoundbank.h @bin2s soundbank.bin > $@