2017-02-08 19 views
0

UWPのAudioGraph APIを使用して合成音声と短い通知音(「earcons」)を再生しようとしています。AudioGraphは2番目のフレーム入力ノードでXAUDIO2_E_INVALID_CALLをスローします

UWPにはWAVファイルを含むストリームを提供する音声合成APIがありますが、パラメータ(ビットレート、サンプルの深さなど)についてあまり想定したくないので、 AudioSubmixNodeと再現する音声がある場合はいつでもAudioFrameInputNode秒を追加してください。別々の発声をキューイングして重複しないようにするには、いくらかの複雑さがあります。

グラフは

private async Task InitAudioGraph() 
    { 
     var graphCreated = await AudioGraph.CreateAsync(new AudioGraphSettings(Windows.Media.Render.AudioRenderCategory.Speech) 
     { 
      QuantumSizeSelectionMode = QuantumSizeSelectionMode.LowestLatency 
     }); 
     if (graphCreated.Status != AudioGraphCreationStatus.Success) return; 

     _Graph = graphCreated.Graph; 
     var outputCreated = await _Graph.CreateDeviceOutputNodeAsync(); 
     if (outputCreated.Status != AudioDeviceNodeCreationStatus.Success) return; 

     _Mixer = _Graph.CreateSubmixNode(); 
     _Mixer.AddOutgoingConnection(outputCreated.DeviceOutputNode); 

     _Graph.Start(); 
    } 

として初期化され、その後、現在の発話は、これが一度に動作

class SpeechStreamPlayer : IDisposable 
{ 
    internal static void Play(AudioGraph graph, AudioSubmixNode mixer, SpeechSynthesisStream speechStream) 
    { 
     if (!speechStream.ContentType.Equals("audio/wav", StringComparison.OrdinalIgnoreCase)) throw new NotSupportedException("Content type: " + speechStream.ContentType); 

     var stream = speechStream.AsStreamForRead(); 

     // Read the RIFF header 
     uint chunkId = stream.ReadUint(); // "RIFF" - but in little-endian 
     if (chunkId != 0x46464952) throw new NotSupportedException("Magic: " + chunkId); 
     uint chunkSize = stream.ReadUint(); // Length of rest of stream 
     uint format = stream.ReadUint(); // "WAVE" 
     if (format != 0x45564157) throw new NotSupportedException("Stream format: " + format); 

     // "fmt " sub-chunk 
     uint subchunkId = stream.ReadUint(); 
     if (subchunkId != 0x20746d66) throw new NotSupportedException("Expected fmt sub-chunk, found " + subchunkId); 
     uint subchunkSize = stream.ReadUint(); 
     uint subchunk2Off = (uint)stream.Position + subchunkSize; 
     uint audioFormat = (uint)stream.ReadShort(); 
     uint chans = (uint)stream.ReadShort(); 
     uint sampleRate = stream.ReadUint(); 
     uint byteRate = stream.ReadUint(); 
     uint blockSize = (uint)stream.ReadShort(); 
     uint bitsPerSample = (uint)stream.ReadShort(); 

     // Possibly extra stuff added, so... 
     stream.Seek(subchunk2Off, SeekOrigin.Begin); 

     subchunkId = stream.ReadUint(); // "data" 
     if (subchunkId != 0x61746164) throw new NotSupportedException("Expected data sub-chunk, found " + subchunkId); 
     subchunkSize = stream.ReadUint(); 

     // Ok, the stream is in the correct place to start extracting data and we have the parameters. 
     var props = AudioEncodingProperties.CreatePcm(sampleRate, chans, bitsPerSample); 

     var frameInputNode = graph.CreateFrameInputNode(props); 
     frameInputNode.AddOutgoingConnection(mixer); 

     new SpeechStreamPlayer(frameInputNode, mixer, stream, blockSize); 
    } 

    internal event EventHandler StreamFinished; 

    private SpeechStreamPlayer(AudioFrameInputNode frameInputNode, AudioSubmixNode mixer, Stream stream, uint sampleSize) 
    { 
     _FrameInputNode = frameInputNode; 
     _Mixer = mixer; 
     _Stream = stream; 
     _SampleSize = sampleSize; 

     _FrameInputNode.QuantumStarted += Source_QuantumStarted; 
     _FrameInputNode.Start(); 
    } 

    private AudioFrameInputNode _FrameInputNode; 
    private AudioSubmixNode _Mixer; 
    private Stream _Stream; 
    private readonly uint _SampleSize; 

    private unsafe void Source_QuantumStarted(AudioFrameInputNode sender, FrameInputNodeQuantumStartedEventArgs args) 
    { 
     if (args.RequiredSamples <= 0) return; 
     System.Diagnostics.Debug.WriteLine("Requested {0} samples", args.RequiredSamples); 

     var frame = new AudioFrame((uint)args.RequiredSamples * _SampleSize); 
     using (var buffer = frame.LockBuffer(AudioBufferAccessMode.Write)) 
     { 
      using (var reference = buffer.CreateReference()) 
      { 
       byte* pBuffer; 
       uint capacityBytes; 

       var directBuffer = reference as IMemoryBufferByteAccess; 
       ((IMemoryBufferByteAccess)reference).GetBuffer(out pBuffer, out capacityBytes); 

       uint bytesRemaining = (uint)_Stream.Length - (uint)_Stream.Position; 
       uint bytesToCopy = Math.Min(capacityBytes, bytesRemaining); 

       for (uint i = 0; i < bytesToCopy; i++) pBuffer[i] = (byte)_Stream.ReadByte(); 
       for (uint i = bytesToCopy; i < capacityBytes; i++) pBuffer[i] = 0; 

       if (bytesRemaining <= capacityBytes) 
       { 
        Dispose(); 
        StreamFinished?.Invoke(this, EventArgs.Empty); 
       } 
      } 
     } 

     sender.AddFrame(frame); 
    } 

    public void Dispose() 
    { 
     if (_FrameInputNode != null) 
     { 
      _FrameInputNode.QuantumStarted -= Source_QuantumStarted; 
      _FrameInputNode.Dispose(); 
      _FrameInputNode = null; 
     } 

     if (_Stream != null) 
     { 
      _Stream.Dispose(); 
      _Stream = null; 
     } 
    } 
} 

で再生されます。第1の発話が終了すると、StreamFinished?.Invoke(this, EventArgs.Empty);次の発話が再生されるべき待ち行列管理システム、及び

var frameInputNode = graph.CreateFrameInputNode(props); 

メッセージException from HRESULT: 0x88960001Exceptionスローラインに通知します。掘り出し物の一部にはit corresponds to XAUDIO2_E_INVALID_CALLが表示されていますが、これはあまり説明的ではありません。

どちらの場合も、AudioEncodingProperties.CreatePcmに渡されるパラメータは(22050, 1, 16)です。

何が問題になったのかについてもっと詳しく知ることができますか?最悪の場合、グラフ全体を投げ捨て、毎回新しいグラフを作成することができますが、それはむしろ非効率的なようです。

+0

で別のスレッドに、グラフの操作を移動するのですか? –

+0

@ NicoZhu-MSFT、完了。私はあなたが原因として偽のパラメータを取り除こうとしていると仮定しているので、ヘッダーを解析した結果も追加しました。 –

答えて

0

問題がStreamFinished?.Invoke(this, EventArgs.Empty);AudioFrameInputNode.QuantumStartedのドキュメントはありませんが、次の発話が

演奏されるべきキュー管理システムに通知し、

最初の発話が終了し

であると思われます禁止された行為について何かを言うと、の文書は、

と言っています。QuantumSta rtedイベントは同期的です。つまり、このイベントのハンドラ内のAudioGraphまたは個々のオーディオノードのプロパティや状態を更新することはできません。オーディオグラフを停止する、個々のオーディオノードを追加、削除、または開始するなどの操作を実行しようとすると、例外がスローされます。

これはノードのQuantumStartedイベントにも当てはまると思われます。

簡単な解決策は、あなたが `クラスSpeechStreamPlayer`のコードを埋めるでし

     Task.Run(() => StreamFinished?.Invoke(this, EventArgs.Empty)); 
関連する問題