Skip to content

Instantly share code, notes, and snippets.

@sroccaserra
Last active April 7, 2024 13:24
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sroccaserra/0857010d91a5bc7c83d2dac8ff452f58 to your computer and use it in GitHub Desktop.
Save sroccaserra/0857010d91a5bc7c83d2dac8ff452f58 to your computer and use it in GitHub Desktop.
Convert from raw to aiff format
import argparse
"""
Works only for raw 8 bit signed mono data.
This script was writen as an easy way to convert original Amiga ST-XX raw
samples to a more documented format that keeps the original data untouched.
See also:
- http://paulbourke.net/dataformats/audio/
- https://github.com/audacity/audacity/blob/fa00dd0/lib-src/libsndfile/src/aiff.c
- https://archive.org/details/AmigaSTXX
"""
sample_rate_in_extended_precision = {
11025: b'\x40\x0c\xac\x44\x00\x00\x00\x00\x00\x00',
16000: b'\x40\x0c\xfa\x00\x00\x00\x00\x00\x00\x00',
16726: b'\x40\x0d\x82\xac\x00\x00\x00\x00\x00\x00',
22050: b'\x40\x0d\xac\x44\x00\x00\x00\x00\x00\x00',
44100: b'\x40\x0e\xac\x44\x00\x00\x00\x00\x00\x00',
}
DEFAULT_SAMPLE_RATE = 16726
def convert(input_file_name, output_file_name, sample_rate):
sample_data = bytearray(open(input_file_name, 'rb').read())
nb_samples= len(sample_data)
form_chunk_size = 4 + 4 + 4
comm_chunk_size = 4 + 4 + 2 + 4 + 2 + 10
ssnd_chunk_size = 4 + 4 + 4 + 4
total_size = form_chunk_size + comm_chunk_size + ssnd_chunk_size + nb_samples
nb_channels = 1
sample_size = 8
form_chunk = b'FORM' + \
(total_size - 8).to_bytes(4, byteorder='big') + \
b'AIFF'
comm_chunk = b'COMM\x00\x00\x00\x12' + \
nb_channels.to_bytes(2, byteorder='big') + \
(sample_rate*nb_channels).to_bytes(4, byteorder='big') + \
sample_size.to_bytes(2, byteorder='big') + \
sample_rate_in_extended_precision[sample_rate]
ssnd_chunk = b'SSND' + \
(total_size-34).to_bytes(4, byteorder='big') + \
b'\x00\x00\x00\x00' + \
b'\x00\x00\x00\x00'
header = form_chunk + comm_chunk + ssnd_chunk
open(output_file_name, 'wb').write(header+sample_data)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('input_file')
parser.add_argument('-o', '--output-file', help='The name of the output file (default is the name of the input file with ".aiff" extension)')
parser.add_argument('-r', '--sample-rate', help='The sample rate of the source in Hz. The default is {}.'.format(DEFAULT_SAMPLE_RATE), default=DEFAULT_SAMPLE_RATE)
args = parser.parse_args()
input_file_name = args.input_file
output_file_name = args.output_file or input_file_name + '.aiff'
convert(input_file_name, output_file_name, args.sample_rate)
@Stakker
Copy link

Stakker commented Apr 3, 2020

Was looking for a way to convert ST-01 and ST-02 and found this – super helpful, thank you!

@sroccaserra
Copy link
Author

Nice, glad it helps :)

I noticed I have another gist with more info, maybe you saw it already:

And a tool to load the 8SVX samples at correct sample rate in Renoise:

@Stakker
Copy link

Stakker commented Apr 20, 2020

Yeah I did notice the other stuff too (a bit later :-).
Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment