ソフトウエア

Voice driver( コード )

MCUの DAC( Digital to Analog Converter )を使用して発声を行う機能です。発声は 音声合成ライブラリを使用しています。

◀ この記事の前に: Voice driver( 操作説明 )
▶ この記事の次に: 音声合成ライブラリ

概 要

発音を行う処理です。音声合成ライブラリと I2Sを使用して DAC出力を行います。省電力制御も連動します。

モジュールの構造

発音はローマ字表記に近い音声記号列で表した発音データから、音声合成ライブラリにより発音を行います。音声合成ライブラリは音声波形データを作ります。音声波形データは DACによりアナログ信号に変換して出力します。DACは I2Sライブラリで操作します。DAC出力は MCU外部の DAC増幅回路の入力となりスピーカーを鳴らします。
DAC増幅回路の電源は ON/OFF制御することができます。上位には Speak interpreterがあり統合的にアプリケーションから操作することができます。Voice driver 単独でも操作することができます。音声合成ライブラリに関しては以下をご覧ください。

関数の概要

関数は以下のとおりです。

外部関数

他のジュールやファイルの外部からアクセス可能な関数です。


引 数:

・DAC 増幅回路の電源の操作( nSwitchPower ):
 SOUND_POWER_ON : DAC 増幅回路の電源 ON
 SOUND_POWER_OFF: DAC 増幅回路の電源 OFF

分 類:

API( 省電力制御、 対象は Speak Interpreter、他 )

機 能:

DAC 増幅回路の電源を ON / OFF する。

関係する関数:

上位関数: operateSI()、アプリケーション、他
下位関数: ー
この関数のコードはこちらです。


引 数:

・const char szData:  発音するローマ字表記音声記号列( ASCII )
 ローマ字表記音声記号列( ASCII )の詳細は以下をご覧ください。
ローマ字音声記号列仕様書: 発音のデータ(文字列)の仕様書 / 株式会社 アクエスト

分 類:

API( 対象は Speak Interpreter、他 )

機 能:

発音処理の本体。AquesTalk Pi ライブラリと I2S ライブラリを使用して引数の音声( 音声記号列 )を発音する。

関係する関数:

上位関数: playSI()、speakSI()、アプリケーション、他
下位関数: CAqTkPicoF_SetKoe()、CAqTkPicoF_SyntheFrame()、writeDAC_VD()
この関数のコードはこちらです。


引 数:

・String szInput: 設定する発音速度 [%] を 50 ~ 300の間で指定する。( 初期値は 100 )

分 類:

API( 対象は Speak Interpreter、他 )

機 能:

発声速度を設定(変更)する。

関係する関数:

上位関数: setSpeedSI()、アプリケーション、他
下位関数: ( CAqTkPicoF_SetKoe() )
この関数のコードはこちらです。


引 数:

なし

分 類:

API( 対象は Speak Interpreter、他 )

機 能:

発音処理の準備を行う。

関係する関数:

上位関数: operateSI()、アプリケーション、他
下位関数: AqResample_Reset()、i2s_driver_install()、i2s_set_dac_mode()、i2s_zero_dma_buffer()
この関数のコードはこちらです。


引 数:

なし

分 類:

API( 対象は Speak Interpreter、他 )

機 能:

発音処理の終結を行う。

関係する関数:

上位関数: operateSI()、アプリケーション、他
下位関数: i2s_driver_uninstall()
この関数のコードはこちらです。

内部関数


引 数:

なし

分 類:

内部関数( 初期化 ) 

機 能:

音声合成ライブラリの初期化を行います。

関係する関数:

上位関数: setup()
下位関数: CAqTkPicoF_Init()
この関数のコードはこちらです。


引 数:

・int len: 波形データのデータ長
・int16_t *wav: 波形データ

戻り値:

・int : エラー値( -1:ESP_FAIL / 0:TIMEOUT )

分 類:

内部関数( 初期化 ) 

機 能:

発音を行います。

関係する関数:

上位関数: playVD()
下位関数: AqResample_Conv()
この関数のコードはこちら#code_writeDAC_VDです。

ライブラリ関数( I2S ライブラリ )


I2Sライブラリ内の関数です。
詳細は以下をご覧ください。
M5StickC非公式日本語リファレンス( i2s ) /
m5stack/M5StickC / M5Stack Quick & easy IoT development

関数の説明は上記の仕様書からの抜粋です。
戻り値の esp_err_t は、ESP32のESP-IDF( Espressif IoT Development Framework )で使用されるエラーコードの型です。


引 数:

・i2s_port_t i2s_num: DMAバッファの指定( I2S_NUM_0、I2S_NUM_1 )
・i2s_config_tconst *i2s_configI2S: I2S のコンフィグレーションを参照
・int queue_sizeI2S: イベント キューのサイズ/深さ。
・void *i2s_queueI2S: イベント キュー ハンドル。NULL に設定するとドライバはイベント キューを使用しない。

戻り値:

esp_err_t

分 類:

内部関数( I2Sライブラリ )

機 能:

I2S ドライバーをインストールして起動する。

関係する関数:

上位関数: createDAC_VD()
下位関数: ー


引 数:

・adc_unit_t adc_unit: SAR ADC ユニットの番号
・adc1_channel_t *i2s_configI2Sadc_channel: ADC チャネルの番号

戻り値:

esp_err_t

分 類:

内部関数( I2Sライブラリ )

機 能:

I2S DMA の組み込み ADC モードを設定する。

関係する関数:

上位関数: createDAC_VD()
下位関数: ー


引 数:

・i2s_port_t i2s_num: DMAバッファの指定( I2S_NUM_0、I2S_NUM_1 )

戻り値:

esp_err_t

分 類:

内部関数( I2Sライブラリ )

機 能:

TX DMA バッファの内容をゼロにする。

関係する関数:

上位関数: createDAC_VD()
下位関数: ー


引 数:

・i2s_port_t i2s_num: DMAバッファの指定( I2S_NUM_0、I2S_NUM_1 )
・const void *src: 書き込み元のデータの所在アドレス
・size_t size: データのサイズ( バイト単位 )
・size_t *bytes_written: 書き込まれたバイト数
・TickType_t ticks_to_wait: TX バッファ待機タイムアウト ( RTOS ティック単位 )

戻り値:

esp_err_t

分 類:

内部関数( I2Sライブラリ )

機 能:

I2S DMA 送信バッファにデータを書き込む。

関係する関数:

上位関数: playVD()
下位関数: ー


引 数:

・i2s_port_t i2s_num: DMAバッファの指定( I2S_NUM_0、I2S_NUM_1 )

戻り値:

esp_err_t

分 類:

内部関数( I2Sライブラリ )

機 能:

I2S ドライバーをアンインストールする。

関係する関数:

上位関数: releaseDAC_VD()
下位関数: ー

ライブラリ関数( 音声合成ライブラリ )


音声合成ライブラリ( AquesTalk-ESP32 Ver.2.0 )内の関数です。
詳細は以下をご覧ください。
テキスト音声合成ライブラリ 「AquesTalk pico」 標準インターフェース仕様書(フレーム合成版)
 / 株式会社アクエスト

関数の説明は上記の仕様書からの抜粋です。


引 数:

buf:  ワークバッファのアドレスを指定。バッファのサイズはAQ_SIZE_WORKBUF(AqTkPicoF.h 内に規定)
 音声合成が終了するまで、解放や内容の書き換えは不可。
lenFrame: フレーム長(1度のSyntheFrame()呼び出しで生成するサンプル数)を30-320の間で指定。 デフォルト:160

戻り値:

uint8_t
0:正常終了 0以外:エラー

分 類:

音声合成ライブラリ

機 能:

音声合成ライブラリの初期化処理です。

関係する関数:

上位関数: setupVD()
下位関数: -


引 数:

koe:  音声記号列( 1文程度を指定 )を指定( 文字列の先頭アドレス。NULL終端 )
speed: 発話速度の指定。 値を大きく設定するほど、速くなる。発話速度 [%] 50-300 の間で指定。デフォルト:100
lenPause:  最後のポーズ( 無音区間 )の長さを指定。 0xffffU を指定すると、内部の標準ポーズ長。 最後のポーズが不要な場合は、256の指定を推薦。

戻り値:

uint8_t
0:正常終了 0以外:エラー

分 類:

音声合成ライブラリ

機 能:

音声記号列をライブラリに設定します。

関係する関数:

上位関数: playVD()
下位関数: -


引 数:

wav:  音声波形出力バッファ。生成した1フレームの音声波形データがここに書き込まれて戻る。 バッファのサイズはlenFrameで指定した値以上とし、外部で確保する。
pLen: 生成したデータのサンプル数を返す。通常、最終フレーム以外はlenFrameで指定した値。 最終フレームはlenFrame以下になる。

戻り値:

uint8_t
0:正常終了 0以外:End of Data それ以外:エラー
最終フレームも0を返す。その次に呼び出したときに1を返す。

分 類:

音声合成ライブラリ

機 能:

音声波形を1フレーム生成 CAqTkPicoF_SetKoe()でセットした音声記号列から、音声を1フレーム生成して戻る。 (1フレームの長さは CAqTkPicoF_Init() で先に指定)

関係する関数:

上位関数: playVD()
下位関数: -


引 数:

wav:  音声波形出力バッファ。生成した1フレームの音声波形データがここに書き込まれて戻る。 バッファのサイズはlenFrameで指定した値以上とし、外部で確保する。
pLen: 生成したデータのサンプル数を返す。通常、最終フレーム以外はlenFrameで指定した値。 最終フレームはlenFrame以下になる。

戻り値:

uint8_t
0:正常終了 0以外:End of Data それ以外:エラー
最終フレームも0を返す。その次に呼び出したときに1を返す。

分 類:

音声合成ライブラリ

機 能:

アップサンプリングするためにサンプリング周波数を変換する。

関係する関数:

上位関数: writeDAC_VD()
下位関数: -

Technical Considerations アップサンプリング
アップサンプリング( Upsampling / オーバーサンプリングとも呼ばれる )はデータセット内のデータの数を増やして、データの不均衡を修正するデータ処理の手法です。オーディオの世界では、アップサンプリングは音質向上や DACの最適化 などの目的で行われます。

引 数:

なし

戻り値:

uint8_t
0:正常終了 0以外:End of Data それ以外:エラー
最終フレームも0を返す。その次に呼び出したときに1を返す。

分 類:

音声合成ライブラリ

機 能:

アップサンプリング処理の初期化

関係する関数:

上位関数: createDAC_VD()
下位関数: -


データの構造

データの構造は以下のとおりです。

グローバル定義

グローバル定義は以下のとおりです。

//------------------------------------------------------------------------------
// IO pin name definition
//------------------------------------------------------------------------------

//-----------------------------------------------
// IO pin list
//-----------------------------------------------

...

//---- Voice / DAC / AMP ----
#define	PIN_DAC				25			// DAC output
#define	PIN_PS_AMP			26			// Amplifier power saving control
//------------------------------------------------------------------------------
// Voice driver
//------------------------------------------------------------------------------

//---- Power saving control ----

//.... Output logic .... 
#define AMP_POWER_ON		HIGH		// power saving control for DAC amplifier 
#define AMP_POWER_OFF		LOW

//.... Argument ....
#define SOUND_POWER_ON		1			// 
#define SOUND_POWER_OFF		0

//---- Voice data ----
#define VD_BUF_LEN			32			// 

#define VD_DMA_BUFNO		0			//  i2s port number
名 称説 明
PIN_DACdefine25DAC 出力の信号ピン
PIN_PS_AMPdefine26DAC 増幅回路の省電力制御用の信号ピン
AMP_POWER_ONdefineHIGHDAC 増幅回路の電源 ON時の出力
AMP_POWER_OFFdefineLOWDAC 増幅回路の電源 OFF時の出力
SOUND_POWER_ONdefine1DAC 増幅回路の電源 ON
SOUND_POWER_OFFdefine0DAC 増幅回路の電源 OFF
VD_BUF_LENdefine32発音波形データの生成時のサンプル数
VD_DMA_BUFNOdefine0使用する DMAのバッファ指定

FLASHメモリ内の定義

FLASHメモリ内の定義です。

要 素設定値説 明
AQUEST_LICENSE_KEY音声合成ライブラリのライセンスキー

I2S のコンフィグレーション

I2Sのコンフィグレーションに関する定義です。

//.... i2s configuration ....
i2s_config_t i2s_config = {
							// Master and transmit mode
  .mode 					= (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),
							// Sampling rate 12kHz
  .sample_rate 				= 12000,
							// 16bit Audio
  .bits_per_sample 			= I2S_BITS_PER_SAMPLE_16BIT,
							// Stereo
  .channel_format 			= I2S_CHANNEL_FMT_RIGHT_LEFT,
							// I2S Format MSB first
  .communication_format 	= (i2s_comm_format_t)I2S_COMM_FORMAT_I2S_MSB,
							// Deault interrupt
  .intr_alloc_flags 		= 0,
							// Number of DMA buffer
  .dma_buf_count 			= 4,
							// Length of DMA buffer
  .dma_buf_len 				= 384,
							// No useed Audio PLL
  .use_apll 				= false,
							// Enabled automatic clear
  .tx_desc_auto_clear		= true,
							// Setting automatic for Master clock
  .fixed_mclk				= 0
};
要 素設定値 / 説明
.mode(i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN)
マスター + 送信モード
.sample_rate12000
サンプリングレート: 12kHz
.bits_per_sampleI2S_BITS_PER_SAMPLE_16BIT
16ビットオーディオ
.channel_formatI2S_CHANNEL_FMT_RIGHT_LEFT
ステレオ
.communication_format(i2s_comm_format_t)I2S_COMM_FORMAT_I2S_MSB
I2Sの通信フォーマットMSBファースト
.intr_alloc_flags0デフォルトの割り込み設定
.dma_buf_count4DMAバッファ数
.dma_buf_len3841つのDMAバッファの長さ
.use_apll0APLL (オーディオPLL) を使用しない
.tx_desc_auto_cleartrue自動クリアを有効化
.fixed_mclk0固定のマスタークロックは自動設定

音声合成ライブラリ内の定義

音声合成ライブラリ( AquesTalk-ESP32 Ver.2.0 )を使用するときのインクルードファイル内での定義です。

要 素初期値説 明
AQ_SIZE_WORKBUF100x4byte、ローマ字音声記号列から生成した音声波形データのバッファ長

グローバル変数

グローバル変数は以下のとおりです。

//==============================================================================
// Variable definitions in source code
//==============================================================================

//------------------------------------------------------------------------------
// Voice driver
//------------------------------------------------------------------------------

//.... control ....
bool		gVDenable		= SOUND_POWER_OFF;	// amplifier power saving control
int			gVDspeed		= 100;				// speaking speed
int			gVDvolume		= 100;				// speaking volume

//.... buffer ....
uint32_t	gVDbuffer[ AQ_SIZE_WORKBUF ];
要 素初期値説 明
gVDenableboolfalse発音処理の ON/OFF状態の保存
・ON: True
・OFF: False
gVDspeedint100発音速度 [%] を 50 ~ 300の間で指定
gVDvolumeint100発音音量 [%] を 0 ~ 100の間で指定
gVDbuffer[AQ_SIZE_WORKBUF]uint32_t0発音波形データのバッファ

コード

コードは以下のとおりです。

インクルードファイル

必要なインクルードファイルは以下のとおりです。

要 素説 明
“driver/i2s.h”I2S の使用
“aquestalk.h”音声合成ライブラリ( AquesTalk-ESP32 Ver.2.0 )の使用

関数間の関係

処理ごとの関数の関係は以下のとおりです。

関数の関係は以下のとおりです。

外部関数


DAC 増幅回路の電源を ON / OFF します。

/*----------------------------------------------------------------------------*/
/* Power saving control for DAC amplifier
/* param     : int nSwitch / control amplifier power saving
/* return    : -
/*----------------------------------------------------------------------------*/
setPowerControVD( int nSwitch )
{

	if( nSwitch == VOICE_ON ){
		gVDenable = true;
		digitalWrite( PIN_PS_AMP, AMP_POWER_ON );

	} else {
		gVDenable = false;
		digitalWrite( PIN_PS_AMP, AMP_POWER_OFF );
	}		
}	

発音処理の本体。AquesTalk Pi ライブラリと I2S ライブラリを使用して引数の音声( 音声記号列 )を発音します。

/*----------------------------------------------------------------------------*/
/* Playback phonetic symbol data
/* param     : String szSpeakData / AquesTalk phonetic symbol data
/* return    : -
/*----------------------------------------------------------------------------*/
void PlayVD( String szSpeakData )
{
	//.... local ....
	int			nRet;
	char		koe[ LEN_SI_PHRASE ];
	int16_t		wav[ VD_BUF_LEN ];
	uint16_t	len;

	if( gVDenable == false ){
		return;
	}

	//.... set phrase data to Voice Library ....
	szSpeakData.toCharArray( koe, sizeof(koe) );
	CAqTkPicoF_SetKoe( (const uint8_t*)koe, gVDspeed, 0xffffU );

	for (;;) {

		//.... synthesis voice wave data from Voice Library ....	
		nRet = CAqTkPicoF_SyntheFrame( wav, &len );
		if ( nRet ) {
			break; // EOD
		}

		//.... send to DAC ....
		writeDAC_VD( (int)len, wav ) ;
	}

  i2s_zero_dma_buffer( (i2s_port_t)VD_DMA_BUFNO );

}

発音する文章( 音声記号列 )を事前に設定する CAqTkPicoF_SetKoe()を実行し、フレーム長ごとに設定した文章( 音声記号列 )を CAqTkPicoF_SyntheFrame()を実行して波形データに変換します。( WAV[] )変換処理は1フレームごとに行います。フレーム長を VD_BUF_LEN( = 32 サンプル )に指定しましたので、32 / 8kHz( 4 ms )ごとに音声を生成します。波形データは writeDAC_VD()を介して DAC出力します。

すべての発生後に i2s_zero_dma_buffer()を実行して DMAバッファをクリアにします。


発声速度の設定を行います。

/*----------------------------------------------------------------------------*/
/* Set speaking speed
/* param     : int nSpeed / speaking speed
/* return    : -
/*----------------------------------------------------------------------------*/
void setSpeedVD( int nSpeed )
{

	nSpeed = gVDspeed;

}

発声速度は CAqTkPicoF_SetKoe()の引数で設定しています。発生時に gVDspeedを使用することにより発声速度が決まります。


発音処理の準備を行う。

/*----------------------------------------------------------------------------*/
/* Setup DAC
/* param     : -
/* return    : -
/*----------------------------------------------------------------------------*/
void createDAC_VD()
{

  AqResample_Reset();

  i2s_driver_install( (i2s_port_t)VD_DMA_BUFNO, &i2s_config, 0, NULL );
  i2s_set_dac_mode( I2S_DAC_CHANNEL_RIGHT_EN ); // 25
  i2s_zero_dma_buffer( (i2s_port_t)VD_DMA_BUFNO );

}

アップサンプリング処理の初期化 AqResample_Reset() と I2Sの初期化を行います。初期化パラメータは I2S のコンフィグレーションで設定しています。
出力ピン GPIO と DAC出力の左右の対応は以下のとおりです。
・RIGHT: IO25( Qumcum Lab. は IO25をモノラルの DAC出力としています。 )
・LEFT: IO26( DAC出力の増幅回路の省電力制御に使用しています。 )


発音処理の終結を行う。

/*----------------------------------------------------------------------------*/
/* Release DAC
/* param     : -
/* return    : -
/*----------------------------------------------------------------------------*/
void releaseDAC_VD()
{

	i2s_driver_uninstall( (i2s_port_t)VD_DMA_BUFNO ); 

}

I2S ドライバをアンインストールします。

内部関数


音声合成ライブラリの初期化処理を呼び出します。

void setup( void )
{

	・・・

	/* Initialize Voice driver */
	setupVD();	// include DAC initialize

	・・・

}	

音声合成ライブラリの初期化と DAC増幅回路を OFFにします。

/*----------------------------------------------------------------------------*/
/* Initialize Voice driver
/* param     : -
/* return    : -
/*----------------------------------------------------------------------------*/
void setupVoice()
{

	CAqTkPicoF_Init( gVDbuffer, VD_BUF_LEN, AQUEST_LICENSE_KEY );  //●●○

	setPowerControVD( SOUND_POWER_OFF );

}

発音を行う。

/*----------------------------------------------------------------------------*/
/* Write DAC data
/* param     : int len / length of wavedata
/*             int16_t *wav / wavedata
/* return    : int / 
/*----------------------------------------------------------------------------*/
int writeDAC_VD( int len, int16_t *wav )
{

	//.... local ....
	int			i, k, nRet;
	int16_t		wav3[3];
	uint16_t	us;
	uint16_t	sample[2];
	size_t		transBytes;

	for ( i = 0; i < len; i++ ) {

		//.... upsampling x3 ....
		AqResample_Conv( wav[i], wav3 );

		//.... write to I2S DMA buffer ....
		for ( k = 0; k < 3; k++ ) {
			us = ( (uint16_t)wav3[k] ) ^ 0x8000U;
			sample[0] = sample[1] = us; // mono -> stereo
			nRet = i2s_write( (i2s_port_t)VD_DMA_BUFNO, (const char*)sample, 4, &transBytes, portMAX_DELAY );

			if ( nRet < 0 ) {
				return nRet;	// -1:ESP_FAIL
			}

			if ( nRet == 0 ) {
				break;			//	0:TIMEOUT
			}
		}
	}
  return nRet;

}

データの流れを中心にした処理の手順は以下のとおりです。

発声速度は CAqTkPicoF_SetKoe()の引数で設定しています。発生時に gVDspeedを使用することにより発声速度が決まります。AqResample_Conv()によりアップサンプリングして雑音を抑えています。( 3倍
I2Sのデータは符号付きで 16bitデータの場合、値は-32768~32767の範囲( 中央値は0 )です。ESP32の DACは、0-65535( 下位8bitは無視 )の範囲であり中央値が 32768になります。そのための変換を行っています。( 外部 DACを使うときは変換はしません。 )

クロックレートの決め方です。
ESP32のI2S設定で、i2s_config_t.sample_rate( サンプリングレート )と I2S_CHANNEL_MONO( モノラル設定 )には 密接な関係 があります。ESP32のI2Sは、ステレオ( 左右2ch ) と モノラル( 1ch ) の2種類のデータフォーマットをサポートしています。以下のとおりです。

  • モノラル( 1ch ): I2S_CHANNEL_MONO = 1
  • ステレオ( 2ch ): I2S_CHANNEL_STEREO = 2

ESP32のI2Sハードウェアでは、データの送信・受信は常に「左チャンネル + 右チャンネル」の単位 で処理されます。モノラル設定の場合も、内部的にはステレオデータとして処理 されます。
I2S_CHANNEL_MONO を設定しても、ESP32のI2Sは 内部的にはステレオデータのクロックを使います。そのため、モノラルのサンプルレートを設定すると、実際にはその 2倍のクロックが必要 になります。

AquesTalkは、WAV フォーマット( 8KHz サンプリング、16bitPCM、モノラル )データです。但し、24KHzサンプリングにアップサンプリング機能を含んでいます。

サンプルプログラムはステレオで処理しています。また3倍のアップサンプリングを行っています。そのため 8kHz X 3 = 24 kHzとなりますが、ステレオ処理のため 1/2 の 12000( 12kHz )がサンプリング周波数となります。
モノラルで処理する場合( I2S_CHANNEL_MONO )は、1/1のため 24000( 24kHz )のサンプリング周波数となります。

参考情報

参考になる情報は以下のとおりです。

このホームページ内

他のWebサイト

カテゴリー
最近の記事 おすすめ記事

記事一覧を表示するには、カスタム投稿「ブログ」にて、4つ以上記事を作成してください。

記事一覧を表示するには、カスタム投稿「ブログ」にて、4つ以上記事を作成してください。

TOP