public unsafe void Update() { if (Listener.Ptr == IntPtr.Zero) { return; } AudioLayer.ListenerPush3D(Listener, (float *)Interop.Fixed(ref Position), (float *)Interop.Fixed(ref forward), (float *)Interop.Fixed(ref up), (float *)Interop.Fixed(ref Velocity)); }
/// <summary> /// Platform specifc implementation of <see cref="Destroy"/>. /// </summary> internal void DestroyAudioEngine() { if (AudioDevice.Ptr != IntPtr.Zero) { AudioLayer.ListenerDestroy(DefaultListener.Listener); AudioLayer.Destroy(AudioDevice); } }
/// <summary> /// Initialize audio engine /// </summary> internal virtual void InitializeAudioEngine() { AudioDevice = AudioLayer.Create(audioDevice.Name == "default" ? null : audioDevice.Name); if (AudioDevice.Ptr == IntPtr.Zero) { State = AudioEngineState.Invalidated; } DefaultListener = new AudioListener(this); }
/// <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) { SoundInstance = soundInstance; for (var i = 0; i < numberOfBuffers; i++) { var buffer = AudioLayer.BufferCreate(maxBufferSizeBytes); deviceBuffers.Add(buffer); freeBuffers.Enqueue(deviceBuffers[i]); } }
private static void SourcePlayAsync(CompressedSoundSource source) { Task.Run(async() => { var playMe = await source.ReadyToPlay.Task; if (playMe) { AudioLayer.SourcePlay(source.SoundInstance.Source); } }); }
/// <summary> /// Destroys the instance. /// </summary> protected override void Destroy() { if (AudioEngine == null || AudioEngine.State == AudioEngineState.Invalidated) { return; } if (!StreamFromDisk) { AudioLayer.BufferDestroy(PreloadedBuffer); } }
/// <summary> /// Disposes the Listener /// </summary> public void Dispose() { if (Listener.Ptr == IntPtr.Zero) { return; } #if !SILICONSTUDIO_PLATFORM_IOS AudioLayer.ListenerDisable(Listener); AudioLayer.ListenerDestroy(Listener); #endif }
/// <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) { prebufferedTarget = (int)Math.Ceiling(numberOfBuffers / (double)3); SoundInstance = soundInstance; for (var i = 0; i < numberOfBuffers; i++) { var buffer = AudioLayer.BufferCreate(maxBufferSizeBytes); deviceBuffers.Add(buffer); freeBuffers.Enqueue(deviceBuffers[i]); } }
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: {0}]", 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 '{1}'. [Error info: {0}]", error.UserInfo, preferedAudioLatency); } // 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 '{1}'. [Error info: {0}]", error.UserInfo, AudioSampleRate); } // activate the sound for the application error = audioSession.SetActive(true); if (error != null) { Logger.Warning("Failed to activate the audio session. [Error info: {0}]", error.UserInfo); State = AudioEngineState.Invalidated; } }
/// <summary> /// Pause the sounds. /// </summary> /// <remarks>A call to Pause when the sound is already paused or stopped has no effects.</remarks> public void Pause() { if (engine.State == AudioEngineState.Invalidated) { return; } if (PlayState != SoundPlayState.Playing) { return; } AudioLayer.SourcePause(Source); playState = SoundPlayState.Paused; }
/// <summary> /// Stop playing the sound immediately and reset the sound to the beginning of the track. /// </summary> /// <remarks>A call to Stop when the sound is already stopped has no effects</remarks> public void Stop() { if (engine.State == AudioEngineState.Invalidated) { return; } if (playState == SoundPlayState.Stopped) { return; } AudioLayer.SourceStop(Source); soundSource?.Restart(); playState = SoundPlayState.Stopped; }
/// <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) { 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); }
/// <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="endOfStream">If this buffer is the last buffer of the stream set to true, if not false</param> protected void FillBuffer(IntPtr pcm, int bufferSize, bool endOfStream) { var buffer = freeBuffers.Dequeue(); AudioLayer.SourceQueueBuffer(SoundInstance.Source, buffer, pcm, bufferSize, endOfStream); if (readyToPlay) { return; } prebufferedCount++; if (prebufferedCount > 1) { return; } readyToPlay = true; ReadyToPlay.TrySetResult(true); }
/// <summary> /// If CanFillis true with this method you can fill the next free buffer /// </summary> /// <param name="pcm">The array containing 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 unsafe void FillBuffer(short[] pcm, int bufferSize, AudioLayer.BufferType type) { var buffer = freeBuffers.Dequeue(); fixed(short *pcmBuffer = pcm) AudioLayer.SourceQueueBuffer(SoundInstance.Source, buffer, new IntPtr(pcmBuffer), bufferSize, type); if (readyToPlay) { return; } prebufferedCount++; if (prebufferedCount < prebufferedTarget) { return; } readyToPlay = true; ReadyToPlay.TrySetResult(true); }
/// <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> public SoundInstance(AudioEngine engine, AudioListener listener, DynamicSoundSource dynamicSoundSource, int sampleRate, bool mono, bool spatialized = false) { 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); if (Source.Ptr == IntPtr.Zero) { throw new Exception("Failed to create an AudioLayer Source"); } ResetStateToDefault(); }
/// <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(); }
protected override void Destroy() { base.Destroy(); if (IsDisposed) { return; } Stop(); soundSource?.Dispose(); sound?.UnregisterInstance(this); if (engine.State == AudioEngineState.Invalidated) { return; } AudioLayer.SourceDestroy(Source); }
public AudioListener(AudioEngine engine) { if (engine.State == AudioEngineState.Invalidated) { return; } #if SILICONSTUDIO_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> /// Stop playing the sound immediately and reset the sound to the beginning of the track. /// </summary> /// <remarks>A call to Stop when the sound is already stopped has no effects.</remarks> public void Stop() { if (engine.State == AudioEngineState.Invalidated) { return; } if (playState == SoundPlayState.Stopped) { return; } if (soundSource == null) { AudioLayer.SourceStop(Source); } else { soundSource.Stop(); } playState = SoundPlayState.Stopped; }
/// <summary> /// Pause the sounds. /// </summary> /// <remarks>A call to Pause when the sound is already paused or stopped has no effects.</remarks> public void Pause() { if (engine.State == AudioEngineState.Invalidated) { return; } if (PlayState != SoundPlayState.Playing) { return; } if (soundSource == null) { AudioLayer.SourcePause(Source); } else { soundSource.Pause(); } playState = SoundPlayState.Paused; }
internal void Apply3D(AudioLayer.Source source) { AudioLayer.SourcePush3D(source, ref Position, ref forward, ref up, ref Velocity, ref WorldTransform); }
internal unsafe void Apply3D(AudioLayer.Source source) { AudioLayer.SourcePush3D(source, (float *)Interop.Fixed(ref Position), (float *)Interop.Fixed(ref forward), (float *)Interop.Fixed(ref up), (float *)Interop.Fixed(ref Velocity)); }
private static unsafe void Worker() { var utilityBuffer = new UnmanagedArray <short>(SamplesPerBuffer * MaxChannels); var toRemove = new List <CompressedSoundSource>(); while (true) { toRemove.Clear(); while (!NewSources.IsEmpty) { CompressedSoundSource source; if (!NewSources.TryTake(out source)) { continue; } source.compressedSoundStream = ContentManager.FileProvider.OpenStream(source.soundStreamUrl, VirtualFileMode.Open, VirtualFileAccess.Read, VirtualFileShare.Read, StreamFlags.Seekable); source.decoder = new Celt(source.sampleRate, SamplesPerFrame, source.channels, true); source.compressedBuffer = new byte[source.maxCompressedSize]; source.reader = new BinarySerializationReader(source.compressedSoundStream); Sources.Add(source); } foreach (var source in Sources) { if (!source.Disposed) { while (!source.Commands.IsEmpty) { AsyncCommand command; if (!source.Commands.TryDequeue(out command)) { continue; } switch (command) { case AsyncCommand.Play: if (source.Playing && !source.Paused) { break; } if (!source.Paused) { source.Restart(); SourcePrepare(source); SourcePlayAsync(source); } else { AudioLayer.SourcePlay(source.SoundInstance.Source); } source.playing = true; source.Playing = true; source.Paused = false; source.PlayingQueued = false; break; case AsyncCommand.Pause: source.Paused = true; AudioLayer.SourcePause(source.SoundInstance.Source); break; case AsyncCommand.Stop: source.Paused = false; source.Playing = false; source.playing = false; AudioLayer.SourceStop(source.SoundInstance.Source); break; case AsyncCommand.SetRange: source.Restart(); SourcePrepare(source); break; case AsyncCommand.Dispose: AudioLayer.SourceDestroy(source.SoundInstance.Source); source.Destroy(); source.Disposed = true; toRemove.Add(source); break; default: throw new ArgumentOutOfRangeException(); } } source.PlayingState = (source.Playing && !source.Ended.Task.IsCompleted) || AudioLayer.SourceIsPlaying(source.SoundInstance.Source); if (!source.Playing || !source.CanFill || !source.playing) { continue; } const int passes = SamplesPerBuffer / SamplesPerFrame; var offset = 0; var bufferPtr = (short *)utilityBuffer.Pointer; var startingPacket = source.startingPacketIndex == source.currentPacketIndex; var endingPacket = false; for (var i = 0; i < passes; i++) { endingPacket = source.endPacketIndex == source.currentPacketIndex; //read one packet, size first, then data var len = source.reader.ReadInt16(); source.compressedSoundStream.Read(source.compressedBuffer, 0, len); source.currentPacketIndex++; var writePtr = bufferPtr + offset; if (source.decoder.Decode(source.compressedBuffer, len, writePtr) != SamplesPerFrame) { throw new Exception("Celt decoder returned a wrong decoding buffer size."); } offset += SamplesPerFrame * source.channels; if (source.compressedSoundStream.Position != source.compressedSoundStream.Length && !endingPacket) { continue; } if (source.looped) { //prepare again to play from begin SourcePrepare(source); } else { source.playing = false; source.Ended.TrySetResult(true); } break; } var finalPtr = new IntPtr(bufferPtr + (startingPacket ? source.startPktSampleIndex : 0)); var finalSize = (offset - (startingPacket ? source.startPktSampleIndex : 0) - (endingPacket ? source.endPktSampleIndex : 0)) * sizeof(short); var bufferType = AudioLayer.BufferType.None; if (endingPacket) { bufferType = source.looped ? AudioLayer.BufferType.EndOfLoop : AudioLayer.BufferType.EndOfStream; } else if (source.begin) { bufferType = AudioLayer.BufferType.BeginOfStream; source.begin = false; } source.FillBuffer(finalPtr, finalSize, bufferType); } else { toRemove.Add(source); } } foreach (var source in toRemove) { Sources.Remove(source); } Utilities.Sleep(20); } }