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..."); } }
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(); }
/// <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(); }
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); }
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); }
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(); }
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()); } } }
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); }