예제 #1
0
 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));
 }
예제 #2
0
 /// <summary>
 /// Platform specifc implementation of <see cref="Destroy"/>.
 /// </summary>
 internal void DestroyAudioEngine()
 {
     if (AudioDevice.Ptr != IntPtr.Zero)
     {
         AudioLayer.ListenerDestroy(DefaultListener.Listener);
         AudioLayer.Destroy(AudioDevice);
     }
 }
예제 #3
0
        /// <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);
        }
예제 #4
0
 /// <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]);
     }
 }
예제 #5
0
 private static void SourcePlayAsync(CompressedSoundSource source)
 {
     Task.Run(async() =>
     {
         var playMe = await source.ReadyToPlay.Task;
         if (playMe)
         {
             AudioLayer.SourcePlay(source.SoundInstance.Source);
         }
     });
 }
예제 #6
0
        /// <summary>
        /// Destroys the instance.
        /// </summary>
        protected override void Destroy()
        {
            if (AudioEngine == null || AudioEngine.State == AudioEngineState.Invalidated)
            {
                return;
            }

            if (!StreamFromDisk)
            {
                AudioLayer.BufferDestroy(PreloadedBuffer);
            }
        }
예제 #7
0
        /// <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
        }
예제 #8
0
        /// <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]);
            }
        }
예제 #9
0
        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;
            }
        }
예제 #10
0
        /// <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;
        }
예제 #11
0
        /// <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;
        }
예제 #12
0
        /// <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);
        }
예제 #13
0
        /// <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);
        }
예제 #14
0
        /// <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);
        }
예제 #15
0
        /// <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();
        }
예제 #16
0
        /// <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();
        }
예제 #17
0
        protected override void Destroy()
        {
            base.Destroy();

            if (IsDisposed)
            {
                return;
            }

            Stop();

            soundSource?.Dispose();
            sound?.UnregisterInstance(this);

            if (engine.State == AudioEngineState.Invalidated)
            {
                return;
            }

            AudioLayer.SourceDestroy(Source);
        }
예제 #18
0
        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
        }
예제 #19
0
        /// <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;
        }
예제 #20
0
        /// <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;
        }
예제 #21
0
 internal void Apply3D(AudioLayer.Source source)
 {
     AudioLayer.SourcePush3D(source, ref Position, ref forward, ref up, ref Velocity, ref WorldTransform);
 }
예제 #22
0
 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));
 }
예제 #23
0
        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);
            }
        }