PyAudio可以处理非交错缓冲区吗?

时间:2016-04-28 22:14:49

标签: python numpy pyaudio

我无法将非交错的立体声数据传递给PyAudio。

更新(重写问题)

PyAudio文档中给出的示例显示了如何使用回调传递 interleaved 数据。交错立体数据的缓冲区具有(nframes, 2)的形状,如下所示:

array([[f0l, f0r],
       [f1l, f1r],
       [f2l, f2r],
       ...], dtype=float32)

部分由于说明原因,我想在回调中使用非交错数据。阅读文档,我认为非交错立体数据的缓冲区的形状为(2, nframes),如下所示:

array([[f0l, f1l, f2l, ...],
       [f0r, f1r, f2r, ...]], dtype=float32)

AFAICT,在非交错模式下设置流的调用应该在格式参数的非交错位中OR,如下所示:

PA_NONINTERLEAVED = 0x80000000
stream = p.open(format=pyaudio.paFloat32 | PA_NONINTERLEAVED,
                channels=DEFAULT_CHANNEL_COUNT,
                rate=SRATE,
                output=True,
                stream_callback=callback)

但是当我试图运行它时,它会使我的耳朵发出声音或者用数字噪音冲击我的耳朵,或者不会播放任何东西 - 某个记忆的所有标志都在某处。

有没有人成功将非交错数据传递给PyAudio?

原始问题(以及完整的代码示例)

以下代码应该向左声道写入440Hz正弦波,向右声道写入442Hz正弦波。

相反,它是段错误。

我已成功使用交错的numpy数据运行类似的测试,但希望我可以将通道数据分开。关于我做错了什么的指示?

# python sketches/s05.py 
"""
PyAudio / NumPy example: synthesize and play stereo sine waves
using non-interleaved buffers
"""

import pyaudio
import numpy as np
import time

p = pyaudio.PyAudio()

SRATE = 44100
DEFAULT_CHANNEL_COUNT = 2
DEFAULT_FRAME_COUNT = 1024
# from http://portaudio.com/docs/v19-doxydocs/portaudio_8h.html
PA_NONINTERLEAVED = 0x80000000

class Sinner:
    def __init__(self, freq, channel):
        self._dtheta = freq * 2.0 * np.pi / SRATE
        self._theta = 0.0
        self._channel = channel

    def render(self, buffer, frame_count):
        thetas = np.arange(frame_count) * self._dtheta + self._theta
        np.sin(thetas, buffer[self._channel])
        self._theta += frame_count * self._dtheta

sin_l = Sinner(440.0, 0)
sin_r = Sinner(442.0, 1)
out_data = np.zeros((DEFAULT_CHANNEL_COUNT, DEFAULT_FRAME_COUNT),
                    dtype=np.float32)

def callback(in_data, frame_count, time_info, status):
    global sin_l, sin_r, out_data

    if (out_data.shape[1] != frame_count):
        # resize numpy array if needed
        out_data = np.zeros((DEFAULT_CHANNEL_COUNT, frame_count),
                            dtype=np.float32)
    sin_l.render(out_data, frame_count)
    sin_r.render(out_data, frame_count)

    print(out_data[0])
    print(out_data[1])
    return (out_data * 0.4, pyaudio.paContinue)

stream = p.open(format=pyaudio.paFloat32 | PA_NONINTERLEAVED,
                channels=DEFAULT_CHANNEL_COUNT,
                rate=SRATE,
                output=True,
                stream_callback=callback)

stream.start_stream()
while stream.is_active(): time.sleep(0.1)
stream.stop_stream()
stream.close()
p.terminate()

2 个答案:

答案 0 :(得分:0)

我非常怀疑非交错数据的教学优势,听到你原因的另一部分会很有趣。

你知道matplotlib如果你把它传递给一个二维数组那么会绘制列吗?

此外,您似乎忽略了阵列的order设置。如果您假设order='C',那么您在问题中所说的内容才有意义。您应该能够通过numpy.asfortranarray()运行数组然后将缓冲区传递给PyAudio来获得所需的行为(但没有使用PA_NONINTERLEAVED标志!)。 请注意,在许多情况下,这会产生不必要的数据副本。

鉴于您的示例代码,您似乎误解了PortAudio的“非交错”模式的工作原理。在这种模式下,PortAudio假定输入和输出数据是指向一堆其他数组的指针的C数组,每个数组都保存一个通道的数据。为了能够使用它,您需要获取指向NumPy数组的每一行的指针。我不知道这在纯Python中是否可行......

最后,一些广告:您应该考虑使用sounddevice模块而不是PyAudio。它仍然使用交错数据,并且通道存储在列中,但它直接支持NumPy数组,并且应该比PyAudio更容易安装和使用。

答案 1 :(得分:0)

不,至少还在 0.2.11 版本中。录制某些内容时,此回调代码仅允许在一个通道上进行投影,在示例中为第一个通道。诀窍是使用 NumPy 并将数据排列成一个维度为 frame x channel x data (= SAMPLE_WIDTH) 的数组(立方体),然后投影到第一个通道 (0) 上:


# 16 bits sample width
SAMPLE_WIDTH = 2


def __audio_callback__(in_data, frame_count, time_info, status_flags):
    channels = int(len(in_data) / (SAMPLE_WIDTH * frame_count))
    buffer = np.frombuffer(in_data, dtype=uint8)
    first_channel = buffer.reshape(frame_count, channels, SAMPLE_WIDTH)[:, 0, :].tobytes()
    audio_samples.append(first_channel)
    # when not recording, return here the processed data as first tuple part
    return None, pyaudio.paContinue