public void ChannelLayouts()
 {
     Assert.AreNotEqual(0, SoundIOChannelLayout.BuiltInCount, "no built in channel layout?");
     for (int i = 0; i < SoundIOChannelLayout.BuiltInCount; i++)
     {
         var l    = SoundIOChannelLayout.GetBuiltIn(i);
         var name = l.DetectBuiltInName();
         Assert.AreNotEqual(null, name, "It should be built-in...");
         foreach (var c in l.Channels.Take(l.ChannelCount))
         {
             Assert.AreNotEqual(SoundIOChannelId.Invalid, c, $"Until l.ChannelCount = {l.ChannelCount}, Channel ID should be valid");
         }
         foreach (var c in l.Channels.Skip(l.ChannelCount))
         {
             Assert.AreEqual(SoundIOChannelId.Invalid, c, $"After l.ChannelCount = {l.ChannelCount}, Channel ID should be invalid");
         }
     }
     Assert.IsFalse(SoundIOChannelLayout.GetDefault(0).Channels.Any(), "soundio returned non-null layout for zero-channels??");
     for (int channels = 1; channels < 10; channels++)
     {
         var l = SoundIOChannelLayout.GetDefault(channels);
         if (l.IsNull)                 // some channel count (e.g. 9) has no applicable default device. It depends on the default device.
         {
             continue;
         }
         Assert.IsNotNull(l.DetectBuiltInName(), $"channel layout for {channels} has no builtin name...");
     }
 }
예제 #2
0
        void write_callback(SoundIOOutStream outstream, int frame_count_min, int frame_count_max)
        {
            int frames_left = frame_count_min;
            var writeSize   = (int)(Source.Format.SampleRate * WriteLatency);

            int frame_count = Math.Max(frames_left, Math.Min(writeSize, frame_count_max));

            if (frame_count == 0)
            {
                return;
            }

            var results = outstream.BeginWrite(ref frame_count);
            SoundIOChannelLayout layout = outstream.Layout;

            var buffer = isPaused ? new float[frame_count] : Source.Next(frame_count);

            for (int frame = 0; frame < frame_count; frame++)
            {
                for (int channel = 0; channel < layout.ChannelCount; channel++)
                {
                    var area = results.GetArea(channel);
                    write_sample(area.Pointer, buffer[frame]);
                    area.Pointer += area.Step;
                }
            }

            outstream.EndWrite();
        }
예제 #3
0
        /// <summary>
        /// Opens the audio track with the specified parameters
        /// </summary>
        /// <param name="sampleRate">The requested sample rate of the track</param>
        /// <param name="channelCount">The requested channel count of the track</param>
        /// <param name="callback">A <see cref="ReleaseCallback" /> that represents the delegate to invoke when a buffer has been released by the audio track</param>
        /// <param name="format">The requested sample format of the track</param>
        public void Open(
            int sampleRate,
            int channelCount,
            ReleaseCallback callback,
            SoundIOFormat format = SoundIOFormat.S16LE)
        {
            // Close any existing audio streams
            if (AudioStream != null)
            {
                Close();
            }

            if (!AudioDevice.SupportsSampleRate(sampleRate))
            {
                throw new InvalidOperationException($"This sound device does not support a sample rate of {sampleRate}Hz");
            }

            if (!AudioDevice.SupportsFormat(format))
            {
                throw new InvalidOperationException($"This sound device does not support SoundIOFormat.{Enum.GetName(typeof(SoundIOFormat), format)}");
            }

            AudioStream = AudioDevice.CreateOutStream();

            AudioStream.Name       = $"SwitchAudioTrack_{TrackID}";
            AudioStream.Layout     = SoundIOChannelLayout.GetDefault(channelCount);
            AudioStream.Format     = format;
            AudioStream.SampleRate = sampleRate;

            AudioStream.WriteCallback = WriteCallback;

            BufferReleased += callback;

            AudioStream.Open();
        }
예제 #4
0
        public void Initialize(SoundIODevice device, AudioFormat format)
        {
            if (format.Channels != 1)
            {
                throw new OutputInitializationException("Format must qualify channels == 1");
            }

            Device = device;
            Format = format;

            var bytesPerSample = format.BitDepth / 8;
            var capacity       = Format.SampleRate * Format.Channels * bytesPerSample * 30;

            ringBuffer = new RingBuffer <float>((uint)capacity);
            if (Device.ProbeError != 0)
            {
                throw new OutputInitializationException($"Probe Error : {Device.ProbeError}");
            }

            outstream = Device.CreateOutStream();
            outstream.WriteCallback     = (min, max) => write_callback(outstream, min, max);
            outstream.UnderflowCallback = () => underflow_callback(outstream);
            outstream.SampleRate        = Format.SampleRate;
            outstream.SoftwareLatency   = DesiredLatency.TotalSeconds;
            outstream.Format            = SoundIODevice.S16NE;

            outstream.Open();
            outstream.Layout          = SoundIOChannelLayout.GetDefault(Format.Channels);
            outstream.SoftwareLatency = DesiredLatency.TotalSeconds;
            api.FlushEvents();

            ActualLatency = TimeSpan.FromSeconds(outstream.SoftwareLatency);
        }
예제 #5
0
        static void WriteCallback(SoundIOOutStream stream, int frameCountMin, int frameCountMax)
        {
            double floatSampleRate = stream.SampleRate;
            double secondsPerFrame = 1.0 / floatSampleRate;
            SoundIOChannelAreas areas;
            SoundIoError        err;
            int framesLeft = frameCountMax;

            while (true)
            {
                int frameCount = framesLeft;
                err = stream.BeginWrite(out areas, ref frameCount);
                if (err != SoundIoError.None)
                {
                    throw new SoundIOException(string.Format("Unrecoverable stream error: {0}.", err.GetErrorMessage()));
                }

                if (areas == null || frameCount == 0)
                {
                    break;
                }

                SoundIOChannelLayout layout = stream.Layout;

                double pitch            = 440.0;
                double radiansPerSecond = pitch * 2.0 * Math.PI;
                for (int frame = 0; frame < frameCount; frame++)
                {
                    double sample = Math.Sin((secondsOffset + frame * secondsPerFrame) * radiansPerSecond);
                    for (int channel = 0; channel < layout.ChannelCount; channel += 1)
                    {
                        writeSample(areas[channel].Pointer, sample);
                        areas[channel].AdvancePointer();
                    }
                }
                secondsOffset = (secondsOffset + secondsPerFrame * frameCount) % 1.0;

                err = stream.EndWrite();
                if (err != SoundIoError.None)
                {
                    Console.WriteLine("EndWrite failed with error: {0}.", err);
                    if (err == SoundIoError.Underflow)
                    {
                        return;
                    }

                    throw new SoundIOException(string.Format("Unrecoverable stream error: {0}.", err.GetErrorMessage()));
                }

                framesLeft -= frameCount;
                if (framesLeft <= 0)
                {
                    break;
                }
            }

            stream.Pause(wantPause);
        }
예제 #6
0
        unsafe void write_callback(SoundIOOutStream outstream, int frame_count_min, int frame_count_max)
        {
            int frame_count = frame_count_max;
            var results     = outstream.BeginWrite(ref frame_count);

            SoundIOChannelLayout layout = outstream.Layout;

            int readBytes = frame_count * outstream.BytesPerFrame;
            int readCount = 0;
            int tryCount  = -1;
            int read;

            while (readBytes - readCount > 0 && tryCount++ < UnderflowRetryCount)
            {
                int bufferLength = (int)_ringBuffer.GetLength();
                if (bufferLength % outstream.BytesPerSample != 0)
                {
                    bufferLength -= outstream.BytesPerSample - (bufferLength % outstream.BytesPerSample);
                }

                read       = Math.Min(bufferLength, readBytes - readCount);
                readCount += read;

                byte[] buffer = new byte[read];
                _ringBuffer.Dequeue(buffer);

                SoundIOChannelArea area;
                fixed(byte *buf = buffer)
                {
                    byte *ptr;

                    for (var i = 0; i < buffer.Length; i += outstream.BytesPerSample * layout.ChannelCount)
                    {
                        for (int channel = 0; layout.ChannelCount > channel; channel++)
                        {
                            area = results.GetArea(channel);

                            ptr = (byte *)area.Pointer;
                            for (int j = 0; j < outstream.BytesPerSample; j++)
                            {
                                *ptr = buf[i + j + (channel * outstream.BytesPerSample)];
                                ptr++;
                            }

                            area.Pointer += area.Step;
                        }
                    }
                }

                if (readBytes - readCount > 0)
                {
                    Underflow?.Invoke(this, new UnderflowEventArgs(readBytes - readCount));
                }
            }

            outstream.EndWrite();
        }
예제 #7
0
 static void PrintChannelLayout(SoundIOChannelLayout layout)
 {
     if (layout.Name != null)
     {
         Console.Write("{0}", layout.Name);
     }
     else
     {
         Console.WriteLine("{0}", layout.Channels[0].GetChannelName());
         for (int i = 1; i < layout.ChannelCount; i++)
         {
             Console.WriteLine("{0}", layout.Channels[i].GetChannelName());
         }
     }
 }
예제 #8
0
        static void write_callback(SoundIOOutStream outstream, int frame_count_min, int frame_count_max)
        {
            double float_sample_rate = outstream.SampleRate;
            double seconds_per_frame = 1.0 / float_sample_rate;

            int frames_left = frame_count_max;
            int frame_count = 0;

            for (; ;)
            {
                frame_count = frames_left;
                var results = outstream.BeginWrite(ref frame_count);

                if (frame_count == 0)
                {
                    break;
                }

                SoundIOChannelLayout layout = outstream.Layout;

                double pitch = 440.0;
                double radians_per_second = pitch * 2.0 * Math.PI;
                for (int frame = 0; frame < frame_count; frame += 1)
                {
                    double sample = Math.Sin((seconds_offset + frame * seconds_per_frame) * radians_per_second);
                    for (int channel = 0; channel < layout.ChannelCount; channel += 1)
                    {
                        var area = results.GetArea(channel);
                        write_sample(area.Pointer, sample);
                        area.Pointer += area.Step;
                    }
                }
                seconds_offset = Math.IEEERemainder(seconds_offset + seconds_per_frame * frame_count, 1.0);

                outstream.EndWrite();

                frames_left -= frame_count;
                if (frames_left <= 0)
                {
                    break;
                }
            }

            outstream.Pause(want_pause);
        }