/// <summary> /// Platform specifc implementation of <see cref="Destroy"/>. /// </summary> internal void DestroyAudioEngine() { if (AudioDevice.Ptr != IntPtr.Zero) { AudioLayer.ListenerDestroy(DefaultListener.Listener); AudioLayer.Destroy(AudioDevice); } }
internal void Update() { if (Listener.Ptr == IntPtr.Zero) { return; } AudioLayer.ListenerPush3D(Listener, ref Position, ref forward, ref up, ref Velocity, ref WorldTransform); }
/// <summary> /// Initialize audio engine /// </summary> internal virtual void InitializeAudioEngine(AudioLayer.DeviceFlags flags) { AudioDevice = AudioLayer.Create(audioDevice.Name == "default" ? null : audioDevice.Name, flags); if (AudioDevice.Ptr == IntPtr.Zero) { State = AudioEngineState.Invalidated; } DefaultListener = new AudioListener(this); }
private void PlayAsyncInternal() { Task.Run(async() => { var playMe = await ReadyToPlay.Task; if (playMe) { AudioLayer.SourcePlay(SoundInstance.Source); } }); }
/// <summary> /// Should be called from working thread only (or add proper locks) /// </summary> protected override void SeekInternal() { storageBuffer.CountDataBytes = 0; //To set the begin flag to true PrepareInternal(); MediaCurrentTime = mediaCurrentTimeMax = TimeSpan.Zero; //Seek AudioLayer.SourceFlushBuffers(soundInstance.Source); SeekInternalImpl(commandSeekTime); }
/// <summary> /// Disposes the Listener /// </summary> public void Dispose() { if (Listener.Ptr == IntPtr.Zero) { return; } #if !XENKO_PLATFORM_IOS AudioLayer.ListenerDisable(Listener); AudioLayer.ListenerDestroy(Listener); #endif }
private void ActivateAudioSession() { const double preferedAudioLatency = 0.005; // start the AudioSession var audioSession = AVAudioSession.SharedInstance(); AVAudioSession.Notifications.ObserveInterruption((sender, args) => { if (args.InterruptionType == AVAudioSessionInterruptionType.Began) { AudioLayer.ListenerDisable(DefaultListener.Listener); } else { AudioLayer.ListenerEnable(DefaultListener.Listener); } }); // set the audio category so that: playback/recording is possible, playback is mixed with background music, music is stopped when screen is locked var error = audioSession.SetCategory(AVAudioSessionCategory.SoloAmbient); if (error != null) { Logger.Warning($"Failed to set the audio category to 'Ambient'. [Error info: {error.UserInfo}]"); State = AudioEngineState.Invalidated; return; } // Reduce the buffer size for better latency response.. audioSession.SetPreferredIOBufferDuration(preferedAudioLatency, out error); if (error != null) { Logger.Warning($"Failed to set the audio IO buffer duration to '{preferedAudioLatency}'. [Error info: {error.UserInfo}]"); } // set the preferred sampling rate of the application if (AudioSampleRate != 0) { audioSession.SetPreferredSampleRate(AudioSampleRate, out error); Logger.Warning($"Failed to set the audio session preferred sampling rate to '{AudioSampleRate}'. [Error info: {error.UserInfo}]"); } // activate the sound for the application error = audioSession.SetActive(true); if (error != null) { Logger.Warning($"Failed to activate the audio session. [Error info: {error.UserInfo}]"); State = AudioEngineState.Invalidated; } }
protected override void Destroy() { base.Destroy(); if (AudioEngine == null || AudioEngine.State == AudioEngineState.Invalidated) { return; } if (!StreamFromDisk) { AudioLayer.BufferDestroy(PreloadedBuffer); } }
/// <summary> /// Destroys the instance. /// </summary> protected virtual void DisposeInternal() { AudioLayer.SourceDestroy(SoundInstance.Source); foreach (var deviceBuffer in deviceBuffers) { AudioLayer.BufferDestroy(deviceBuffer); } deviceBuffers.Clear(); freeBuffers.Clear(); IsDisposed = true; IsInitialized = false; }
/// <summary> /// Sub classes can implement their own streaming sources. /// </summary> /// <param name="soundInstance">the sound instance associated.</param> /// <param name="numberOfBuffers">the size of the streaming ring-buffer.</param> /// <param name="maxBufferSizeBytes">the maximum size of each buffer.</param> protected DynamicSoundSource(SoundInstance soundInstance, int numberOfBuffers, int maxBufferSizeBytes) { nativeBufferSizeBytes = maxBufferSizeBytes; prebufferedTarget = (int)Math.Ceiling(numberOfBuffers / (double)3); SoundInstance = soundInstance; for (var i = 0; i < numberOfBuffers; i++) { var buffer = AudioLayer.BufferCreate(nativeBufferSizeBytes); deviceBuffers.Add(buffer); freeBuffers.Enqueue(deviceBuffers[i]); } if (readFromDiskWorker == null) { readFromDiskWorker = Task.Factory.StartNew(Worker, TaskCreationOptions.LongRunning); } }
protected virtual void PlayInternal() { switch (State) { case PlayState.Playing: break; case PlayState.Paused: AudioLayer.SourcePlay(SoundInstance.Source); break; case PlayState.Stopped: Ended.TrySetResult(false); Ended = new TaskCompletionSource <bool>(); PlayAsyncInternal(); break; } PlayingQueued = false; State = PlayState.Playing; }
/// <summary> /// Initializes a new instance of the <see cref="SoundInstance"/> class using a dynamic sound source. /// </summary> /// <param name="engine">The audio engine that will be used to play this instance</param> /// <param name="listener">The listener of this instance</param> /// <param name="dynamicSoundSource">The source from where the PCM data will be fetched</param> /// <param name="sampleRate">The sample rate of this audio stream</param> /// <param name="mono">Set to true if the souce is mono, false if stereo</param> /// <param name="spatialized">If the SoundInstance will be used for spatialized audio set to true, if not false, if true mono must also be true</param> /// <param name="useHrtf">If the engine should use Hrtf for spatialization</param> /// <param name="directionalFactor"></param> /// <param name="environment"></param> public SoundInstance(AudioEngine engine, AudioListener listener, DynamicSoundSource dynamicSoundSource, int sampleRate, bool mono, bool spatialized = false, bool useHrtf = false, float directionalFactor = 0.0f, HrtfEnvironment environment = HrtfEnvironment.Small) { Listener = listener; this.engine = engine; this.spatialized = spatialized; soundSource = dynamicSoundSource; if (engine.State == AudioEngineState.Invalidated) { return; } Source = AudioLayer.SourceCreate(listener.Listener, sampleRate, dynamicSoundSource.MaxNumberOfBuffers, mono, spatialized, true, useHrtf, directionalFactor, environment); if (Source.Ptr == IntPtr.Zero) { throw new Exception("Failed to create an AudioLayer Source"); } ResetStateToDefault(); }
/// <summary> /// Initializes a new instance of the <see cref="DynamicSoundSource"/> class. /// Sub classes can implement their own streaming sources. /// </summary> /// <param name="soundInstance">the sound instance associated.</param> /// <param name="numberOfBuffers">the size of the streaming ring-buffer.</param> /// <param name="maxBufferSizeBytes">the maximum size of each buffer.</param> protected DynamicSoundSource(SoundInstance soundInstance, int numberOfBuffers, int maxBufferSizeBytes) { nativeBufferSizeBytes = maxBufferSizeBytes; prebufferedTarget = (int)Math.Ceiling(numberOfBuffers / 3.0); this.soundInstance = soundInstance; for (var i = 0; i < numberOfBuffers; i++) { var buffer = AudioLayer.BufferCreate(nativeBufferSizeBytes); deviceBuffers.Add(buffer); freeBuffers.Enqueue(deviceBuffers[i]); } if (readFromDiskWorker == null) { readFromDiskWorker = new Thread(new ThreadStart(Worker)); readFromDiskWorker.Priority = ThreadPriority.BelowNormal; readFromDiskWorker.IsBackground = true; readFromDiskWorker.Start(); } }
public AudioListener(AudioEngine engine) { if (engine.State == AudioEngineState.Invalidated) { return; } #if XENKO_PLATFORM_IOS if (engine.DefaultListener == null) { Listener = AudioLayer.ListenerCreate(engine.AudioDevice); AudioLayer.ListenerEnable(Listener); } else { Listener = engine.DefaultListener.Listener; } #else Listener = AudioLayer.ListenerCreate(engine.AudioDevice); AudioLayer.ListenerEnable(Listener); #endif }
/// <summary> /// If CanFillis true with this method you can fill the next free buffer /// </summary> /// <param name="pcm">The pointer to PCM data</param> /// <param name="bufferSize">The full size in bytes of PCM data</param> /// <param name="type">If this buffer is the last buffer of the stream set to true, if not false</param> protected void FillBuffer(IntPtr pcm, int bufferSize, AudioLayer.BufferType type) { if (bufferSize > nativeBufferSizeBytes) { Logger.Error("Provided buffer size is bigger than native buffer. Data will be cut."); bufferSize = nativeBufferSizeBytes; } var buffer = freeBuffers.Dequeue(); AudioLayer.SourceQueueBuffer(SoundInstance.Source, buffer, pcm, bufferSize, type); if (readyToPlay) { return; } prebufferedCount++; if (prebufferedCount < prebufferedTarget) { return; } readyToPlay = true; ReadyToPlay.TrySetResult(true); }
private static unsafe void Worker() { var toRemove = new List <DynamicSoundSource>(); while (true) { toRemove.Clear(); while (!NewSources.IsEmpty) { if (!NewSources.TryTake(out var source)) { continue; } if (!source.IsInitialized) { source.InitializeInternal(); } if (source.IsInitialized) { Sources.Add(source); } } foreach (var source in Sources) { if (source.IsDisposed) { toRemove.Add(source); continue; } source.UpdateInternal(); var seekRequested = false; while (!source.Commands.IsEmpty) { AsyncCommand command; if (!source.Commands.TryDequeue(out command)) { continue; } switch (command) { case AsyncCommand.Play: source.PlayInternal(); break; case AsyncCommand.Pause: source.PauseInternal(); break; case AsyncCommand.Stop: source.StopInternal(); break; case AsyncCommand.Seek: seekRequested = true; break; case AsyncCommand.SetRange: source.RestartInternal(); break; case AsyncCommand.Dispose: source.DisposeInternal(); toRemove.Add(source); break; default: throw new ArgumentOutOfRangeException(); } } if (source.IsDisposed) { continue; } source.isSourcePausedOrPlaying = (source.IsPausedOrPlaying && !source.Ended.Task.IsCompleted) || AudioLayer.SourceIsPlaying(source.SoundInstance.Source); //Did we get a Seek request? if (seekRequested) { source.SeekInternal(); continue; } if (source.CanFill) { source.ExtractAndFillData(); } } foreach (var source in toRemove) { Sources.Remove(source); } var buffersShouldBeFill = false; foreach (var source in Sources) { if (source.CanFill) { buffersShouldBeFill = true; break; } } if (!buffersShouldBeFill) // avoid active looping when no work is needed { Utilities.Sleep(10); } } }
protected virtual void PauseInternal() { State = PlayState.Paused; AudioLayer.SourcePause(SoundInstance.Source); }
internal void Apply3D(AudioLayer.Source source) { AudioLayer.SourcePush3D(source, ref Position, ref forward, ref up, ref Velocity, ref WorldTransform); }