| File: Internal\Synthesis\AudioFileOut.cs | Web Access |
| Project: src\runtime\src\libraries\System.Speech\src\System.Speech.csproj (System.Speech) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.IO; using System.Speech.AudioFormat; using System.Threading; namespace System.Speech.Internal.Synthesis { /// <summary> /// Encapsulates Waveform Audio Interface playback functions and provides a simple /// interface for playing audio. /// </summary> internal class AudioFileOut : AudioBase, IDisposable { #region Constructors /// <summary> /// Create an instance of AudioFileOut. /// </summary> internal AudioFileOut(Stream stream, SpeechAudioFormatInfo? formatInfo, bool headerInfo, IAsyncDispatch asyncDispatch) { _asyncDispatch = asyncDispatch; _stream = stream; _startStreamPosition = _stream.Position; _hasHeader = headerInfo; _wfxOut = new WAVEFORMATEX(); // if we have a formatInfo object, format conversion may be necessary if (formatInfo != null) { // Build the Wave format from the formatInfo _wfxOut.wFormatTag = (short)formatInfo.EncodingFormat; _wfxOut.wBitsPerSample = (short)formatInfo.BitsPerSample; _wfxOut.nSamplesPerSec = formatInfo.SamplesPerSecond; _wfxOut.nChannels = (short)formatInfo.ChannelCount; } else { // Set the default values _wfxOut = WAVEFORMATEX.Default; } _wfxOut.nBlockAlign = (short)(_wfxOut.nChannels * _wfxOut.wBitsPerSample / 8); _wfxOut.nAvgBytesPerSec = _wfxOut.wBitsPerSample * _wfxOut.nSamplesPerSec * _wfxOut.nChannels / 8; } public void Dispose() { _evt.Close(); GC.SuppressFinalize(this); } #endregion #region Internal Methods /// <summary> /// Begin to play /// </summary> internal override void Begin(byte[] wfx) { if (_deviceOpen) { System.Diagnostics.Debug.Assert(false); throw new InvalidOperationException(); } // Get the audio format if conversion is needed _wfxIn = WAVEFORMATEX.ToWaveHeader(wfx); _doConversion = _pcmConverter.PrepareConverter(ref _wfxIn, ref _wfxOut); if (_totalByteWrittens == 0 && _hasHeader) { WriteWaveHeader(_stream, _wfxOut, _startStreamPosition, 0); } _bytesWritten = 0; // set the flags _aborted = false; _deviceOpen = true; } /// <summary> /// Begin to play /// </summary> internal override void End() { if (!_deviceOpen) { System.Diagnostics.Debug.Assert(false); throw new InvalidOperationException(); } _deviceOpen = false; if (!_aborted) { if (_hasHeader) { long position = _stream.Position; WriteWaveHeader(_stream, _wfxOut, _startStreamPosition, _totalByteWrittens); _stream.Seek(position, SeekOrigin.Begin); } } } #region AudioDevice implementation /// <summary> /// Play a wave file. /// </summary> internal override void Play(byte[] buffer) { if (!_deviceOpen) { System.Diagnostics.Debug.Assert(false); } else { byte[] abOut = _doConversion ? _pcmConverter.ConvertSamples(buffer) : buffer; if (_paused) { _evt.WaitOne(); _evt.Reset(); } if (!_aborted) { _stream.Write(abOut, 0, abOut.Length); _totalByteWrittens += abOut.Length; _bytesWritten += abOut.Length; } } } /// <summary> /// Pause the playback of a sound. /// </summary> internal override void Pause() { if (!_aborted && !_paused) { lock (_noWriteOutLock) { _paused = true; } } } /// <summary> /// Resume the playback of a paused sound. /// </summary> internal override void Resume() { if (!_aborted && _paused) { lock (_noWriteOutLock) { _paused = false; _evt.Set(); } } } /// <summary> /// Wait for all the queued buffers to be played /// </summary> internal override void Abort() { lock (_noWriteOutLock) { _aborted = true; _paused = false; _evt.Set(); } } internal override void InjectEvent(TTSEvent ttsEvent) { if (!_aborted && _asyncDispatch != null) { _asyncDispatch.Post(ttsEvent); } } /// <summary> /// File operation are basically synchronous /// </summary> internal override void WaitUntilDone() { lock (_noWriteOutLock) { } } #endregion #endregion #region Internal Fields internal override TimeSpan Duration { get { if (_wfxIn.nAvgBytesPerSec == 0) { return new TimeSpan(0); } return new TimeSpan((_bytesWritten * TimeSpan.TicksPerSecond) / _wfxIn.nAvgBytesPerSec); } } internal override long Position { get { return _stream.Position; } } internal override byte[] WaveFormat { get { return _wfxOut.ToBytes(); } } #endregion #region Private Fields protected ManualResetEvent _evt = new(false); protected bool _deviceOpen; protected Stream _stream; protected PcmConverter _pcmConverter = new(); protected bool _doConversion; protected bool _paused; protected int _totalByteWrittens; protected int _bytesWritten; #endregion #region Private Fields private IAsyncDispatch _asyncDispatch; private object _noWriteOutLock = new(); private WAVEFORMATEX _wfxIn; private WAVEFORMATEX _wfxOut; private bool _hasHeader; private long _startStreamPosition; #endregion } }