概要
マイク搭載ボードが3つあったので、マイクの取得方法とデータの比較をしてみました。簡易調査なのであとで録音してから更に調査したいと思います。
※ここに書いてあるスケッチは正しくありません。次回作以降で録音データを確認しながらチューニングをしていきます
M5Stack Fire
はじめてのM5StackはBASICかGRAYにする予定でしたのですが、マイクがほしかったのでFireを買ってみました。
赤いFireにはスピーカーとマイクが搭載されています。スピーカーはノイズをよく拾うのでスケッチを転送するときなどに音がします。。。
マイクはアナログ接続なのでADCを使って定期的に取得する方法になるのですが、I2S経由でも取得が可能です。
スケッチ例
// https://github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino
#include <driver/i2s.h>
#define I2S_NUM I2S_NUM_0 // 0 or 1
#define I2S_SAMPLE_RATE 16000
#define I2S_BUFFER_SIZE 512
#define ADC_INPUT ADC1_GPIO34_CHANNEL // ADC CHANNEL
#define ADC_UNIT ADC_UNIT_1 // ADC1 or ADC2
int16_t i2s_sambles[I2S_BUFFER_SIZE];
void i2sMicInit() {
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
.sample_rate = I2S_SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ALL_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S_MSB,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 4,
.dma_buf_len = 512,
.use_apll = false,
.tx_desc_auto_clear = false,
.fixed_mclk = 0
};
i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
i2s_set_adc_mode(ADC_UNIT, ADC1_GPIO34_CHANNEL);
i2s_set_clk(I2S_NUM, I2S_SAMPLE_RATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO);
i2s_adc_enable(I2S_NUM);
}
void setup() {
Serial.begin(115200);
delay(100);
i2sMicInit();
}
void loop() {
size_t bytes_read;
i2s_read(I2S_NUM, (void *)i2s_sambles, sizeof(i2s_sambles), &bytes_read, portMAX_DELAY);
// Display only the first data
int16_t sample = 0x0fff - (i2s_sambles[0] & 0x0fff) - 0x0800;
Serial.printf("%4d\n", sample);
}
I2S経由で取得した場合には、指定のサンプリングレートで自動的にデータを取得してくれます。自分でタイマーを用意する必要がないので楽ですね。
I2S_MODE_ADC_BUILT_INというオプションを渡して、ADCの設定をするのが特徴になります。ADCで取得した場合は0x6812みたいなデータが取得されて、先頭の6の部分はADCのチャンネル番号で、下3桁がデータになります。また、0xfffが最小で0x000が最大の、反転したデータになっているそうです。マイクなので補正しなくても大丈夫だと思いますが一応補正してあります。
1秒で16000サンプリングですので、1ミリ秒あたり16サンプリング。1サンプリングは2バイトになります。一度に512バイト取得しているので、256サンプリングで、16ミリ秒間のデータを取得しています。
表示部分は先頭の1サンプリング分だけ表示する簡易的な表示になります。全部表示するとすぐに画面が流れちゃうので、全体の傾向をみるためのサンプリング表示になります。
プロット

声を出すと、それっぽく表示されています。しかしマイナス方向にオフセットされていますね。
M5StickC
M5StickCのマイクは検証したことがあるので、なんとなく結果がわかっています。。。
スケッチ例
#include <M5StickC.h>
#include <driver/i2s.h>
#define I2S_NUM I2S_NUM_0 // 0 or 1
#define I2S_SAMPLE_RATE 16000
#define I2S_BUFFER_SIZE 512
#define I2S_PIN_CLK I2S_PIN_NO_CHANGE
#define I2S_PIN_WS 0
#define I2S_PIN_DOUT I2S_PIN_NO_CHANGE
#define I2S_PIN_DIN 34
int16_t i2s_sambles[I2S_BUFFER_SIZE];
void i2sMicInit() {
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM),
.sample_rate = I2S_SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ALL_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 4,
.dma_buf_len = 256,
.use_apll = false,
.tx_desc_auto_clear = false,
.fixed_mclk = 0
};
i2s_pin_config_t pin_config = {
.bck_io_num = I2S_PIN_CLK,
.ws_io_num = I2S_PIN_WS,
.data_out_num = I2S_PIN_DOUT,
.data_in_num = I2S_PIN_DIN,
};
i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM, &pin_config);
}
void setup() {
M5.begin();
i2sMicInit();
}
void loop() {
size_t bytes_read;
i2s_read(I2S_NUM, (void *)i2s_sambles, sizeof(i2s_sambles), &bytes_read, portMAX_DELAY);
// Display only the first data
Serial.printf("%6d\n", i2s_sambles[0]);
}
標準的なI2Sですが、PDMマイクなのでクロックのかわりにWS端子を使ってクロックの制御をしています。また、M5StickCのマイクはAXP192の初期化をしないと、電源が供給されないので注意してください。
プロット

安定のオフセットですね。。。手元にある4台を確認したところ-1500から-6000ぐらいの幅でオフセットがありました。個体差が激しいですね。。。
ESP-EYE
ESP32を開発したEspressif Systems社の純正開発ボードです。カメラとマイクが付いているモデルになります。たぶん国内で取り扱っている店舗はないかな?
Amazonのリンクは画像を見るためのものなので、ここからは買わないと方がいいと思います、、、
国内だとマルツさんでDigi-Key経由で取り寄せができますが、6000円以上にしてDigi-Keyから直接購入してみました。Digi-KeyやMouserでは2300円前後で、マルツ経由だと2800円前後+送料500円になります。6000円未満だとDigi-KeyやMouserは送料が2000円かかるので、単品だとマルツのほうがいいと思います。
#include <driver/i2s.h>
#define I2S_NUM I2S_NUM_0 // 0 or 1
#define I2S_SAMPLE_RATE 16000
#define I2S_BUFFER_SIZE 512
#define I2S_PIN_CLK 26
#define I2S_PIN_WS 32
#define I2S_PIN_DOUT I2S_PIN_NO_CHANGE
#define I2S_PIN_DIN 33
int16_t i2s_sambles[I2S_BUFFER_SIZE];
void i2sMicInit() {
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = I2S_SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 4,
.dma_buf_len = 256,
.use_apll = false,
.tx_desc_auto_clear = false,
.fixed_mclk = 0
};
i2s_pin_config_t pin_config = {
.bck_io_num = I2S_PIN_CLK,
.ws_io_num = I2S_PIN_WS,
.data_out_num = I2S_PIN_DOUT,
.data_in_num = I2S_PIN_DIN,
};
i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM, &pin_config);
}
void setup() {
Serial.begin(115200);
delay(100);
i2sMicInit();
}
void loop() {
size_t bytes_read;
i2s_read(I2S_NUM, (void *)i2s_sambles, sizeof(i2s_sambles), &bytes_read, portMAX_DELAY);
// Display only the first data
Serial.printf("%6d\n", i2s_sambles[0]);
}
M5StickCとは接続しているI2Sマイクが違うのでパラメータも変わります。モノラルマイクなのですが、左チャンネルしかデータが取れません。ブログを検索するとデータシートは左だけれど右でしか録音できないって人もいました(怖い)。ちなみにM5StickCのは右でも左でも同じデータが取得できます。
プロット

すごいです、ちゃんと0が原点になっています! 数値もすごく安定していました。さすがに音声入力用に設計されているボードですね。
まとめ
思ったよりマイクの取得方法は難しかったです。3つとも取得方法が違っていました。このへんの情報は非常に少ないのでわかりにくいですね。




コメント