Sounddevice ValueError:无法将输入数组从形状(2048)广播到形状(2048,1)

时间:2018-12-14 21:12:46

标签: python numpy audio python-sounddevice

我知道这似乎可能是ValueError的另一篇文章,但是请听我说一下,并了解我已经尝试使用Google搜索并浏览StackOverflow社区,以寻求针对我的问题的可行解决方案。

我目前正在尝试将解调后的样本传递给 sounddevice 模块,并使用其 callback 函数实时回放它。

错误:

  

audio_callback中的文件“ rtl-fm-cont.py”,第120行       outdata [:] =数据ValueError:无法将输入数组从形状(2048)广播到形状(2048,1)

我在下面附加了此代码的完整输出:

import numpy as np

#pythran export _mergesorted_1(float[:], int[:], int[:], int, bool)
#pythran export _mergesorted_1(int[:], int[:], int[:], int, bool)

def _mergesorted_1(DB, RB, SB, n, countequal):
    N = len(DB)
    K = ((N-n-1)>>1)<<1
    for i in range(0, K, 2):
        if DB[i] < DB[i+1] or (countequal and DB[i] == DB[i+1]):
            SB[i] = i
            SB[i+1] = i+1
            RB[i+1] += 1
        else:
            SB[i] = i+1
            SB[i+1] = i
        if DB[i+1] < DB[i+1+n] or (countequal and DB[i+1] == DB[i+1+n]):
            RB[i+1+n] += 1
    for i in range(K, (N>>1)<<1, 2):
        if DB[i] < DB[i+1] or (countequal and DB[i] == DB[i+1]):
            SB[i] = i
            SB[i+1] = i+1
            RB[i+1] += 1
        else:
            SB[i] = i+1
            SB[i+1] = i
    if N & 1:
        SB[N-1] = N-1

输出:

#!/usr/bin/env python

# library imports ...

parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('-d', '--device', type=int_or_str,
                    help='output device (numeric ID or substring)')
parser.add_argument('-b', '--blocksize', type=int, default=2048,
                    help='block size (default: %(default)s)')
parser.add_argument(
    '-q', '--buffersize', type=int, default=20,
    help='number of blocks used for buffering (default: %(default)s)')
args = parser.parse_args()
if args.blocksize == 0:
    parser.error('blocksize must not be zero')
if args.buffersize < 1:
    parser.error('buffersize must be at least 1')

q = queue.Queue(maxsize=args.buffersize)
event = threading.Event()

device_index = RtlSdr.get_device_index_by_serial('00000001')

class fmDemodulator(object):
    # Called for each updates
    def __init__(self, sdr=None):
        self.sdr = sdr if sdr else RtlSdr(device_index)

    def Demod(self, *args):
        Fs = self.sdr.sample_rate
        # Fc = self.sdr.center_freq

        # Read IQ samples
        samples = self.sdr.read_samples(4*12*2048)
        print ('Fetching {} IQ samples from SDR #{}'.format(len(samples), device_index))
        # Convert sampled data into numpy array
        x1 = np.array(samples).astype("complex64")

        # Downmixed Baseband Signal (Adjust offset to be centered)
        offsetFreq = 0    # already centered
        fc1 = np.exp(-1.0j*2.0*np.pi* offsetFreq/Fs*np.arange(len(x1)))  
        x2 = x1 * fc1  

        # Filter and downsample the FM Radio Signal
        bwFM = 200000   # approx. 170 kHz for a single channel
        decRate = int(Fs/bwFM)
        x3 = signal.decimate(x2, decRate)
        newFs = Fs/decRate

        ### Demodulate 200kHz FM Signal
        # Polar discriminator
        y4 = x3[1:] * np.conj(x3[:-1])  
        x4 = np.angle(y4)  

        # The de-emphasis filter
        # Given a signal 'x4' (in a numpy array) with sampling rate newFS
        d = newFs * 75e-6   # Calculate the # of samples to hit the -3dB point  
        x = np.exp(-1/d)   # Calculate the decay between each sample  
        b = [1-x]          # Create the filter coefficients  
        a = [1,-x]  
        x5 = signal.lfilter(b,a,x4)  

        # Find a decimation rate to achieve audio sampling rate between 44-48 kHz
        audioFreq = 44100 
        dec_audio = int(newFs/audioFreq)  
        audioFs = newFs/dec_audio
        x6 = signal.decimate(x5, dec_audio) 

        # Scale audio to adjust volume
        x6 *= 10000 / np.max(np.abs(x6))  

        # debug
        print ('decRate: {}, newFs : {}, dec_audio: {}'.format(decRate, newFs, dec_audio))
        print ('Output audio: {} samples, audioFreq: {}, audioFs: {}'.format(len(x6), audioFreq, audioFs))

        return x6

# https://python-sounddevice.readthedocs.io/en/0.3.6/examples.html
def audio_callback(outdata, frames, time, status):
    """This is called (from a separate thread) for each audio block."""
    assert frames == args.blocksize
    if status.output_underflow:
        print('Output underflow: increase blocksize?', file=sys.stderr)
        raise sd.CallbackAbort
    assert not status
    try:
        data = q.get_nowait()
        print(data)
        print(data.dtype)
    except queue.Empty:
        print('Buffer is empty: increase buffersize?', file=sys.stderr)
        raise sd.CallbackAbort
    if len(data) < len(outdata):
        outdata[:len(data)] = data
        outdata[len(data):] = b'\x00' * (len(outdata) - len(data))
        raise sd.CallbackStop
    else:
        outdata[:] = data

def main():
    sdr = RtlSdr(device_index)
    fm = fmDemodulator(sdr)

    # SDR Configurations
    sdr.sample_rate = int(2.4e6)        # Hz
    sdr.center_freq = int(102e6)        # Hz
    sdr.freq_correction = 77            # PPM +- 20
    sdr.gain = 'auto'

    samplerate = 50000
    channels = 1
    try:
        for _ in range(args.buffersize):
            data = fm.Demod()
            if not np.any(data):
                break
            q.put_nowait(data) # pre-fill queue

        stream = sd.OutputStream(
            samplerate=samplerate, blocksize=args.blocksize,
            device=args.device, channels=channels, dtype='int16',
            callback=audio_callback, finished_callback=event.set)
        with stream:
            timeout = args.blocksize * args.buffersize / samplerate
            while np.any(data): # while data
                data = fm.Demod()
                q.put(data, timeout=timeout)
            event.wait()  # Wait until playback is finished
    except KeyboardInterrupt:
        parser.exit('\nInterrupted by user')
    except queue.Full:
        # A timeout occured, i.e. there was an error in the callback
        parser.exit(1)
    except Exception as e:
        parser.exit(type(e).__name__ + ': ' + str(e))

我们将非常感谢您的帮助。 预先谢谢你。

1 个答案:

答案 0 :(得分:0)

看起来很简单:

outdata[:] = data.reshape(2048,1)

numpy.reshape

解决了问题。