質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Ubuntu

Ubuntuは、Debian GNU/Linuxを基盤としたフリーのオペレーティングシステムです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

4回答

5578閲覧

Unity(C#)におけるdocomoの音声合成apiによるwavデータの取得方法

donsan4090

総合スコア14

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Ubuntu

Ubuntuは、Debian GNU/Linuxを基盤としたフリーのオペレーティングシステムです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2017/05/13 22:40

###現状
先日投稿した,
https://teratail.com/questions/73993
に近い質問です.

docomoの音声合成APIを使用して,Unity上のキャラクタに話させたいと思っています.
このAPIから出力された音声データ(バイナリデータ)が
以下のプログラムを通して,www.bytesに格納されています(と思っています).

string url = "https://api.apigw.smt.docomo.ne.jp/aiTalk/v1/textToSpeech?APIKEY=" + docomoApiKey; Dictionary<string, string> aiTalksParams = new Dictionary<string, string> (); aiTalksParams["speaker"] = "nozomi"; aiTalksParams["pitch"] = "1"; aiTalksParams["range"] = "1"; aiTalksParams["rate"] = "1"; aiTalksParams["volume"] = "1.5"; string text = inputText; var postData = createSsml(text, aiTalksParams); var data = System.Text.Encoding.UTF8.GetBytes(postData); Dictionary<string, string> headers = new Dictionary<string, string> (); headers["Content-Type"] = "application/ssml+xml"; headers["Accept"] = "audio/L16"; headers["Content-Length"] = data.Length.ToString(); WWW www = new WWW(url, data, headers); yield return www; if (www.error != null) { Debug.LogError(www.error); yield break; }

音声データの形式がBinaryで
音声データのフォーマットは下記の通りです.

【符号化方式】
リニアPCM
【チャネル数】
1(モノラル)
【サンプル周波数】
16000
【ビット深度】
16bit(ビッグエンディアン)

###問題点
www.bytesのbyte[]型のデータをwavファイルに変換して保存し,それをUnityのAudioClipにして再生したいと考えています.

ビッグエンディアンからリトルエンディアンへの変換は

byte[] lEndianBytes = ConvertBytesEndian(www.bytes); // http://wawatete.ddo.jp/exec/program/cs/binary_convertendian.html static byte[] ConvertBytesEndian(byte[] bytes) { // 引数の配列と同じサイズの配列を宣言 byte[] newBytes = new byte[bytes.Length]; // 配列のコピーを作成 bytes.CopyTo(newBytes, 0); // 反転 System.Array.Reverse(newBytes); return newBytes; }

としてます.

また,そのデータをちゃんとしたヘッダ情報のあるwavファイルにするために,

[StructLayout(LayoutKind.Sequential)] public class WAVHDR { [MarshalAs(UnmanagedType.I4)] public UInt32 riff = 0x46464952; /* RIFF */ [MarshalAs(UnmanagedType.I4)] public UInt32 fileSize; [MarshalAs(UnmanagedType.I4)] public UInt32 wave = 0x45564157; /* WAVE */ [MarshalAs(UnmanagedType.I4)] public UInt32 fmt = 0x20746D66; /* fmt */ [MarshalAs(UnmanagedType.I4)] public UInt32 fmtbytes = 16; [MarshalAs(UnmanagedType.I2)] public UInt16 formatid; [MarshalAs(UnmanagedType.I2)] public UInt16 channel; [MarshalAs(UnmanagedType.I4)] public UInt32 fs; [MarshalAs(UnmanagedType.I4)] public UInt32 bytespersec; [MarshalAs(UnmanagedType.I2)] public UInt16 blocksize; [MarshalAs(UnmanagedType.I2)] public UInt16 bitspersample; [MarshalAs(UnmanagedType.I4)] public UInt32 data = 0x61746164; /* data */ [MarshalAs(UnmanagedType.I4)] public UInt32 size; //convert the struct to byte array public byte[] getByteArray() { int len = Marshal.SizeOf(this); byte[] arr = new byte[len]; IntPtr ptr = Marshal.AllocHGlobal(len); Marshal.StructureToPtr(this, ptr, true/*or false*/); Marshal.Copy(ptr, arr, 0, len /*or arr.Length*/); Marshal.FreeHGlobal(ptr); return arr; } } WAVHDR wavHdr = new WAVHDR(); // wavファイルに格納 uint fs = 16000; // 16K wavHdr.formatid = 0x0001; //PCM 非圧縮 wavHdr.channel = 1; // ch=1 モノラル wavHdr.fs = fs; // wavHdr.bytespersec = fs * 2; // 16bit 16K wavHdr.blocksize = 2; // ブロックサイズ (Byte/sample×チャンネル数)->→16ビットモノラルなので 2 wavHdr.bitspersample = 16; // サンプルあたりのビット数 (bit/sample) wavHdr.size = 10 * fs * 2; // 波形データのバイト数 wavHdr.fileSize = wavHdr.size + (uint)Marshal.SizeOf(wavHdr); // 全体のバイト数

としてます.

そのヘッダ情報と音声データをwavに保存するために,

PrepareFile(wavHdr, lEndianBytes); private void PrepareFile(WAVHDR hdr, byte[] databuf) { using (FileStream fs = new FileStream("sample.wav", FileMode.Create, FileAccess.Write)) using (BinaryWriter bWriter = new BinaryWriter(fs)) { // ヘッダ書きだし foreach(byte b in hdr.getByteArray()) { bWriter.Write(b); } // 波形書きだし foreach(Int16 data in databuf) { bWriter.Write(data); } bWriter.Flush(); bWriter.Close(); fs.Close(); } }

つぎはぎのコードになってしまい,すみません.

###問題点
これにより,実際にsample.wavは生成されますが,雑音しか聞こえてきません.
・PCMデータとして取得
・ビッグエンディアンからリトルエンディアンへの変換
・wavファイルへの保存

という流れは,pythonで書いたものは動いていたので流れはこれで良いと思います.それをUnityで使用するC#に移行しようとした際にうまく言っていない状況です.
ただ,目的達成には,wavファイルとして保存する必要がないので,それを飛ばせる方法等があればそれでもいいです.

お力添え願います.

###補足情報(言語/FW/ツール等のバージョンなど)
OS: Ubuntu14.04
Unity: 5.4
Language: C#
API: docomo音声合成API

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

下記のように修正することで期待通りのエンディアンの変換が出来ます。

C#

1byte[] ConvertBytesEndian(byte[] bytes) 2{ 3 byte[] newBytes = new byte[bytes.Length]; 4 for (int i = 0; i < bytes.Length; i += 2) 5 { 6 newBytes[i] = bytes[i + 1]; 7 newBytes[i + 1] = bytes[i]; 8 } 9 return newBytes; 10}

エンディアンそのものについては、HiroshiWatanabeさんとBongoさんの説明のとおりです。

投稿2017/05/19 12:29

kokeiro001

総合スコア62

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

HiroshiWatanabeさんのご指摘に加え、もう一つ怪しい箇所として、foreach(Int16 data in databuf)で書き出しを行っているようですが、この記述ですとdatabufからbyte単位で要素を列挙した上でInt16として書き込みを行っているように見えます。この周辺は意図通りの動作をしていますでしょうか?

投稿2017/05/15 03:12

Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

donsan4090

2017/05/15 21:32

御返事有難うございます. pythonで書いたものは意図どおりに動いていて,それはバイナリデータdataを # ビッグエンディアン->リトルエンディアン変換 lst = unpack('>{}h'.format(int(len(data)/2)), data) data = pack('{}h'.format(len(lst)), *lst) と変換しています.フォーマット指定のhの記号が,cにおけるshortに相当してます. ですので,C#ではInt16として出力するのではないかと思ってそうしています. http://www.atmarkit.co.jp/fdotnet/csharp_abc/csharp_abc_005/csharp_abc01.html ただ,正常に動作していないので,問題はどこかにあると思うのですが.
Bongo

2017/05/15 22:39 編集

pythonについてはうといのですが、pythonにおいてpack/unpackでバイト列・16ビット整数列の相互変換を行うことは問題ないアプローチだと思います。実際にpython版では正しい音声データが出力されているようですね。 C#版で怪しいと申し上げた箇所の場合、次のような動作をしているのではないかと思いました。 - foreachでdatabufを列挙する。databufはbyte[]なので、取り出される値はbyteとなる。 - 取り出されたデータは変数dataに格納される。dataはInt16と宣言されているので、暗黙的型変換によりInt16になる。 - dataをbWriter経由で書き込む。dataはInt16なので、Int16として2バイト分書き込まれる。 結果として、元々のbyte[]が倍の長さに引き伸ばされて書き込まれた状態にならないでしょうか。 bWriter.Write(data)の直前でDebug.Log(data)などでdataの内容を見てみた場合、Int16なのに0〜255の値しか出てこない、というようなことになるような気がします。 あるいは、できあがったノイズだけのwavファイルをバイナリファイルエディタ等で開いてみて、中程までスクロールしてみると、1バイトおきに0x00が出現するような状態になっていないでしょうか?
guest

0

System.Array.Reverse() では全体が逆順になるんじゃないですか?
(例えば 1,2,3,4 を 2,1,4,3 にしたいのに 4,3,2,1 にしてる…的な)

投稿2017/05/15 02:39

HiroshiWatanabe

総合スコア2160

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

donsan4090

2017/05/15 21:26

御返事有難うございます. 知識不足で申し訳ないのですが, ビッグエンディアンからリトルエンディアンに変換するには,すべてを逆順にするのではないのでしょうか? http://www.ertl.jp/~takayuki/readings/info/no05.html 私の思い違いでしたら,ご指摘ください.
HiroshiWatanabe

2017/05/16 01:24

PCMの波形データ自体にはビッグエンディアンとかリトルエンディアンとかの発想(仕様)はありません。それは波形を構成する各16bitの数値をどう格納しているかという実装部分に適用可能な考えです。「波形データ全体を逆向き」にするという事は逆再生する理屈にならないでしょうか?CPUレベルの実装的な話しとCPUやメモリに何の関係もない波形データの話しをごっちゃにしてしまっているようです。
Bongo

2017/05/20 00:31

ご参考にされたサイトの図は64ビットで(8バイトで)ひとかたまりのデータがメモリ上にどう格納されるか、を解説されているようです。図だけ見ると全体が反転しているように見えるかもしれませんが、今回の場合、書き出したいのは2バイトでひとかたまりのデータが連なった配列です。そのため、kokeiro001さんのご回答のように2バイトで一つのデータであることを意識して、個々のデータについて逆転させるということが必要になるのかと思います。 このあたり、Cなどのメモリと密接に関連したプログラミングが必要な言語ですと、簡単にいろいろな実験ができて面白いとは思うのですが...
guest

0

みなさま本当に親切に回答頂きありがとうございました.

Bongoさんのお返事で主となる問題が解決できましたので,ベストアンサーにさせていただきました.
kokeiro001さんは具体的なコードを記載いただきありがとうございます.実際に使わせて頂いており,正常に動作しております.
HiroshiWatanabeさんは素早い御返事有難うございました.ビックエンディアンとリトルエンディアンは波形そのものでなく,
「波形を構成する各16bitの数値をどう格納しているかという実装部分に適用可能な考え」
であるという点を混同していたので,良い気づきをいただきました.

kokeiro001さんのコードでリトルエンディアンに変換したdatabufをヘッダ情報hdrと合わせてファイルに保存することで解決しました.

private void PrepareFile(WAVHDR hdr, byte[] databuf) { System.IO.FileStream fs = new System.IO.FileStream("sample.wav", System.IO.FileMode.Append, System.IO.FileAccess.Write); // ヘッダ書き出し fs.Write(hdr.getByteArray(), 0, hdr.getByteArray().Length); // 波形書き出し fs.Write(databuf, 0, databuf.Length); fs.Close(); }

投稿2017/05/21 05:48

donsan4090

総合スコア14

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問