この記事はIoTLT Advent Calendar 2016の14日目の記事です。
昨日は @narikei さんのElectronで作ったデスクトップアプリとArduinoでシリアル通信して遊ぼうでした。
はじめに(と言い訳)
今回、はやしよのすけさんの【冬休みの工作】Arduinoをしゃべらせてみたをトレースしつつ、Arduinoを単体でWebAPIサーバにしてAPI叩くとしゃべるオモチャを作ろうと思っていました。
が、結局間に合わなかったのでArduinoをしゃべらせるためのPCMデータのカンタンな作り方解説にしたいと思います。
とりあえず作ったもの
Arduino nanoと圧電スピーカーだけでしゃべらせてみた。 #arduino pic.twitter.com/G1ZQawtmSp
— 写真屋?@ゲムマJ28 (@moomooya) 2016年12月13日
「がんばるぞい」
作り方
基本的には【冬休みの工作】Arduinoをしゃべらせてみたのままです。
#include <avr/pgmspace.h>
// prog_ucharだと上手く行かなかったので、const unsigned charにした
const unsigned char VOICE[] PROGMEM ={
82,73,70,70,7,64,0,0,87,65,86,69,102,109,116,32,
16,0,0,0,1,0,1,0,64,31,0,0,64,31,0,0,
1,0,8,0,100,97,116,97,227,63,0,0,128,127,127,127,
// 中略、ここを作るのがめんどい
131,133,129,122,125,128,127,127,127,127,128,128,127,127,127,127,
127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,
127,127,127,127,127,127,127,127,127,127,127,127,127,127,127
};
void setup() {
pinMode( 3,OUTPUT);
TCCR2A = 0b00100011;
TCCR2B = 0b00011001;
OCR2A = 255;
OCR2B = 0;
for(int i=0; i<16399; i++){ // ココはVOICE[]の要素数にあわせる
OCR2B = pgm_read_byte_near(&VOICE[i]);
delayMicroseconds(125);
}
}
void loop() {
}
VOICE[]
には8bit/8kHzのPCMデータを格納します。
今回はコレの作り方を中心に説明。
PCMデータの作り方
- 適当に音声データを用意
- UnoとかNanoのATmega328搭載のArduinoだとFlashROMが32kBなので約4秒まで
- Audacityとかで以下の設定&Microsoft PCM形式のwavファイルにして保存
- 1ch(モノラル)
- サンプリング周波数8kHz
- サンプリングレート8bit
- wavファイルをコマンドラインで1バイトごとのunsigned型で表示する
wavファイルをコマンドラインで1バイトごとのunsigned型で表示する
ワンライナーが得意な方はコマンド一発で変換してしまってください。macOSでも使えるいいワンライナーが出来たら教えてください。
$ od -tu1 -An zoi.wav | perl -ple 's/^\s+//g' | perl -ple 's/\s+/ /g' > zoi.txt
でこんな感じのデータを出力する。
82 73 70 70 7 64 0 0 87 65 86 69 102 109 116 32
16 0 0 0 1 0 1 0 64 31 0 0 64 31 0 0
1 0 8 0 100 97 116 97 227 63 0 0 128 127 127 127
128 128 127 127 128 128 127 127 128 128 127 127 128 128 127 127
128 128 127 127 128 128 128 128 128 128 128 128 129 128 128 127
128 128 128 128 128 128 128 128 127 128 127 127 127 128 127 127
127 128 127 127 127 127 127 127 127 127 127 127 127 127 126 127
127 127 127 127 127 128 127 127 127 127 128 127 127 127 127 128
128 127 127 128 128 128 128 128 128 128 128 128 128 128 128 128
128 127 128 128 128 128 128 128 128 128 127 127 128 128 127 127
127 128 127 127 127 127 127 127 127 127 127 127 127 127 127 127
(以下略)
空白と行末をカンマに置換して
82,73,70,70,7,64,0,0,87,65,86,69,102,109,116,32,
16,0,0,0,1,0,1,0,64,31,0,0,64,31,0,0,
1,0,8,0,100,97,116,97,227,63,0,0,128,127,127,127,
128,128,127,127,128,128,127,127,128,128,127,127,128,128,127,127,
128,128,127,127,128,128,128,128,128,128,128,128,129,128,128,127,
128,128,128,128,128,128,128,128,127,128,127,127,127,128,127,127,
127,128,127,127,127,127,127,127,127,127,127,127,127,127,126,127,
(中略)
127,127,127,127,127,127,127,127,127,127,127,127,127,127,127
これで先述のVOICE[]
の初期化に使えるようになりました。
あとは好きなようにしゃべらせましょう。
(IoTのI成分がない記事になってしまった……)
ちなみに回し者ではないですが、別の機会にボイス素材作る方法を検討していたときにココナラでわりと低価格でボイス素材の収録をお願いできそうな感じでした。ご参考までに。
明日は @iwata-n@github さんのマインクラフトのTNTを現実世界から爆発させてみる(Wio Nodeを使います)です。