Python Forum

Full Version: Python 2 to Python 3 Struct Issue
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I'm trying to get the following code to work in Python 3. It is fine in Python 2. I have changed the xranges to range, but there is a problem with this line: data = ''.join(struct.pack('f', samp) for samp in tone):

Error:
sequence item 0: expected str instance, bytes found.
I found this answer but couldn't work out how to apply it to my situation. Any help much appreciated.
    import math
    import struct
    import pyaudio

    def play_tone(frequency, amplitude, duration, fs, stream):
        N = int(fs / frequency)
        T = int(frequency * duration)  # repeat for T cycles
        dt = 1.0 / fs
        # 1 cycle
        tone = (amplitude * math.sin(2 * math.pi * frequency * n * dt)
                for n in xrange(N))
        # todo: get the format from the stream; this assumes Float32
        data = ''.join(struct.pack('f', samp) for samp in tone)
        for n in xrange(T):
            stream.write(data)

    fs = 48000
    p = pyaudio.PyAudio()
    stream = p.open(
        format=pyaudio.paFloat32,
        channels=1,
        rate=fs,
        output=True)

    # play the C major scale
    scale = [130.8, 146.8, 164.8, 174.6, 195.0, 220.0, 246.9, 261.6]
    for tone in scale:
        play_tone(tone, 0.5, 0.75, fs, stream)

    # up an octave
    for tone in scale[1:]:
        play_tone(2*tone, 0.5, 0.75, fs, stream)

    stream.close()
    p.terminate()
This answer might be more helpful to you.
what is data supposed to look like? (a small sample)
You have the answer in the link of stack overflow...
The problem is that struct returns a string in python2 and a bytes array in python3. So when str.join tries to concatenate the bytes complains about it.
The solution is just transform the string operations to bytes operations, and change the line 13 of your script for:
# Notice the b to transform the operation in a bytes operation
data = b''.join(struct.pack('f', samp) for samp in tone)
You could have saved yourself some data manipulations if you used array
import array
data = array.array('f', tone).tostring()

And instead of
    for n in xrange(T):
        stream.write(data)
just
    stream.write(data * T)
Great, thanks everyone. This is now working a treat.
One little detail that would be icing on the cake - why does the very last note of any sequence I give this code clip in an unpleasant way? Is there a (simple) way to smooth this?