BandcampFS

(https://github.com/tuxxy/BandcampFS)

BandcampFS is a tool I wrote to accomplish the goal of using Bandcamp as a storage medium. One of the best things about it is that it uses the Python standard library for everything! No external dependencies!!

In a short hour, I wrote BandcampFS to convert any binary file to a WAV file. I selected the WAV format for two reasons:

Bandcamp only allows WAV, AIFF, and FLAC uploads. The WAV format is simple and in the Python standard library.

Furthermore, WAV is a lossless format so any converted data will be exact to the original file. Meaning that if I have a 20MB photo and I convert that to a WAV file, the size will be 20MB + the header which should be less than 1MB anyway.

Let’s dig into Python’s wave module: (https://docs.python.org/3/library/wave.html)

Encoding

import wave with open('test_photo.jpg', 'rb') as f:

data = f.read() wav_file = wave.open('test.wav', 'wb')

wav_file.setnchannels(1)

wav_file.setsampwidth(2)

wav_file.setframerate(44100) wav_file.writeframes(data)

wav_file.close()

The above code opens a photo called ‘test_photo.jpg’ and reads the data from it. Then it opens a new file called ‘test.wav’ and allows us to write data to it.

wav_file.setnchannels(1)

This is setting how many channels in the audio where 1 would be mono, and 2 would be stereo. We only need one channel for our use.

wav_file.setsampwidth(2)

In the WAV format, we have sound data encoded into frames. Here, we’re simply declaring that we will have two bytes per-frame. This matches with our number of channels (one). For PCM encoding with mono, it should be set to 2 or 16-bit.

I learned that if you don’t have this set properly, Bandcamp will think the file is invalid due to the faulty encoding.

wav_file.setframerate(44100)

This is simply the frame rate of the track which is how many frames we’re playing a second. Typically, this sample rate is set to 44100.

wav_file.writeframes(data)

This will write our photo data into the frames of the WAV file with the correct number of ‘nframes’. Compare this to the `writeframesraw` function in the same module.

Decoding

import wave wav_file = wave.open('test.wav', 'wb') num_frames = wav_file.getnframes()

data = wav_file.readframes(num_frames)

wav_file.close() with open('new_test.jpg', 'wb') as f:

f.write(data)

This code decodes the ‘test.wav’ file from above. Remember this is a photo encoded as sound. We then open up a new file ‘new_test.jpg’ and put the raw data in it.

num_frames = wav_file.getnframes()

All this does is tell us how many frames there are in the file. If you want to sanity check this, it should be equal to half the original unencoded file size. If it was a 10 byte file, it should be 5 frames.

This is because we set the sample width when we encoded it to two bytes per-frame.

data = wav_file.readframes(num_frames)

This reads the data from the number of frames you provide. This is our raw data from the original unencoded file.