python-soundfile icon indicating copy to clipboard operation
python-soundfile copied to clipboard

Read/write file from memory

Open onslauth opened this issue 3 years ago • 3 comments

I am trying to record some content, and instead of writing to disk, instead use a memory file.

So far I have the following, but I am getting an error with sf.read.

import soundfile   as sf
import sounddevice as sd
import os
import io

fs = 44100
seconds = 5

print( "recording..." )
recording = sd.rec( int ( seconds * fs ), samplerate = fs, channels = 1 )
sd.wait( )

file_format = "WAV"
memory_file = io.BytesIO( )
sf.write( memory_file, recording, fs, format = file_format )

# This works, and file can be played back
with open( "test.wav", 'wb' ) as f:
	f.write( memory_file.getbuffer( ) )

# This also works
temp_data, temp_sr = sf.read( "test.wav" )

# Playback is correct here too
sd.play( temp_data, temp_sr )
sd.wait( )

# This does not work?
temp_data, temp_sr = sf.read( memory_file )

The error from the final line:

  File "python3.9/site-packages/soundfile.py", line 1357, in _error_check
    raise RuntimeError(prefix + _ffi.string(err_str).decode('utf-8', 'replace'))
RuntimeError: Error opening <_io.BytesIO object at 0x1045b2180>: File contains data in an unknown format.

What would be the correct way to read data from memory?

onslauth avatar Mar 18 '22 10:03 onslauth

Since memory_file does not have a file extension, you need to tell it to interpret it as a WAV file.

bastibe avatar Mar 21 '22 15:03 bastibe

Thank you for the reply.

I have had a bit more time to dig into how SoundFile works, and looked at what it was doing internally. It seems that everything is working, and that it was correctly picking up the memory_file as virtual IO, however libsndfile was throwing the error.

This occurs because the memory_file internal stream has not been reset to the start, so when libsndfile tries to read from the stream, it receives empty bytes and throws the error. Adding a memory_file.seek( 0 ) to the after the write to the memory_file solves the above issue.

Please feel free to close the ticket if needed.

Final working code

import soundfile   as sf
import sounddevice as sd
import os
import io

fs = 44100
seconds = 5

print( "recording..." )
recording = sd.rec( int ( seconds * fs ), samplerate = fs, channels = 1 )
sd.wait( )

file_format = "WAV"
memory_file = io.BytesIO( )
memory_file.name = "test.wav"
sf.write( memory_file, recording, fs, format = file_format )

memory_file.seek( 0 )

# This now works
temp_data, temp_sr = sf.read( memory_file )

sd.play( temp_data, temp_sr )
sd.wait( )

onslauth avatar Mar 26 '22 19:03 onslauth

Thank you for the follow-up! Would you like to contribute that gotcha to the documentation of soundfile's virtual-IO?

bastibe avatar Mar 27 '22 20:03 bastibe

Thank you very much for your issue, which has helped me a lot.

hua-zhi-wan avatar Nov 27 '22 06:11 hua-zhi-wan

Thank you guys! You really helped me to convert OGG to WAV and send it to REST API without disturbing the filesystem!

acriptis avatar Feb 21 '23 18:02 acriptis

import io
import soundfile as sf

def ogg2wav(ogg: bytes):
    ogg_buf = io.BytesIO(ogg)
    ogg_buf.name = 'file.ogg'
    data, samplerate = sf.read(ogg_buf)
    wav_buf = io.BytesIO()
    wav_buf.name = 'file.wav'
    sf.write(wav_buf, data, samplerate)
    wav_buf.seek(0)
    return wav_buf.read()

if someone will search that too... that's how you can convert ogg to wav without touching the disk

thewh1teagle avatar Jul 16 '23 00:07 thewh1teagle

Using file_like.seek(0) after sf.write(file_like, ...) solved the issue of seemingly not being able to write to in-memory files for me, too.


@bastibe Mentioning that this is required in the Virtual IO docs would have been really helpful. The above code snippet would be a sufficient example, I think. Or maybe it could be called automatically by default. 🤷‍♂️

Schöne Grüße 😉

jneuendorf-i4h avatar Sep 22 '23 09:09 jneuendorf-i4h

I'm glad you found a solution! @jneuendorf-i4h would you like to contribute the change as a pull request?

Ebenfalls schöne Grüße!

bastibe avatar Sep 23 '23 09:09 bastibe