FIFO API(2010.1.20に書いて消してしまった分)
任天堂DSで動くプログラムを作ることができる開発ツールDevkitproの話です。NDSはARM9とARM7の2つのCPUが同時に動いていて、お互いに通信しながら処理を行っています。
Devkitproでは、fifoを使ってARM9とARM7間でデータをやり取りするAPIが用意されています。
普段Devkitproを用いてプログラムを組んでいる分には、ARM7を意識することなくプログラムが組めると思います。今回、サウンドの再生について気に入らないことがあったので、FIFO APIを使ってサウンドの部分を修正していました。
libnds1.4.0になって、ずいぶん、FIFO API回りもわかりやすくなっていると思います。今回行ったことをメモ程度に書いておきます。
サウンドの不具合
libnds1.4.0のsoundAPIを用いて音楽を鳴らすと、長い曲が再生できないと言う不具合がありました。原因は、前回の日記で書いたように、データサイズを格納する部分がu16だからです。そこで、これを書き直しました。とりあえず、現状のライブラリはそのままにして、新たに、サウンド用のFIFO APIを作りました。
FifoChannels
FIFO APIには、Fifo通信につかうチャネルがいくつか用意されています。
/*! \file fifocommon.h \brief low level FIFO API. */ typedef enum { FIFO_PM = 0, FIFO_SOUND = 1, FIFO_SYSTEM = 2, FIFO_MAXMOD = 3, FIFO_DSWIFI = 4, FIFO_RSDV_01 = 5, FIFO_RSVD_02 = 6, FIFO_RSVD_03 = 7, FIFO_USER_01 = 8, FIFO_USER_02 = 9, FIFO_USER_03 = 10, FIFO_USER_04 = 11, FIFO_USER_05 = 12, FIFO_USER_06 = 13, FIFO_USER_07 = 14, FIFO_USER_08 = 15, } FifoChannels;
これをみると、FIFO_USER_xxというのがあり、たぶん、ユーザがFIFO API使ったプログラムが作れるように用意してくれているものと思います。
そこで、これを使って、新たにsound2.cという書き直しのapiを作ることにしました。
Fifo通信
FIFO APIは、共有のメモリ領域にデータ(FIFO APIではMessageと言っています)を置いて、割り込みを使って、ARM9からARM7に処理情報を送り、その割り込み内でMessageを読み出して、ARM7内でいろいろと処理をすることができる仕組みを提供しています。そのときに、使われる関数が下記です。
bool fifoSendDatamsg(int channel, int num_bytes, u8 * data_array); int fifoGetDatamsg(int channel, int buffersize, u8 * destbuffer); bool fifoSendValue32(int channel, u32 value32); u32 fifoGetValue32(int channel);
これらを使えば、メッセージや32bit数値を通信して読み出すことができます。
Messages
通信するメッセージは、typedefで構造体を定義すれば言いだけです。今回は、何も考えずに、既にあるFifoMessage構造体をFifoMessage2として書き直しました。ARM9側のヘッダの部分です。DataSizeとloopPointを32ビットに拡張しています。
typedef struct FifoMessage2 { u16 type; union { struct { u16 type; const void* data; u32 loopPoint; u32 dataSize; u16 freq; u8 volume; u8 pan; bool loop; u8 format; } SoundPlay; struct{ u16 freq; u8 dutyCycle; u8 volume; u8 pan; } SoundPsg; struct{ void* buffer; u32 bufferLength; u16 freq; u8 format; } MicRecord; struct{ void* buffer; u32 length; } MicBufferFull; struct{ touchPosition touch; u16 keys; } SystemInput; }; } ALIGN(4) FifoMessage2; void soundEnable2(void); void soundDisable2(void); int soundPlayPSG2(DutyCycle cycle, u16 freq, u8 volume, u8 pan); int soundPlayNoise2(u16 freq, u8 volume, u8 pan); int soundPlaySample2(const void* data, SoundFormat format, u32 dataSize, u16 freq, u8 volume, u8 pan, bool loop, u32 loopPoint); void soundPause2(int soundId); void soundKill2( int soundId ); void soundResume2(int soundId); void soundSetVolume2(int soundId, u8 volume); void soundSetPan2(int soundId, u8 pan); void soundSetFreq2(int soundId, u16 freq); void soundSetWaveDuty2(int soundId, DutyCycle cycle); void micBufferHandler2(int bytes, void* user_data); int soundMicRecord2(void *buffer, u32 bufferLength, MicFormat format, int freq, MicCallback callback); void soundMicOff2(void);
ARM7側も、全く同じ構造体を宣言します。
割り込みはFIFO_USER_05
今回は、割り込みにFIFO_USER_05を使いました。例えば、ARM9側からの呼び出しは下記のような感じです。
int soundPlaySample2(const void* data, SoundFormat format, u32 dataSize, u16 freq, u8 volume, u8 pan, bool loop, u32 loopPoint){ FifoMessage2 msg; msg.type = SOUND_PLAY_MESSAGE; msg.SoundPlay.data = data; msg.SoundPlay.freq = freq; msg.SoundPlay.volume = volume; msg.SoundPlay.pan = pan; msg.SoundPlay.loop = loop; msg.SoundPlay.format = format; msg.SoundPlay.loopPoint = loopPoint; msg.SoundPlay.dataSize = dataSize >> 2; fifoSendDatamsg(FIFO_USER_05, sizeof(msg), (u8*)&msg); while(!fifoCheckValue32(FIFO_USER_05)); return (int)fifoGetValue32(FIFO_USER_05); }
ARM7側の処理は、下記のような感じになります。
//--------------------------------------------------------------------------------- void soundDataHandler2(int bytes, void *user_data) { //--------------------------------------------------------------------------------- int channel = -1; FifoMessage2 msg; fifoGetDatamsg(FIFO_USER_05, bytes, (u8*)&msg); if(msg.type == SOUND_PLAY_MESSAGE) { channel = getFreeChannel2(); if(channel >= 0) { SCHANNEL_SOURCE(channel) = (u32)msg.SoundPlay.data; SCHANNEL_REPEAT_POINT(channel) = (u32)msg.SoundPlay.loopPoint; SCHANNEL_LENGTH(channel) = msg.SoundPlay.dataSize; SCHANNEL_TIMER(channel) = SOUND_FREQ( msg.SoundPlay.freq ); SCHANNEL_CR(channel) = SCHANNEL_ENABLE | SOUND_VOL(msg.SoundPlay.volume) | SOUND_PAN(msg.SoundPlay.pan) | (msg.SoundPlay.format << 29) | (msg.SoundPlay.loop ? SOUND_REPEAT : SOUND_ONE_SHOT); } } else if(msg.type == SOUND_PSG_MESSAGE) { channel = getFreePSGChannel2(); if(channel >= 0) { SCHANNEL_CR(channel) = SCHANNEL_ENABLE | msg.SoundPsg.volume | SOUND_PAN(msg.SoundPsg.pan) | (3 << 29) | (msg.SoundPsg.dutyCycle << 24); SCHANNEL_TIMER(channel) = SOUND_FREQ( (u16)msg.SoundPsg.freq ); } } else if(msg.type == SOUND_NOISE_MESSAGE) { channel = getFreeNoiseChannel2(); if(channel >= 0) { SCHANNEL_CR(channel) = SCHANNEL_ENABLE | msg.SoundPsg.volume | SOUND_PAN(msg.SoundPsg.pan) | (3 << 29); SCHANNEL_TIMER(channel) = SOUND_FREQ( (u16)msg.SoundPsg.freq ); } } else if(msg.type == MIC_RECORD_MESSAGE) { micStartRecording(msg.MicRecord.buffer, msg.MicRecord.bufferLength, (u16)msg.MicRecord.freq, 1, msg.MicRecord.format, micSwapHandler2); channel = 17; } fifoSendValue32(FIFO_USER_05, (u32)channel); }
ARM7側のFifo起動
ARM7側のプログラムでFifo処理の初期化を行う必要があります。初期化プログラムは下記のような感じです。
//--------------------------------------------------------------------------------- void installSoundFIFO2(void) { //--------------------------------------------------------------------------------- fifoSetDatamsgHandler(FIFO_USER_05, soundDataHandler2, 0); fifoSetValue32Handler(FIFO_USER_05, soundCommandHandler2, 0); }
ARM7のメインプログラムは下記のような感じです。
//************************************************************************ // ARM7側の処理 2010.1.19 //************************************************************************ #include <nds.h> #include <dswifi7.h> //#include <maxmod7.h> #include "FIFO_USER_05/audio2.h" //--------------------------------------------------------------------------------- void VcountHandler() { //--------------------------------------------------------------------------------- inputGetAndSend(); } //--------------------------------------------------------------------------------- void VblankHandler(void) { //--------------------------------------------------------------------------------- //Wifi_Update(); } //--------------------------------------------------------------------------------- int main() //--------------------------------------------------------------------------------- { irqInit(); //irqの初期化 fifoInit(); //fifoの初期化 readUserSettings(); //ファームからユーザセッティングを読込む initClockIRQ(); //RTCのirqへのトラッキング開始 SetYtrigger(80); // installWifiFIFO(); //Wifiのfifoの初期化と開始 installSystemFIFO(); //システム関連のfifo初期化と開始 installSoundFIFO2(); //サウンドのfifoの初期化と開始 //irqの設定 irqSet(IRQ_VCOUNT, VcountHandler); irqSet(IRQ_VBLANK, VblankHandler); //irq割り込みの開始 irqEnable( IRQ_VBLANK | IRQ_VCOUNT | IRQ_NETWORK); // Keep the ARM7 mostly idle while (1) swiWaitForVBlank(); return( 0 ); }
ざっくりした説明ですが、これで、無事、大きな音楽データも鳴らすことができるようになりました。そのうち、どこかにソースを上げておきますね。
本当にやりたいことリスト
(ブログの終わりにやりたいことを書いておきたいと思います)
- 求職活動・・・このブログで興味を持った人一声かけてください。m(_ _)m
- Androidプログラム
- Web系のプログラム