#!/usr/bin/python # flipnoise.py # # Converts Renoise song files (.xrns) to Flipper Zero song files (.fmf). Pass # in the .xrns file as the only argument and flipnoise will unzip it, parse # the Song.xml file within and generate a .fmf file you can place on your # device with qFlipper. # # flipnoise will read the song name, artist name and BPM from the Renoise file. # Empty lines are filled in with the last note played. The intention here is to # primarily make chiptune/keygen-sounding music at a very high BPM (e.g., 500). # # Note that Flipper Zero only has a single sound channel, so only make one track # in your Renoise song. # # Juan Irming # https://amplitudeproblem.com/ import os from os.path import exists import shutil import sys import uuid import xml.etree.ElementTree as ET import zipfile # ------------------------------------------------------------------------------ # Constants SONG_FILENAME = "Song.xml" RENOISE_MAGIC = "RenoiseSong" APP_NAME = "flipnoise" APP_VERSION = 1.0 # ------------------------------------------------------------------------------ def invalid_file_exit(filename): sys.exit(filename + " is not a valid Renoise song") # ------------------------------------------------------------------------------ def main(): if len(sys.argv) < 2: sys.exit("Usage: " + APP_NAME + " [filename.xrns]") else: filename = sys.argv[1] if not exists(filename): invalid_file_exit(filename) tmp_dir = "." + APP_NAME + "_tmp/" if exists(tmp_dir): sys.exit("Temporary directory " + tmp_dir + " already exists") os.mkdir(tmp_dir) shutil.copy(filename, tmp_dir) with zipfile.ZipFile(tmp_dir + filename, 'r') as zip_handle: zip_handle.extractall(tmp_dir) try: renoise = ET.parse(tmp_dir + SONG_FILENAME) except: invalid_file_exit(filename) print(filename) renoise_root = renoise.getroot() if not renoise_root.tag == RENOISE_MAGIC: invalid_file_exit(filename) song_name = renoise_root.find("./GlobalSongData/SongName").text print("Song name:", song_name) artist = renoise_root.find("./GlobalSongData/Artist").text print("Artist:", artist) bpm = renoise_root.find("./GlobalSongData/BeatsPerMin").text print("BPM:", bpm) octave = renoise_root.find("./GlobalSongData/Octave").text print("Octave:", octave) notes = "Notes: " for pattern in renoise_root.findall("./PatternPool/Patterns/Pattern"): last_index = 0 last_note = "" for line in pattern.findall("Tracks/PatternTrack/Lines/Line"): index = int(line.attrib.get("index")) index_delta = index - last_index if index_delta == 0: index_delta = 1 note = line.find("NoteColumns/NoteColumn/Note").text note = note.replace("-", "") for i in range(0, index_delta - 1): notes += note + ", " notes += note + ", " last_index = index last_note = note notes = notes.rstrip(", ") print(notes) flipper_filename = artist + "-" + song_name + "." + str(uuid.uuid4()) + ".fmf" with open(flipper_filename, "w") as file: file.write("Filetype: Flipper Music Format\n\n") file.write("Version: 0\n\n") file.write("BPM: " + bpm + "\n\n") file.write("Duration: 16\n\n") file.write("Octave: 5\n\n") file.write(notes) print("Saved", flipper_filename) shutil.rmtree(tmp_dir) # ------------------------------------------------------------------------------ if __name__ == "__main__": main()