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(); }
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); }
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(); }
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); }
private unsafe void Update(int minFrameCount, int maxFrameCount) { int bytesPerFrame = _outputStream.BytesPerFrame; uint bytesPerSample = (uint)_outputStream.BytesPerSample; int bufferedFrames = _ringBuffer.Length / bytesPerFrame; int frameCount = Math.Min(bufferedFrames, maxFrameCount); if (frameCount == 0) { return; } SoundIOChannelAreas areas = _outputStream.BeginWrite(ref frameCount); int channelCount = areas.ChannelCount; byte[] samples = new byte[frameCount * bytesPerFrame]; _ringBuffer.Read(samples, 0, samples.Length); // This is a huge ugly block of code, but we save // a significant amount of time over the generic // loop that handles other channel counts. // TODO: Is this still right in 2021? // Mono if (channelCount == 1) { SoundIOChannelArea area = areas.GetArea(0); fixed(byte *srcptr = samples) { if (bytesPerSample == 1) { for (int frame = 0; frame < frameCount; frame++) { ((byte *)area.Pointer)[0] = srcptr[frame * bytesPerFrame]; area.Pointer += area.Step; } } else if (bytesPerSample == 2) { for (int frame = 0; frame < frameCount; frame++) { ((short *)area.Pointer)[0] = ((short *)srcptr)[frame * bytesPerFrame >> 1]; area.Pointer += area.Step; } } else if (bytesPerSample == 4) { for (int frame = 0; frame < frameCount; frame++) { ((int *)area.Pointer)[0] = ((int *)srcptr)[frame * bytesPerFrame >> 2]; area.Pointer += area.Step; } } else { for (int frame = 0; frame < frameCount; frame++) { Unsafe.CopyBlockUnaligned((byte *)area.Pointer, srcptr + (frame * bytesPerFrame), bytesPerSample); area.Pointer += area.Step; } } } } // Stereo else if (channelCount == 2) { SoundIOChannelArea area1 = areas.GetArea(0); SoundIOChannelArea area2 = areas.GetArea(1); fixed(byte *srcptr = samples) { if (bytesPerSample == 1) { for (int frame = 0; frame < frameCount; frame++) { // Channel 1 ((byte *)area1.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 0]; // Channel 2 ((byte *)area2.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 1]; area1.Pointer += area1.Step; area2.Pointer += area2.Step; } } else if (bytesPerSample == 2) { for (int frame = 0; frame < frameCount; frame++) { // Channel 1 ((short *)area1.Pointer)[0] = ((short *)srcptr)[(frame * bytesPerFrame >> 1) + 0]; // Channel 2 ((short *)area2.Pointer)[0] = ((short *)srcptr)[(frame * bytesPerFrame >> 1) + 1]; area1.Pointer += area1.Step; area2.Pointer += area2.Step; } } else if (bytesPerSample == 4) { for (int frame = 0; frame < frameCount; frame++) { // Channel 1 ((int *)area1.Pointer)[0] = ((int *)srcptr)[(frame * bytesPerFrame >> 2) + 0]; // Channel 2 ((int *)area2.Pointer)[0] = ((int *)srcptr)[(frame * bytesPerFrame >> 2) + 1]; area1.Pointer += area1.Step; area2.Pointer += area2.Step; } } else { for (int frame = 0; frame < frameCount; frame++) { // Channel 1 Unsafe.CopyBlockUnaligned((byte *)area1.Pointer, srcptr + (frame * bytesPerFrame) + (0 * bytesPerSample), bytesPerSample); // Channel 2 Unsafe.CopyBlockUnaligned((byte *)area2.Pointer, srcptr + (frame * bytesPerFrame) + (1 * bytesPerSample), bytesPerSample); area1.Pointer += area1.Step; area2.Pointer += area2.Step; } } } } // Surround else if (channelCount == 6) { SoundIOChannelArea area1 = areas.GetArea(0); SoundIOChannelArea area2 = areas.GetArea(1); SoundIOChannelArea area3 = areas.GetArea(2); SoundIOChannelArea area4 = areas.GetArea(3); SoundIOChannelArea area5 = areas.GetArea(4); SoundIOChannelArea area6 = areas.GetArea(5); fixed(byte *srcptr = samples) { if (bytesPerSample == 1) { for (int frame = 0; frame < frameCount; frame++) { // Channel 1 ((byte *)area1.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 0]; // Channel 2 ((byte *)area2.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 1]; // Channel 3 ((byte *)area3.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 2]; // Channel 4 ((byte *)area4.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 3]; // Channel 5 ((byte *)area5.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 4]; // Channel 6 ((byte *)area6.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 5]; area1.Pointer += area1.Step; area2.Pointer += area2.Step; area3.Pointer += area3.Step; area4.Pointer += area4.Step; area5.Pointer += area5.Step; area6.Pointer += area6.Step; } } else if (bytesPerSample == 2) { for (int frame = 0; frame < frameCount; frame++) { // Channel 1 ((short *)area1.Pointer)[0] = ((short *)srcptr)[(frame * bytesPerFrame >> 1) + 0]; // Channel 2 ((short *)area2.Pointer)[0] = ((short *)srcptr)[(frame * bytesPerFrame >> 1) + 1]; // Channel 3 ((short *)area3.Pointer)[0] = ((short *)srcptr)[(frame * bytesPerFrame >> 1) + 2]; // Channel 4 ((short *)area4.Pointer)[0] = ((short *)srcptr)[(frame * bytesPerFrame >> 1) + 3]; // Channel 5 ((short *)area5.Pointer)[0] = ((short *)srcptr)[(frame * bytesPerFrame >> 1) + 4]; // Channel 6 ((short *)area6.Pointer)[0] = ((short *)srcptr)[(frame * bytesPerFrame >> 1) + 5]; area1.Pointer += area1.Step; area2.Pointer += area2.Step; area3.Pointer += area3.Step; area4.Pointer += area4.Step; area5.Pointer += area5.Step; area6.Pointer += area6.Step; } } else if (bytesPerSample == 4) { for (int frame = 0; frame < frameCount; frame++) { // Channel 1 ((int *)area1.Pointer)[0] = ((int *)srcptr)[(frame * bytesPerFrame >> 2) + 0]; // Channel 2 ((int *)area2.Pointer)[0] = ((int *)srcptr)[(frame * bytesPerFrame >> 2) + 1]; // Channel 3 ((int *)area3.Pointer)[0] = ((int *)srcptr)[(frame * bytesPerFrame >> 2) + 2]; // Channel 4 ((int *)area4.Pointer)[0] = ((int *)srcptr)[(frame * bytesPerFrame >> 2) + 3]; // Channel 5 ((int *)area5.Pointer)[0] = ((int *)srcptr)[(frame * bytesPerFrame >> 2) + 4]; // Channel 6 ((int *)area6.Pointer)[0] = ((int *)srcptr)[(frame * bytesPerFrame >> 2) + 5]; area1.Pointer += area1.Step; area2.Pointer += area2.Step; area3.Pointer += area3.Step; area4.Pointer += area4.Step; area5.Pointer += area5.Step; area6.Pointer += area6.Step; } } else { for (int frame = 0; frame < frameCount; frame++) { // Channel 1 Unsafe.CopyBlockUnaligned((byte *)area1.Pointer, srcptr + (frame * bytesPerFrame) + (0 * bytesPerSample), bytesPerSample); // Channel 2 Unsafe.CopyBlockUnaligned((byte *)area2.Pointer, srcptr + (frame * bytesPerFrame) + (1 * bytesPerSample), bytesPerSample); // Channel 3 Unsafe.CopyBlockUnaligned((byte *)area3.Pointer, srcptr + (frame * bytesPerFrame) + (2 * bytesPerSample), bytesPerSample); // Channel 4 Unsafe.CopyBlockUnaligned((byte *)area4.Pointer, srcptr + (frame * bytesPerFrame) + (3 * bytesPerSample), bytesPerSample); // Channel 5 Unsafe.CopyBlockUnaligned((byte *)area5.Pointer, srcptr + (frame * bytesPerFrame) + (4 * bytesPerSample), bytesPerSample); // Channel 6 Unsafe.CopyBlockUnaligned((byte *)area6.Pointer, srcptr + (frame * bytesPerFrame) + (5 * bytesPerSample), bytesPerSample); area1.Pointer += area1.Step; area2.Pointer += area2.Step; area3.Pointer += area3.Step; area4.Pointer += area4.Step; area5.Pointer += area5.Step; area6.Pointer += area6.Step; } } } } // Every other channel count else { SoundIOChannelArea[] channels = new SoundIOChannelArea[channelCount]; // Obtain the channel area for each channel for (int i = 0; i < channelCount; i++) { channels[i] = areas.GetArea(i); } fixed(byte *srcptr = samples) { for (int frame = 0; frame < frameCount; frame++) { for (int channel = 0; channel < areas.ChannelCount; channel++) { // Copy channel by channel, frame by frame. This is slow! Unsafe.CopyBlockUnaligned((byte *)channels[channel].Pointer, srcptr + (frame * bytesPerFrame) + (channel * bytesPerSample), bytesPerSample); channels[channel].Pointer += channels[channel].Step; } } } } _outputStream.EndWrite(); ulong sampleCount = (ulong)(samples.Length / bytesPerSample / channelCount); ulong availaibleSampleCount = sampleCount; bool needUpdate = false; while (availaibleSampleCount > 0 && _queuedBuffers.TryPeek(out SoundIoAudioBuffer driverBuffer)) { ulong sampleStillNeeded = driverBuffer.SampleCount - Interlocked.Read(ref driverBuffer.SamplePlayed); ulong playedAudioBufferSampleCount = Math.Min(sampleStillNeeded, availaibleSampleCount); Interlocked.Add(ref driverBuffer.SamplePlayed, playedAudioBufferSampleCount); availaibleSampleCount -= playedAudioBufferSampleCount; if (Interlocked.Read(ref driverBuffer.SamplePlayed) == driverBuffer.SampleCount) { _queuedBuffers.TryDequeue(out _); needUpdate = true; } Interlocked.Add(ref _playedSampleCount, playedAudioBufferSampleCount); } // Notify the output if needed. if (needUpdate) { _updateRequiredEvent.Set(); } }
void WriteCallback(SoundIOOutStream outStream, int min, int max) { if (!play_audio) { return; } Console.WriteLine($"WriteCallback invoked: {min} / {max}"); double float_sample_rate = outStream.SampleRate; double seconds_per_frame = 1.0 / float_sample_rate; //if (max - min > samples.Length) // samples = new short [max - min]; #if test_with_sine //double seconds_offset = 0; #else synth.GetSamples(samples, 0, samples.Length); #endif //if (samples.Any (s => s != 0)) // Console.WriteLine (string.Concat (samples.Take (50).Select (s => s.ToString ("X04")))); int frameRemaining = max; int frameCount = frameRemaining; while (play_audio && frameRemaining > 0) { var results = outStream.BeginWrite(ref frameCount); if (frameCount == 0) { break; } #if test_with_sine double pitch = 440.0; double radians_per_second = pitch * 2.0 * Math.PI; for (int frame = 0; frame < frameCount; frame += 1) { double sample = Math.Sin((seconds_offset + frame * seconds_per_frame) * radians_per_second); for (int channel = 0; channel < outStream.Layout.ChannelCount; channel += 1) { var area = results.GetArea(channel); double range = (double)short.MaxValue - (double)short.MinValue; double val = sample * range / 2.0; unsafe { *((short *)area.Pointer) = (short)val; } area.Pointer += area.Step; } } seconds_offset = Math.IEEERemainder(seconds_offset + seconds_per_frame * frameCount, 1.0); #else for (int i = 0; i < outStream.Layout.ChannelCount; i++) { var area = results.GetArea(i); unsafe { Marshal.Copy(samples, 0, area.Pointer, samples.Length); area.Pointer += area.Step * samples.Length; } } #endif outStream.EndWrite(); frameRemaining -= frameCount; } }
public static void WriteCallback(SoundIOOutStream stream, int frameCountMin, int frameCountMax) { SoundIoError err; DateTime callbackTime = DateTime.Now; // loop to utilize as much as the frameCountMax as possible while (frameCountMax > 0) { int frameCount = frameCountMax; if ((err = stream.BeginWrite(out SoundIOChannelAreas areas, ref frameCount)) != SoundIoError.None) { Console.WriteLine("unrecoverable stream error: {0}", err.GetErrorMessage()); _fileDone = true; } if (frameCount == 0) { break; } var bufferCount = frameCount * stream.Layout.ChannelCount; // if buffer is done add silence based on latency to allow stream to complete through // audio path before stream is Disposed() if (_waveFile.Position >= _waveFile.Length) { if (_startSilence) { // windows latency is a little higher (using DateTime to determine the callback time delay) // and needs to be accoutned for... _latencySeconds -= (DateTime.Now - callbackTime).TotalMilliseconds / 1000.0; _silentSamplesRemaining = (int)((stream.SampleRate * stream.Layout.ChannelCount) * _latencySeconds); _silentSamplesRemaining -= _silentSamplesAlreadySent; _startSilence = false; } int silentBufferSize; if (_silentSamplesRemaining > bufferCount) { silentBufferSize = bufferCount; _silentSamplesRemaining -= bufferCount; } else { silentBufferSize = _silentSamplesRemaining; _silentSamplesRemaining = 0; } if (silentBufferSize > 0) { // create a new buffer initialized to 0 and copy to native buffer var silenceBuffer = new float[silentBufferSize]; stream.CopyTo(silenceBuffer, 0, areas, silentBufferSize); } if (_silentSamplesRemaining == 0) { _fileDone = true; stream.EndWrite(); stream.Pause(true); return; } // if the remaining audioBuffer will only partially fill the frameCount // copy the remaining amount and set the startSilence flag to allow // stream to play to the end. } else if (_waveFile.Position + (frameCount * _waveFile.WaveFormat.Channels) >= _waveFile.Length) { float[] audioBuffer = new float[bufferCount]; var actualSamplesRead = _sampleProvider.Read(audioBuffer, 0, bufferCount); stream.CopyTo(audioBuffer, 0, areas, bufferCount); _silentSamplesAlreadySent = bufferCount - actualSamplesRead; _latencySeconds = 0.0; _startSilence = true; } else { // copy audioBuffer data to native buffer and advance the bufferPos float[] audioBuffer = new float[bufferCount]; var actualSamplesRead = _sampleProvider.Read(audioBuffer, 0, bufferCount); stream.CopyTo(audioBuffer, 0, areas, actualSamplesRead); if (_waveFile.Position >= _waveFile.Length) { _latencySeconds = 0.0; _startSilence = true; } } if ((err = stream.EndWrite()) != SoundIoError.None) { if (err == SoundIoError.Underflow) { return; } Console.WriteLine("Unrecoverable stream error: {0}", err.GetErrorMessage()); _fileDone = true; } if (_startSilence) { // get actual latency in order to compute number of silent frames stream.GetLatency(out _latencySeconds); callbackTime = DateTime.Now; } // loop until frameCountMax is used up frameCountMax -= frameCount; } return; }
static void write_callback(SoundIOOutStream outstream, int frame_count_min, int frame_count_max) { SoundIOChannelAreas areas = default(SoundIOChannelAreas); int frames_left = 0; int frame_count = 0; var read_ptr = ring_buffer.ReadPointer; int fill_bytes = ring_buffer.FillCount; int fill_count = fill_bytes / outstream.BytesPerFrame; if (frame_count_min > fill_count) { // Ring buffer does not have enough data, fill with zeroes. frames_left = frame_count_min; for (; ;) { frame_count = frames_left; if (frame_count <= 0) { return; } areas = outstream.BeginWrite(ref frame_count); if (frame_count <= 0) { return; } var chCount = outstream.Layout.ChannelCount; for (int frame = 0; frame < frame_count; frame += 1) { for (int ch = 0; ch < chCount; ch += 1) { var area = areas.GetArea(ch); // FIXME: there should be more efficient way for memset(ptr, 0); for (int i = 0; i < outstream.BytesPerSample; i++) { Marshal.WriteByte(area.Pointer, 0); } area.Pointer += area.Step; } } outstream.EndWrite(); frames_left -= frame_count; } } int read_count = Math.Min(frame_count_max, fill_count); frames_left = read_count; while (frames_left > 0) { frame_count = frames_left; areas = outstream.BeginWrite(ref frame_count); if (frame_count <= 0) { break; } var chCount = outstream.Layout.ChannelCount; var copySize = outstream.BytesPerSample; for (int frame = 0; frame < frame_count; frame += 1) { unsafe { for (int ch = 0; ch < chCount; ch += 1) { var area = areas.GetArea(ch); Buffer.MemoryCopy((void *)read_ptr, (void *)area.Pointer, copySize, copySize); area.Pointer += area.Step; read_ptr += outstream.BytesPerSample; } } } outstream.EndWrite(); frames_left -= frame_count; } ring_buffer.AdvanceReadPointer(read_count * outstream.BytesPerFrame); }