PICでWav再生

Nゲージ用のコントローラやレイアウトなどに音を付け加えようとして、MP3ボードを買って、PICマイコンで制御するように製作中。

PICでMP3プレーヤ相当のものを作るにはVS1011eというデコーダICを使っている例がみられるたので、いろいろと検討してみたが、制御するPICやVS1011e、MP3ファイルを格納するSDかメモリチップなどを購入すると千数百円はかかりそう。

一方、マイコンキットドットコムからMK144というMP3 プレーヤーボードが1980円で販売されているが、タクトスイッチや制御用の基板を購入するともう数百円は単価は上がりそう。

費用的には自作のほうがちょっぴりお得か?等々、考えているのであるが、最終的にはMK144を複数買って、それを自作のコントローラで制御しようかなぁ、とゴソゴソと作業中。


まだ、完成したわけではないがフッ..と途中で思った。

駅に停車中のディーゼルカーの「ゴロッ、ゴロッ」というエンジンのアイドリング音や、蒸気機関車のドンキーポンプの音などは単調な音が繰り返しているだけ。

0.5秒程度の「ゴロッ」という音の繰り返しや、数秒程度の繰り返し音などは別にMP3プレーヤーを使って再生させなくてもいいんじゃないかなぁと。
MP3ボードを制御するPICも再生開始や停止を指示するコマンドを送信するのが仕事で、基本的には暇。
ならば、単調な繰り返し音はPICでやってしまおう。
踏切の警報音などもPICで出しているが、格納できるサイズは本当に限られているし、1つの音ごとにPICを使っていてはPICだらけになってしまうので、やっぱ音源はSDカード類から読んで、複数の音源を切り替える必要はあるだろうけど。


まぁ、ひとまずはメモリ上に置いたWavファイルのデータを出力する部分の試作開始。

PIC24FJ64BA002(16ビットマイコン)を使って、数秒程度のWAVファイルをメモリ内に書き込んでおいて再生してみることにした。

一つ持っているマイコンキットドットコムのMP3 プレーヤーボード用コントローラキットMK148は代替品を自作するつもりでお役御免の予定だったが、基板上にはLM386が2つ乗っているし、usart 通信でMP3 プレーヤーボードを制御するための配線も簡単にできるので再度登板。

PIC24FJ64GA002でWav再生

PIC24FJ64GA002を載せたユニバーサル基板をMK148(MP3 プレーヤーボード用コントローラキット)にアクリル板でくっつけてみた。

電源をMK148から採り、PICからの音声の出力はMK148上のLM386でMP3ボードの音声とミキシングして出力。

この次のステップとしてSDカードからWavファイルを読み込ませようとしているので、ユニバーサル基板上にはSDカードスロットを載っけて配線もすませてます。

空き地には、ファイルセレクタ用のスイッチやら、開始、停止用のスイッチなどが入る予定。


・WAVファイルは「8kHz 16bit モノラル」のみを対象
・今回のサンプルプログラムは同じデータをRB0(4ピン)、RB1(5ピン)に出力
・WAVファイルはメモリに格納できるだめのサイズのものを使用してフラッシュメモリに格納

main.c

[c]
/**********************************************
*
* PIC24FJ64GA002
*
* 8kHz sampling , 16bit モノラル Wav再生テスト
* コンパイラ XC16
***********************************************/
#include

// CONFIG2
#pragma config POSCMOD = HS
#pragma config I2C1SEL = PRI
#pragma config IOL1WAY = OFF
#pragma config OSCIOFNC = OFF
#pragma config FCKSM = CSDCMD
#pragma config FNOSC = PRIPLL
#pragma config SOSCSEL = SOSC
#pragma config WUTSEL = LEG
#pragma config IESO = OFF

// CONFIG1
#pragma config WDTPS = PS32768
#pragma config FWPSA = PR128
#pragma config WINDIS = ON
#pragma config FWDTEN = OFF
#pragma config ICS = PGx1
#pragma config GWRP = OFF
#pragma config GCP = OFF
#pragma config JTAGEN = OFF

#include "chime.h"

unsigned int chimeidx;

int playFlag;

/***********************
* タイマ4割り込み処理関数
***********************/
void __attribute__((interrupt, no_auto_psv)) _T4Interrupt(void)
{
int d = 128;

if(playFlag){
// chimeデータの配列は偶数バイトで作成しておくこと
d = chime[chimeidx] | (chime[chimeidx+1]<<8); d = d + 32768; d = d >>8;
d = d & 0xff;
chimeidx += 2;
if(chimeidx>(sizeof chime)){
playFlag = chimeidx = 0;
}
}
OC1RS = (PR2+1)*d/256; // RP0出力
OC2RS = (PR2+1)*d/256; // RP1出力

IFS1bits.T4IF = 0; // 割り込みフラグクリア
}

/**************************
* メイン関数
***************************/
int main(void)
{
playFlag = chimeidx = 0;

CLKDIV = 0;

AD1PCFG = 0xFFFF; // I/Oをすべてデジタルに
TRISB = 0xfffc; // RB0(4ピン)、RB1(5ピン)を音声出力に

CMCON = 0; // コンパレータオフ

// タイマ2設定
T2CON = 0b1000000000000000; // タイマ2モード設定
PR2 = 159; //100kHz

// OC1
RPOR0bits.RP0R = 18; // OC1をRP0(4ピン)に割付け
OC1CON = 0x0006; // タイマ2使用,PWM mode,OCFx disabled
OC1RS = (PR2+1)/2; // 50%

// OC2
RPOR0bits.RP1R = 19; // OC2をRP1(5ピン)に割付け
OC2CON = 0x0006; // タイマ2使用,PWM mode,OCFx disabled
OC2RS = (PR2+1)/2; //50%

// タイマ4モード設定
T4CON = 0b1000000000010000;
PR4 = 249; // 125usec 8kHz
IPC6bits.T4IP = 5; // 割り込みレベル = 5
IFS1bits.T4IF = 0;
IEC1bits.T4IE = 1; // 割り込み許可

while(1){
playFlag = 1;
}
}
[/c]


・WAVファイルは「8kHz 16bit モノラル」で数秒のチャイム音を16進で記述してヘッダファイルとした。
・作成方法は、次のとおり

(1)WAVファイルをバイナリエディタで開く
(2)dataチャンクがあらわれるまで(‘data’の文字列があるのでわかります)を削除
(3)dataチャンクのID(‘data’の文字)とレングス4バイトを削除
(4)その後以降が波形データであるが、末尾の作成したソフトな名前などが入っている場合があるので、その部分があれば削除

(5)1バイト毎に0xと,をつけたかったり。
秀丸等のエディタに貼り付けて、置換コマンドで各バイトの先頭に0xをつけたり、,をつけたり。
1行を80文字くらいで改行したければ、同じく置換コマンドで「^.{80}\f\0\n」を「\0\n」に置換したり。

・以上のファイルをヘッダファイルにして、main関数でインクルード
・ちなみに、今回はチャイム音を使ってみた。

・これは、本番ではSDカードを読んだりするつもりなのだが、エラーがあった場合の切り分け用にLEDをつけたりすると使用できるポートが減ったりするので、エラーがあった場合にはこのチャイム音を鳴らすことにしてみた。
エラーがあったときにこの音がでるようにしておけば、サンプリングなどに問題があって再生できないファイルなのか、再生モジュール自体に問題があって再生ができないようなデグレードがあったのかの切り分けに使用できると思う。

chime.h

/********************************************************************
 チャイム音 Wav 8kHz モノラル 16bit

********************************************************************/
const unsigned char chime[] = {
	// dataチャンクID'data'とサイズの後から、末尾の拡張部分の前までを登録

 0xBD , 0xFF , 0x25 , 0x00 , 0xC5 , 0xFF , 0x97 , 0xFF ,
 0xB9 , 0x00 , 0x46 , 0x00 , 0xD2 , 0xFF , 0x62 , 0x00 ,
 0x85 , 0x01 , 0xD6 , 0xFF , 0xA6 , 0xFB , 0xFF , 0xF8 ,
 0xBA , 0xFD , 0xEF , 0x08 , 0x9A , 0x0B , 0xFF , 0x03 ,
 0x6A , 0x00 , 0x6F , 0xF3 , 0x5A , 0xEE , 0xAC , 0xFC ,
 0xDE , 0x14 , 0x4F , 0x09 , 0x54 , 0xFB , 0xE0 , 0x05 ,
 0x27 , 0x04 , 0xDA , 0xEC , 0x06 , 0xED , 0x42 , 0x02 ,

~
省略
~
  };

ひとまず、音声の出力はできた。

PIC24FJ64BA002の次のステップでは、SDファイルからデータを読んで、それを出力すること。
さらにはusart 通信でMP3 プレーヤーボードを制御すること。

2つのポートから同じ音声を出力しているのは、2ファイルを読みながら音声を出力できるだけの能力があるようにプロクラムが組めて、PICに性能があれば、2種類の音がだせるのではないかともくろんでいるからなのだが。

スイッチをつけてSDファイルから読むデータを切り替えられるようになったら、プラモデルやNゲージのシーナリーなどを置く台の下などに格納できるようにするつもり。

予定では、mp3のBGMが流れると同時にWAVの繰り返し音が流れるのであった。

雪なんかとディーゼルカーがあるセクションではエンジンのアイドリング音と「ぽっぽや」のBMG、三陸鉄道の36系があるセクションではエンジンのアイドリング音と「あまちゃん」のBMS、ハーレーのバイクがある飾り台では3拍子のアイドリング音とBGMは「Born to Be Wild」….

まぁ、そんなことを考えながら、次のステップに進もう。

寄り道が多くて、なかなかNゲージのコントローラなどは完成しないのであるが..


追記
SDカードから読み込んで再生するものを作ってみました。
2ファイルを読みながら、2つの音声を出力することができます。
鉄道模型レイアウト・プラモデル展示台用の音(PICでWAV再生)