音声合成 その3
前回に引き続き音声合成です。
音量の変更
サンプルコードだと音量が大きいので変更しました。
ただ、音が汚くなりました。。。
// hello_aquestalk.ino - AquesTalk pico for ESP32 #include "driver/i2s.h" #include "aquestalk.h" #define LEN_FRAME 32 uint32_t workbuf[AQ_SIZE_WORKBUF]; void setup() { int iret; Serial.begin(115200); Serial.println("Initialize AquesTalk"); iret = CAqTkPicoF_Init(workbuf, LEN_FRAME, "XXX-XXX-XXX"); if(iret){ Serial.println("ERR:CAqTkPicoF_Init"); } DAC_Create(); Serial.println("D/A start"); Play("konnnichiwa."); Play("korewa;te'_sutode_su."); Play("sa'nngatsu/<NUMK VAL=17 COUNTER=nichi> <NUMK VAL=12 COUNTER=ji>/<NUMK VAL=23 COUNTER=funn>."); Play("yukkuri_siteittene?"); DAC_Release(); Serial.println("D/A stop"); } void loop() { } void Play(const char *koe) { Serial.print("Play:"); Serial.println(koe); // ★★★変更点 // int iret = CAqTkPicoF_SetKoe((const uint8_t*)koe, 100, 0xffffU); int iret = CAqTkPicoF_SetKoe((const uint8_t*)koe, 60, 0xffffU); // ★★★変更点 if(iret) Serial.println("ERR:CAqTkPicoF_SetKoe"); for(;;){ int16_t wav[LEN_FRAME]; uint16_t len; iret = CAqTkPicoF_SyntheFrame(wav, &len); if(iret) break; // EOD DAC_Write((int)len, wav); } } //////////////////////////////// //i2s configuration const int i2s_num = 0; // i2s port number i2s_config_t i2s_config = { .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN), .sample_rate = 24000, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_I2S_MSB, .intr_alloc_flags = 0, .dma_buf_count = 4, .dma_buf_len = 384, .use_apll = 0 }; void DAC_Create() { AqResample_Reset(); i2s_driver_install((i2s_port_t)i2s_num, &i2s_config, 0, NULL); i2s_set_pin((i2s_port_t)i2s_num, NULL); } void DAC_Release() { i2s_driver_uninstall((i2s_port_t)i2s_num); //stop & destroy i2s driver } // upsampling & write to I2S int DAC_Write(int len, int16_t *wav) { int i; for(i=0;i<len;i++){ // upsampling x3 int16_t wav3[3]; AqResample_Conv(wav[i], wav3); // write to I2S DMA buffer for(int k=0;k<3; k++){ uint16_t sample[2]; uint16_t us = ((uint16_t)wav3[k])^0x8000U; // signed -> unsigned data sample[0]=sample[1]=us; // mono -> stereo // ★★★変更点 sample[0] = Amplify_edit(sample[0]) + 0x8000; sample[1] = Amplify_edit(sample[1]) + 0x8000; // ★★★変更点 int iret = i2s_push_sample((i2s_port_t)i2s_num, (const char *)sample, 100); if(iret<0) return iret; // -1:ESP_FAIL if(iret==0) break; // 0:TIMEOUT } } return i; } // ★★★変更点 int16_t Amplify_edit(int16_t s) { // 音量設定 int32_t v = (s * (uint8_t)(0.02*(1<<6)))>>6; if (v < -32767) return -32767; else if (v > 32767) return 32767; else return (int16_t)(v&0xffff); } // ★★★変更点
音声合成 その2
前回に引き続き音声合成です。
発話速度の変更
サンプルコードだと速度が速いので変更しました。
// hello_aquestalk.ino - AquesTalk pico for ESP32 #include "driver/i2s.h" #include "aquestalk.h" #define LEN_FRAME 32 uint32_t workbuf[AQ_SIZE_WORKBUF]; void setup() { int iret; Serial.begin(115200); Serial.println("Initialize AquesTalk"); iret = CAqTkPicoF_Init(workbuf, LEN_FRAME, "XXX-XXX-XXX"); if(iret){ Serial.println("ERR:CAqTkPicoF_Init"); } DAC_Create(); Serial.println("D/A start"); Play("konnnichiwa."); Play("korewa;te'_sutode_su."); Play("sa'nngatsu/<NUMK VAL=17 COUNTER=nichi> <NUMK VAL=12 COUNTER=ji>/<NUMK VAL=23 COUNTER=funn>."); Play("yukkuri_siteittene?"); DAC_Release(); Serial.println("D/A stop"); } void loop() { } void Play(const char *koe) { Serial.print("Play:"); Serial.println(koe); // ★★★変更点 // int iret = CAqTkPicoF_SetKoe((const uint8_t*)koe, 100, 0xffffU); int iret = CAqTkPicoF_SetKoe((const uint8_t*)koe, 60, 0xffffU); // ★★★変更点 if(iret) Serial.println("ERR:CAqTkPicoF_SetKoe"); for(;;){ int16_t wav[LEN_FRAME]; uint16_t len; iret = CAqTkPicoF_SyntheFrame(wav, &len); if(iret) break; // EOD DAC_Write((int)len, wav); } } //////////////////////////////// //i2s configuration const int i2s_num = 0; // i2s port number i2s_config_t i2s_config = { .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN), .sample_rate = 24000, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_I2S_MSB, .intr_alloc_flags = 0, .dma_buf_count = 4, .dma_buf_len = 384, .use_apll = 0 }; void DAC_Create() { AqResample_Reset(); i2s_driver_install((i2s_port_t)i2s_num, &i2s_config, 0, NULL); i2s_set_pin((i2s_port_t)i2s_num, NULL); } void DAC_Release() { i2s_driver_uninstall((i2s_port_t)i2s_num); //stop & destroy i2s driver } // upsampling & write to I2S int DAC_Write(int len, int16_t *wav) { int i; for(i=0;i<len;i++){ // upsampling x3 int16_t wav3[3]; AqResample_Conv(wav[i], wav3); // write to I2S DMA buffer for(int k=0;k<3; k++){ uint16_t sample[2]; uint16_t us = ((uint16_t)wav3[k])^0x8000U; // signed -> unsigned data sample[0]=sample[1]=us; // mono -> stereo int iret = i2s_push_sample((i2s_port_t)i2s_num, (const char *)sample, 100); if(iret<0) return iret; // -1:ESP_FAIL if(iret==0) break; // 0:TIMEOUT } } return i; }
音声合成 その1
音声合成で音声出力してみました。
参考サイト
ここを参考にしてみました。
このままだと、音量も大きくて、発話速度も速いままです。
音量と発話速度を改善したい。。。
blog-yama.a-quest.com
m5stackで組込み! スリープ設定
設定した時間内に操作がなかったらスリープするようにしました。
スリープ時間は次の00分までの時間としました。
スリープ処理はこちらの前回書いた記事を参考に作成してます。
m5stack-build.hatenablog.com
動画は設定までですけどこんな感じで作ってます。
スリープモード
今回は自動でスリープモードに入るような設定を作成しました。
deepSleepモード
名前の通り深いスリープです。スリープ復帰後にCPUは再起動されます。
M5.Power.deepSleep(SLEEP_SEC(5));
lightSleepモード
名前の通り軽いスリープです。スリープ復帰後はスリープの次の行から実行されます。
deepSleepに比べ、省電力能力に欠けてしまいます。
M5.Power.lightSleep(SLEEP_SEC(5));
サンプルコード
#include <M5Stack.h> void setup() { // M5Stackの初期化 M5.begin(); // Powerの初期化 M5.Power.begin(); } void loop() { unsigned char aucCnt = 0; // テキストサイズ指定 M5.Lcd.setTextSize(1); for( aucCnt = 0; aucCnt < 100; aucCnt++ ) { M5.Lcd.printf("Cnt = %d\n", aucCnt ); if( 3 == aucCnt ) { M5.Power.lightSleep(SLEEP_SEC(5)); } else if( 6 == aucCnt ) { M5.Power.deepSleep(SLEEP_SEC(5)); } delay(1000); } return; }
いい感じですね!
youtu.be
m5stackで組込み! アラーム設定
アラーム設定した時刻にビープ音を出力できました。
ビープ音を出力
今回はビープ音を出力するプログラミングを作成しました。
音を鳴らすための初期化
ビープ音関係の初期化はM5.begin()
では行っていないのでこちらの関数で初期化しましょう。
私は初期化を忘れてかなりの時間、ハマってました。。。
M5.Speaker.begin();
音を鳴らすためのAPI
音を出すためのAPIはいくつかありますが、今回はM5.Speaker.tone()
を使ってみたら、いきなり爆音!!!!
しかも、音量調節できないみたいです。。。
なので代わりにこちらを使います。
まずは使用するチャンネル設定の関数です。
第一引数に使用するチャンネル
第二引数にPWMの周波数
第三引数にデューティ比を表すビット数
この関数はM5.Speaker.begin()
の中でコールされているので第一、第三引数はそのままで、第二引数の周波数だけ変えてあげればよいです。
ledcSetup( TONE_PIN_CHANNEL, TONE_FREQ, 13);
次に音声音量調整の関数です。
第一引数に使用するチャンネル
第二引数にデューティ比
音量は第二引数のデューティ比を変更して調整します。
ledcWrite( TONE_PIN_CHANNEL, 0x1FF>>(9-ucVol) );
サンプルコード
#include <M5Stack.h> #define TONE_FREQ 900 #define TONE_STOP 0 #define TONE_PLAY 1 static unsigned char gucVol = 0; static unsigned char gucToneSts = 0; void setup() { // M5Stackの初期化 M5.begin(); // Speaker初期化 M5.Speaker.begin(); // BLACK Screen M5.Lcd.fillScreen(BLACK); // 横線 M5.Lcd.drawFastHLine(0, 220, 320, WHITE); // 縦線 M5.Lcd.drawFastVLine(106, 220, 20, WHITE); M5.Lcd.drawFastVLine(219, 220, 20, WHITE); // 矢印 M5.Lcd.fillTriangle( 49, 232, 53, 224, 57, 232,WHITE ); // M5.Lcd.fillTriangle( 159, 224, 167, 228, 159, 232,WHITE ); M5.Lcd.fillTriangle( 269, 224, 273, 232, 277, 224,WHITE ); // テキストサイズ指定 M5.Lcd.setTextSize(1); // START M5.Lcd.setCursor(140, 224); M5.Lcd.print("START"); // ボリューム値 gucVol = 4; // トーン状態 gucToneSts = TONE_STOP; } void loop() { M5.update(); // Aボタン if (M5.BtnA.wasPressed()) { // 再生中 if( TONE_PLAY == gucToneSts) { // 音量UP if( gucVol < 9 ){ gucVol++; } else { gucVol = 9; } SetVolume( gucVol ); } } // Bボタン if (M5.BtnB.wasPressed()) { // 開始 if( TONE_STOP == gucToneSts) { ledcSetup( TONE_PIN_CHANNEL, TONE_FREQ, 13); SetVolume( gucVol ); gucToneSts = TONE_PLAY; // テキストサイズ指定 M5.Lcd.setTextSize(1); // START M5.Lcd.fillRect(107, 221, 87, 19, BLACK); M5.Lcd.setCursor(140, 224); M5.Lcd.print("STOP"); } // 停止 else { // ミュート M5.Speaker.mute(); gucToneSts = TONE_STOP; // テキストサイズ指定 M5.Lcd.setTextSize(1); // START M5.Lcd.fillRect(107, 221, 87, 19, BLACK); M5.Lcd.setCursor(140, 224); M5.Lcd.print("START"); } } // Cボタン if (M5.BtnC.wasPressed()) { // 再生中 if( TONE_PLAY == gucToneSts) { // 音量DOWN if( gucVol > 0 ){ gucVol--; } else { gucVol = 0; } SetVolume( gucVol ); } } //音量 // テキストサイズ指定 M5.Lcd.setTextSize(2); // ENTER M5.Lcd.setCursor(100, 100); M5.Lcd.printf("VOLUME: %d", gucVol); delay(100); return; } static void SetVolume( unsigned char ucVol ) { ledcWrite( TONE_PIN_CHANNEL, 0x1FF>>(9-ucVol) ); return; }
これで爆音でなるビープ音から解放された。
www.youtube.com