Arduinoで音声再生ができるか試してみました。
概要は動画を作ったので、
こちらをご覧下さい。
【ニコ動】
http://www.nicovideo.jp/watch/sm19540105
【その1:きっかけ】
秋月電気で「音声合成LSI」というLSIが
売られています。
このLSIは、LSIのシリアル入力に対して
「ohayou」
という文字を送ると、出力ピンにつないだスピーカーから
「おはよう」
という音声が出てくる、という、すぐれものです。
面白い。
ところでこの「音声合成LSI」ですが、
中身はアトメルの「ATmega328」で
Arduino UnoにのっかってるCPUと同じもの、らしい。
つうことは、
Arduino Unoもしゃべるんだろーか?
と思ったわけです。
【その2:Arduinoの標準関数だけだとしゃべれない】
「Arduinoにしゃべらせる」ためには、
Arduinoにアナログ信号を出力させて
それを制御する必要があります。
Arduino Unoはアナログ出力(と呼ばれている)
機能を持っています。
ただし、Arduino Unoのアナログ出力は、
純粋なアナログ出力ではなくて、
PWMを使った擬似的なアナログ出力です。
analogWrite(pin, value);
valueの所に0~255の数字を入れると
PWMのデューティ比
(出力矩形波のHighになっている時間)
が変わります。
デューティ比を変化させることで音声波形を作り、
それをスピーカーにつなげてしゃべらせよう、
というのが、基本的な考え。
(「音声合成LSI」も、このしくみの、はず。)
ところでArduino UnoのanalogWrite()で作れる
出力矩形波の周期は490Hz(固定)です。
人間の可聴範囲は20Hz~20kHzなので
Arduino Unoでは
デューティ比をどのように設定しても
490Hzの「ぷー」という音になってしまう。
PWMで音声出力を作るためには、
PWMの周期を可聴範囲より高い周波数(20kHz以上)にする
必要があります。
しかし困ったことに、Arduinoの標準関数に
PWMの周期を変更する関数がありません。
つまり、Arduinoの標準関数では
Arduino Unoにお話させることはできない、
ということになります。
・・・できねえじゃねえか。
しかし事実として(アトメルのATmega328を使っている)
「音声合成LSI」がおしゃべりしているので、
PWMの周期を高速化する方法があるはずだ~~と
調べてみました。
【その3:PWMの周期を高速化してみた】
ATmega328の仕様云々はさておいて、
簡単にPWMの周期を高速化できることが
わかりました。
結果だけを書きますと、Arduinoのスケッチに
pinMode( 3,OUTPUT); // *** D03 PWM OUTPUT
TCCR2A = 0b00100011; // *** 高速PWM設定
TCCR2B = 0b00011001;
OCR2A = 255;
// *** 周期
// *** 1count=0.0625us
// *** 0.0625*256 -> 16us
// *** -> 62.5kHz
OCR2B = 0; // *** Duty 0 - 255
と書くだけでPWMの周期を62.5kHzにすることができます。
「OCR2B」に入れる値(0~255)がデューティになり、
「OCR2B」を変化させることで任意の音声波形を作ることが
できます。
【その4:Arduinoをしゃべらせてみた】
上記を使って、Arduinoに
しゃべらせることができました。
Arduinoのスケッチ例です。
// ***********************************************
// *** Arduinoをしゃべらせてみた
// ***********************************************
#include <avr/pgmspace.h>
prog_uchar KUGI[] PROGMEM ={
127,
127,
・
・(8kHzサンプリング8bitのPCMデータを入れます)
・
127,
127
};
// ***********************************************
void setup() {
// ***-----------------------------------------***
// *** D03 OUTPUT PWM設定
// ***-----------------------------------------***
pinMode( 3,OUTPUT); // *** D03 PWM OUTPUT
TCCR2A = 0b00100011; // *** 高速PWM設定
TCCR2B = 0b00011001;
OCR2A = 255;
// *** 周期
// *** 1count=0.0625us
// *** 0.0625*256 -> 16us
// *** -> 62.5kHz
OCR2B = 0; // *** Duty 0 - 255
// ***-----------------------------------------***
}
// ***********************************************
void loop() {
// ***-----------------------------------------***
int i;
// ***-----------------------------------------***
for(i=0; i<12000; i++){
// ***-----------------------------------------***
OCR2B = pgm_read_byte_near(&KUGI[i]);
delayMicroseconds(125); // *** 8kHzで再生
// ***-----------------------------------------***
}
// ***-----------------------------------------***
}
// ***********************************************
// *** PROGRAM END
// ***********************************************
音声データをPROGMEMであらかじめArduinoのFlashに
書き込んでいます。
めちゃくちゃ簡単なプログラムですが、
D03ピンとGNDにスピーカーをつなげると
Arduinoがしゃべり出します。
問題は・・・
音声データをあらかじめROMに書き込んでおく方法では
ROMが32kBしかないので原理的に
32k×125μs=4秒
以上しゃべることができない・・・ことです。
【その5:SDカードをつなげてみた】
長時間再生するためにはROMを大きくしてやれば
いいわけです。
てなわけで、
SDカードをつなげてしまいました。
400円の2GBのSDカードで
再生時間が(原理的には)70時間にできます。
Arduinoのスケッチはこんな感じ。
// ***********************************************
// *** Arduino SD Card 音楽プレーヤー
// ***-----------------------------------------***
// *** 13 : SCK
// *** 12 : MISO(SD-DO)
// *** 11 : MOSI(SD-DI)
// *** 8 : CS
// *** 3 : AUDO OUT (PWM)
// *** 4 : SW(INPUT)
// ***-----------------------------------------***
// *** SPIライブラリを使用
// *** 8kHz版
// ***********************************************
#include <SPI.h>
#include <SD_Card_hayashi.h> // *** お手製
// ***********************************************
// *** グローバル変数(SDカード)
// ***********************************************
byte SDR[512]; // *** SDカード読込みバッファ
// ***********************************************
// *** SETUP
// ***********************************************
void setup(){
// ***-----------------------------------------***
int cnt;
// ***-----------------------------------------***
pinMode( CS_PIN,OUTPUT); // *** SD-CS
SPI.begin();
SPI.setBitOrder(MSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV4);
SPI.setDataMode(SPI_MODE3);
// ***-----------------------------------------***
// *** D03 OUTPUT PWM設定
// ***-----------------------------------------***
pinMode( 3,OUTPUT); // *** D03 PWM OUTPUT
TCCR2A = 0b00100011; // *** 高速PWM設定
TCCR2B = 0b00011001;
OCR2A = 255;
// *** 周期
// *** 1count=0.0625us
// *** 0.0625*256 -> 16us
// *** -> 62.5kHz
OCR2B = 0; // *** Duty 0 - 255
// ***-----------------------------------------***
// *** SDMicroカードのSPIモード移行と初期化
// ***-----------------------------------------***
SD_INIT();
// ***-----------------------------------------***
// *** SDMicroの1ブロックを512バイトに設定
// ***-----------------------------------------***
CMD16();
}
// ***********************************************
// *** LOOP
// ***********************************************
void loop(){
// ***-----------------------------------------***
int i;
int k;
int cnt;
// ***-----------------------------------------***
// *** SD READ
// ***-----------------------------------------***
for(k=0; k<70; k++ ){
for(i=0; i<256; i=i+2 ){
// ***-----------------------------------------***
cnt = CMD17PWM(0x00,k,i);
// ***-----------------------------------------***
}
}
// ***-----------------------------------------***
}
// ***********************************************
// *** PROGRAM END
// ***********************************************
SDカードの読み書きルーチンは省略しちゃいましたので
雰囲気だけですが、こんな感じのスケッチです。
「なんちゃって」ですが
Arduinoが音楽プレーヤーになりました。
・・・といっても、
・ モノラル
・ 8kHzサンプリング
・ 8bit
なので音質は電話機並みです(笑)。
うれしさはホドホド、な無駄骨。が、しかし、
電子工作開発ネタとしてはたいへん楽しかったです。