void write_callback(SoundIOOutStream outstream, int frame_count_min, int frame_count_max) { int frameCount = frame_count_max; if (frameCount <= 0) { return; } var results = outstream.BeginWrite(ref frameCount); var samples = new float[frameCount]; var e = new OutputBufferEventArgs(frameCount); Buffer?.Invoke(this, e); if (e.Buffer != null) { ringBuffer.Enqueue(e.Buffer); } ringBuffer.Dequeue(samples, frameCount); var layout = outstream.Layout; for (int frame = 0; frame < frameCount; frame++) { for (int channel = 0; channel < layout.ChannelCount; channel++) { var area = results.GetArea(channel); // Raspberry Piではなぜかshortじゃないと音がプツプツする write_short_sample(area.Pointer, samples[frame]); area.Pointer += area.Step; } } outstream.EndWrite(); unsafe void write_short_sample(IntPtr ptr, float sample) { short *buf = (short *)ptr; *buf = (short)(sample * short.MaxValue); } unsafe void write_float_sample(IntPtr ptr, float sample) { float *buf = (float *)ptr; *buf = sample; } } void underflow_callback(SoundIOOutStream outstream) { Underflow?.Invoke(this, EventArgs.Empty); } } }
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(); }
private void initInternal(AudioFormat format) { if (Device == null) { throw new Exception("No device is selected"); } 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?.Invoke(this, new UnderflowEventArgs(null)); _outstream.ErrorCallback = () => UnrecoverableError?.Invoke(this, EventArgs.Empty); _outstream.SampleRate = format.SampleRate; _outstream.SoftwareLatency = DesiredLatency.TotalSeconds; var soundioFormat = Soundio.ToSoundioFormat(format); _outstream.Format = soundioFormat ?? SoundIOFormat.Invalid; if (_outstream.LayoutErrorMessage != null) { var msg = _outstream.LayoutErrorMessage; Console.WriteLine($"Channel Layout Error : {msg}"); } _outstream.Open(); _api.FlushEvents(); Format = Soundio.ToManagedFormat(_outstream.Format, _outstream.SampleRate, _outstream.Layout.ChannelCount); SoftwareLatency = TimeSpan.FromSeconds(_outstream.SoftwareLatency); var bytesPerSample = _outstream.BytesPerSample; var capacity = Format.SampleRate * Format.Channels * bytesPerSample * _bufferDuration.TotalSeconds; _ringBuffer = new RingBuffer <byte>((uint)capacity); }
protected override async Task BeforeEach() { underflow = await Underflow.New(RpcClient); // Peform contract deployments and other test setup. }