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
なので音質は電話機並みです(笑)。



うれしさはホドホド、な無駄骨。が、しかし、

電子工作開発ネタとしてはたいへん楽しかったです。