diff --git a/Music_Player/flipnoise/README.md b/Music_Player/flipnoise/README.md new file mode 100644 index 00000000..60240b20 --- /dev/null +++ b/Music_Player/flipnoise/README.md @@ -0,0 +1,2 @@ +# flipnoise +Converts Renoise .xrns files to Flipper Zero .fmf music files diff --git a/Music_Player/flipnoise/flipnoise.py b/Music_Player/flipnoise/flipnoise.py new file mode 100644 index 00000000..86928c0f --- /dev/null +++ b/Music_Player/flipnoise/flipnoise.py @@ -0,0 +1,137 @@ +#!/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()