需要时准备声音数据
Preparing Sound Data when needed
我正在使用 libPd 生成声音。 LibPd 可以处理预定义数量的 Ticks,然后用生成的输出填充 float[]
。然后,在转换为 byte[]
后,我使用 BufferedWaveProvider
来存储 float[]
。
音频的生成可以非常快,因此可以在很短的时间内计算出 1 秒的声音,但也可能很慢,具体取决于 Pd 补丁。当 BufferedWaveProvider
的剩余数据量少于预定数量时,有没有办法触发处理数据?
目前我正在后台线程中生成音频,只是睡了一会儿,希望这就足够了,希望 BufferedWaveProvider
不会溢出,即使溢出,然后丢弃该数据。
public void ProcessData(float[] output, int ticks)
{
while (LibPD.Process(ticks, new float[0], output) == 0)
{
if (BufferReady != null)
{
BufferReady(this, new BufferReadyEventArgs(output));
}
Thread.Sleep(999*BlockSize * ticks / 44100);
}
}
public void StartProcessing(float[] output)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(SetPdOutput), output);
}
private void SetPdOutput(object state)
{
float[] output = state as float[];
if (state == null)
{
return;
}
ProcessData(output, Ticks);
}
虽然我将 libPd 和 NAudio 粘合在一起的代码是这样的:
Buffer = new float[2 * _player.Ticks * _player.BlockSize]; // stereo, number of Ticks
_soundOutput = new WasapiOut(AudioClientShareMode.Shared, 100);
_audioBuffer = new BufferedWaveProvider(WaveFormat.CreateIeeeFloatWaveFormat(44100, 2))
{
BufferDuration = TimeSpan.FromSeconds(10),
DiscardOnBufferOverflow = true
};
_soundOutput.Init(_audioBuffer);
_soundOutput.Play();
_player.BufferReady += ((sender, eventArgs) =>
{
_audioBuffer.AddSamples(PcmFromFloat(output), 0, output.Length * 4);
});
_player.StartProcessing(Buffer);
阅读 NAudio 的源代码后,我想出了一个不同的解决方案:编写自定义 IWaveProvider
,它使用类似于 BufferedWaveProvider
的 CircularBuffer
,但请求新处理低于阈值时来自 lidPd 的音频。
这是操作的要点:
class PdProvider : IWaveProvider
{
readonly CircularBuffer _circularBuffer;
public PdProvider()
{
_buffer = new float[BufferSize];
_player.BufferReady += PdBufferReady;
_circularBuffer = new CircularBuffer(SampleRate * 5); // 5 seconds should be enough for anybody
_minBuffer = SampleRate / 2; // 0.5 second
RefillBuffer();
}
void RefillBuffer()
{
if (_circularBuffer.Count < _minBuffer)
{
// Get data from libPd and add to _circularBuffer
}
}
public int Read(byte[] buffer, int offset, int count)
{
var read = _circularBuffer.Read(buffer, offset, count);
RefillBuffer();
return read;
}
public WaveFormat WaveFormat
{
get
{
return WaveFormat.CreateIeeeFloatWaveFormat(_player.SampleRate, 2);
}
}
}
我正在使用 libPd 生成声音。 LibPd 可以处理预定义数量的 Ticks,然后用生成的输出填充 float[]
。然后,在转换为 byte[]
后,我使用 BufferedWaveProvider
来存储 float[]
。
音频的生成可以非常快,因此可以在很短的时间内计算出 1 秒的声音,但也可能很慢,具体取决于 Pd 补丁。当 BufferedWaveProvider
的剩余数据量少于预定数量时,有没有办法触发处理数据?
目前我正在后台线程中生成音频,只是睡了一会儿,希望这就足够了,希望 BufferedWaveProvider
不会溢出,即使溢出,然后丢弃该数据。
public void ProcessData(float[] output, int ticks)
{
while (LibPD.Process(ticks, new float[0], output) == 0)
{
if (BufferReady != null)
{
BufferReady(this, new BufferReadyEventArgs(output));
}
Thread.Sleep(999*BlockSize * ticks / 44100);
}
}
public void StartProcessing(float[] output)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(SetPdOutput), output);
}
private void SetPdOutput(object state)
{
float[] output = state as float[];
if (state == null)
{
return;
}
ProcessData(output, Ticks);
}
虽然我将 libPd 和 NAudio 粘合在一起的代码是这样的:
Buffer = new float[2 * _player.Ticks * _player.BlockSize]; // stereo, number of Ticks
_soundOutput = new WasapiOut(AudioClientShareMode.Shared, 100);
_audioBuffer = new BufferedWaveProvider(WaveFormat.CreateIeeeFloatWaveFormat(44100, 2))
{
BufferDuration = TimeSpan.FromSeconds(10),
DiscardOnBufferOverflow = true
};
_soundOutput.Init(_audioBuffer);
_soundOutput.Play();
_player.BufferReady += ((sender, eventArgs) =>
{
_audioBuffer.AddSamples(PcmFromFloat(output), 0, output.Length * 4);
});
_player.StartProcessing(Buffer);
阅读 NAudio 的源代码后,我想出了一个不同的解决方案:编写自定义 IWaveProvider
,它使用类似于 BufferedWaveProvider
的 CircularBuffer
,但请求新处理低于阈值时来自 lidPd 的音频。
这是操作的要点:
class PdProvider : IWaveProvider
{
readonly CircularBuffer _circularBuffer;
public PdProvider()
{
_buffer = new float[BufferSize];
_player.BufferReady += PdBufferReady;
_circularBuffer = new CircularBuffer(SampleRate * 5); // 5 seconds should be enough for anybody
_minBuffer = SampleRate / 2; // 0.5 second
RefillBuffer();
}
void RefillBuffer()
{
if (_circularBuffer.Count < _minBuffer)
{
// Get data from libPd and add to _circularBuffer
}
}
public int Read(byte[] buffer, int offset, int count)
{
var read = _circularBuffer.Read(buffer, offset, count);
RefillBuffer();
return read;
}
public WaveFormat WaveFormat
{
get
{
return WaveFormat.CreateIeeeFloatWaveFormat(_player.SampleRate, 2);
}
}
}