音楽データのsinged 8bit 16384hzは、素のデータであって保存形式として向いていません。もし30分の音楽データを用意した場合は30分*60秒=1800秒、1800*16384バイト=29,491,200バイトとなってしまいます。そこで圧縮しつつもCPU使用率を取らないライブラリを以下に紹介します。上2つは使ったことはありますが、他は詳しくありません。ご了承ください。
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に標準装備されていてexamplesフォルダにサンプルコードもあります。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と関係ないですけれどゲームボーイサウンドとして有名です。
海外圏はmidiではなくmod文化の為、ということだと個人的に解釈しています。以下に、MaxModのサンプルプログラムを用意しました。libmm.aを組み込むだけですとdevkitproのサンプルと被るので、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で以下のようにしています。maxmodのmmutil.exeと呼ばれる専用ツールがあるので、1つのバイナリデータに加工してくれます。
soundbank.s : $(WAVFILES) $(MODFILES) @echo \# converting $(WAVFILES) $(MODFILES) @mmutil $^ -osoundbank.bin -hsoundbank.h @bin2s soundbank.bin > $@
もしMaxModを扱うのでしたらMaxmod Programming Referenceは必読書になると思います。ページの後半が詳しいので読んでみてください。