/// <summary> 音声波形を生成する </summary> /// <param name="pronounce"> (株)アクエストが定める発音記号列 </param> /// <returns> wavファイルの内容物に相当するデータ </returns> public byte[] CreateWav(string pronounce) { try { int size = 0; IntPtr wavPtr = SyntheWave(pronounce, Speed, ref size); //wav生成に失敗してるケース: 具体的には[あ-んア-ン.,。、/+_]あたりの記号以外が入ってると失敗する if (wavPtr == IntPtr.Zero) { //ヌルぽなので意味はないハズだが一応 FreeWave(wavPtr); return(new byte[] { }); } var wav = new byte[size]; Marshal.Copy(wavPtr, wav, 0, size); FreeWave(wavPtr); //パラメタに応じて波形処理が入る //FIXME: NAudio使った方がカッコいい…か?AquesTalkだけターゲットにするなら別に現状でもいいが WaveInfo.ChangePitch(wav, Pitch); WaveInfo.ChangeVolume(wav, Volume); return(wav); } catch (ArgumentNullException) { return(new byte[] { }); } }
/// <summary> /// ピッチを変更する。読み上げ速度と声の高さが変わる /// CAUTION: AquesTalk以外の音声データに対応させてない /// </summary> /// <param name="wav">音声データ</param> /// <param name="pitch">ピッチ(100が基準値)</param> public static void ChangePitch(byte[] wav, int pitch) { var wInfo = new WaveInfo(wav); float rate = pitch / 100.0f; wInfo.BytePerSec = (uint)(wInfo.BytePerSec * rate); wInfo.SamplingRate = (uint)(wInfo.SamplingRate * rate); wInfo.SetInfoTo(wav); }
/// <summary> /// ファイルのある位置での音量を取得する /// </summary> /// <param name="t">時刻</param> /// <param name="wav">時刻の周辺幅</param> /// <param name="width">周辺値をとってくる時間幅(秒)</param> /// <returns>音量の2乗平均値</returns> private static double VolumeAt(byte[] wav, double t, double width) { //2015/5/23変更: 任意のブロックサイズやBit Per Sampleに対応出来るようにする var wInfo = new WaveInfo(wav); int bytePerSample = wInfo.BitPerSample / 8; int min = (int)(t * wInfo.BytePerSec); int max = (int)((t + width) * wInfo.BytePerSec); min = Math.Max(min, 0); max = Math.Min(max, wav.Length - 44 - bytePerSample); //スタート位置のアレイをきちんとする min -= min % bytePerSample; max -= max % bytePerSample; //44はヘッダの長さ min += 44; max += 44; //無いと思うがtやwidthの設定に対してロバストにしておく if (min >= max) { return(0.0); } var volumes = new float[(max - min) / bytePerSample]; int index = 0; for (int i = min; i < max; i += bytePerSample) { //HACK: BigEndian対応のためbyte数毎に対応する。 //いやホントは一般化できる(最上位ビット見て余剰ビット埋めればいい)んだけど… if (bytePerSample == 1) { volumes[index] = wav[i] / 128.0f; } else if (bytePerSample == 2) { volumes[index] = GetShortFrom(wav[i], wav[i + 1]) / 32768.0f; } else if (bytePerSample == 3) { var wavValue = new byte[3]; Array.Copy(wav, i, wavValue, 0, 3); volumes[index] = GetIntFrom3Byte(wavValue) / 8388608.0f; } index++; } return(Math.Sqrt(volumes.Sum(v => v * v) / volumes.Length)); }
/// <summary> /// wavを時間でぶつ切りにして音量の配列を返す。口パク連動サポート。 /// </summary> /// <param name="wav">音声データ(.wav)</param> /// <param name="interval">時間区切り幅</param> /// <returns></returns> private static double[] Volumes(byte[] wav, double interval) { var wInfo = new WaveInfo(wav); var result = new List <double>(); for (int i = 1; (i + 1) * interval < wInfo.PlayTime; i++) { result.Add(VolumeAt(wav, i * interval, interval)); } // //File.WriteAllLines("wavMeans.dat", result.Select(v => v.ToString()).ToArray()); return(result.ToArray()); }
/// <summary> /// ファイルのある位置での音量を取得する /// </summary> /// <param name="t">時刻</param> /// <param name="wav">時刻の周辺幅</param> /// <param name="width">周辺値をとってくる時間幅(秒)</param> /// <returns>音量の2乗平均値</returns> private static double VolumeAt(byte[] wav, double t, double width) { //2015/5/23変更: 任意のブロックサイズやBit Per Sampleに対応出来るようにする var wInfo = new WaveInfo(wav); int bytePerSample = wInfo.BitPerSample / 8; int min = (int)(t * wInfo.BytePerSec); int max = (int)((t + width) * wInfo.BytePerSec); min = Math.Max(min, 0); max = Math.Min(max, wav.Length - 44 - bytePerSample); //スタート位置のアレイをきちんとする min -= min % bytePerSample; max -= max % bytePerSample; //44はヘッダの長さ min += 44; max += 44; //無いと思うがtやwidthの設定に対してロバストにしておく if (min >= max) return 0.0; var volumes = new float[(max - min) / bytePerSample]; int index = 0; for (int i = min; i < max; i += bytePerSample) { //HACK: BigEndian対応のためbyte数毎に対応する。 //いやホントは一般化できる(最上位ビット見て余剰ビット埋めればいい)んだけど… if (bytePerSample == 1) { volumes[index] = wav[i] / 128.0f; } else if (bytePerSample == 2) { volumes[index] = GetShortFrom(wav[i], wav[i + 1]) / 32768.0f; } else if (bytePerSample == 3) { var wavValue = new byte[3]; Array.Copy(wav, i, wavValue, 0, 3); volumes[index] = GetIntFrom3Byte(wavValue) / 8388608.0f; } index++; } return Math.Sqrt(volumes.Sum(v => v * v) / volumes.Length); }
/// <summary> /// wavを時間でぶつ切りにして音量の配列を返す。口パク連動サポート。 /// </summary> /// <param name="wav">音声データ(.wav)</param> /// <param name="interval">時間区切り幅</param> /// <returns></returns> private static double[] Volumes(byte[] wav, double interval) { var wInfo = new WaveInfo(wav); var result = new List<double>(); for (int i = 1; (i + 1) * interval < wInfo.PlayTime; i++) { result.Add(VolumeAt(wav, i * interval, interval)); } // //File.WriteAllLines("wavMeans.dat", result.Select(v => v.ToString()).ToArray()); return result.ToArray(); }