/// <summary> /// cTor: Copy constructor /// </summary> /// <param name="other"></param> public SoundBite(SoundBite other) { this.Melody = other.Melody; this.Tone = other.Tone; this.Duration = other.Duration; this.Loops = other.Loops; this.Volume = other.Volume; this.SpeedFact = other.SpeedFact; }
/// <summary> /// Change the sound to play /// </summary> /// <param name="soundBite">The SoundBite to play from now on</param> public void AddSoundBite(SoundBite soundBite) { if (!this.IsBusy) { throw new InvalidOperationException("PingLib-Loop: ERROR - Cannot add SoundBites when not initialized"); } lock ( _soundBiteLock ) { _soundBite = new SoundBite(soundBite); } }
/// <summary> /// Asynchronously plays a sound. /// Waiting for this will not wait for when the Play has ended /// but rather when the trigger for Output was given /// There is currently no event available to detect end of Play /// </summary> /// <param name="soundBite">The SoundBite to play</param> public async Task PlayAsync(SoundBite soundBite) { if (!_canPlay) { return; // Cannot } if (_playing) { return; // no concurrent playing } await PlayAsyncLow(soundBite); }
/// <summary> /// Add a sound to play /// </summary> /// <param name="soundBite">The SoundBite to play</param> public void AddSoundBite(SoundBite soundBite) { if (!this.IsBusy) { throw new InvalidOperationException("PingLib-Sound: ERROR Cannot add SoundBites when not initialized"); } lock ( _soundBites ) { _soundBites.Enqueue(soundBite); // restart if needed if (_soundBites.Count == 1) { _workerWaitHandle.Set( ); } } }
/// <summary> /// Clear the current Word queue /// </summary> public void ClearSoundBites( ) { lock ( _soundBiteLock ) { _soundBite = new SoundBite( ); // Load a default (which is Silence in 1sec duration tones) } }
// Async Speak output of a text private async Task PlayAsyncLow(SoundBite soundBite) { _playing = true; // locks additional calls for Speak until finished talking this bit if (!_canPlay || _audioGraph == null || _deviceOutputNode == null) { LOG.LogError($"PlayAsyncLow: Some items do not exist: cannot play..\n [{_audioGraph}] [{_deviceOutputNode}]"); await EndOfSound( ); return; } // don't reload if the sound is already in use if (soundBite.Melody != _soundInUse?.Melody) { // if a prev. Node exists, remove it if (_fileInputNode != null) { _audioGraph.Stop( ); _fileInputNode.FileCompleted -= _fileInputNode_FileCompleted; _fileInputNode.RemoveOutgoingConnection(_deviceOutputNode); _fileInputNode.Dispose( ); _fileInputNode = null; } // set new sound _sound = _installedSounds.Where(x => x.Melody == soundBite.Melody).FirstOrDefault( ); if (_sound == null) { LOG.LogError($"PlayAsyncLow: Melody has no Audiofile: {soundBite.Melody} - cannot play"); await EndOfSound( ); return; } StorageFile file = await StorageFile.CreateStreamedFileAsync($"{_sound.Id}.{_sound.SType}", StreamedFileWriter, null); // create the InputNode var resultAF = await _audioGraph.CreateFileInputNodeAsync(file); if (resultAF.Status != AudioFileNodeCreationStatus.Success) { LOG.LogError($"PlayAsyncLow: AudioFileNodeCreationStatus creation: {resultAF.Status}" + $"\nExtError: {resultAF.ExtendedError}"); await EndOfSound( ); return; } _fileInputNode = resultAF.FileInputNode; _fileInputNode.FileCompleted += _fileInputNode_FileCompleted; _fileInputNode.AddOutgoingConnection(_deviceOutputNode); _audioGraph.Start( ); _soundInUse = _sound.AsCopy( ); } // we capture problems through Exceptions here - the settings and restrictions seem not complete in the Docs try { // Play it // cannot start after prev end - so set it null and seek to start of file _fileInputNode.StartTime = null; _fileInputNode.EndTime = null; // cannot start after prev end - so set it null _fileInputNode.Seek(new TimeSpan(0)); // have to seek to Start, we cannot assign a StartTime before the current Position // only now we can set any new start and end... (that is not in the documents...) _fileInputNode.StartTime = TimeSpan.FromSeconds(soundBite.Tone * _soundInUse.ToneStep_sec); _fileInputNode.EndTime = _fileInputNode.StartTime + TimeSpan.FromSeconds((soundBite.Duration < 0) ? _soundInUse.ToneDuration_sec : soundBite.Duration); _fileInputNode.OutgoingGain = soundBite.Volume; _fileInputNode.LoopCount = (int)soundBite.Loops; // counts down in the Completed Callback - track completeness there (not in docu...) _fileInputNode.PlaybackSpeedFactor = soundBite.SpeedFact; // Plays in the current Thread - cannot be waited for in the same thread _fileInputNode.Start( ); //_audioGraph.Start( ); } catch (Exception e) { LOG.LogError($"PlayAsyncLow: Sample Setup caused an Exception\n{e.Message}"); await EndOfSound( ); } }
/// <summary> /// Synchronously plays soundBites /// Audio output is the default output device /// NOT YET IMPLEMENTED behaves the same as PlayAsync /// </summary> /// <param name="soundBite">The SoundBite to play</param> public void Play(SoundBite soundBite) { _player.AddSoundBite(soundBite); // TODO }
/// <summary> /// Asynchronously plays soundBites /// Audio output is the default output device /// This is not an awaitable method /// </summary> /// <param name="soundBite">The SoundBite to play (max 10sec supported)</param> public void PlayAsync(SoundBite soundBite) { _player.AddSoundBite(soundBite); }