mirror of
https://github.com/janishutz/midi-micro-bit_sound-converter.git
synced 2025-11-25 05:44:24 +00:00
Finished Version 1.0. Guide for installation will be available soon
This commit is contained in:
122
backend/csv_parsers.py
Normal file
122
backend/csv_parsers.py
Normal file
@@ -0,0 +1,122 @@
|
||||
"""@package docstring
|
||||
This is a simplification of the csv module"""
|
||||
|
||||
import csv
|
||||
|
||||
|
||||
class CsvRead:
|
||||
"""This is a class that reads csv files and depending on the module selected does do different things with it"""
|
||||
def __init__(self):
|
||||
self.__imp = ""
|
||||
self.__raw = ""
|
||||
self.__raw_list = ""
|
||||
|
||||
def importing(self, path):
|
||||
"""Returns a list of the imported csv-file, requires path, either direct system path or relative path"""
|
||||
self.__imp = open(path)
|
||||
self.__raw = csv.reader(self.__imp, delimiter=',')
|
||||
self.__raw_list = list(self.__raw)
|
||||
self.__imp.close()
|
||||
return self.__raw_list
|
||||
|
||||
|
||||
class CsvWrite:
|
||||
"""This is a class that modifies csv files"""
|
||||
def __init__(self):
|
||||
self.__impl = []
|
||||
self.__strpop = []
|
||||
self.__removed = []
|
||||
self.__removing = 0
|
||||
self.__change = 0
|
||||
self.__appending = 0
|
||||
self.__imp = []
|
||||
self.__raw = []
|
||||
|
||||
def rem_str(self, path, row):
|
||||
"""Opens the csv-file in write mode which is specified as an argument either as direct or relative path"""
|
||||
self.__imp = open(path)
|
||||
self.__raw = csv.reader(self.__imp, delimiter=',')
|
||||
self.__impl = list(self.__raw)
|
||||
self.__removed = self.__impl.pop(row + 1)
|
||||
with open(path, "w") as removedata:
|
||||
self.__removing = csv.writer(removedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
|
||||
self.__removing.writerow(self.__impl.pop(0))
|
||||
while len(self.__impl) > 0:
|
||||
with open(path, "a") as removedata:
|
||||
self.__removing = csv.writer(removedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
|
||||
self.__removing.writerow(self.__impl.pop(0))
|
||||
self.__imp.close()
|
||||
removedata.close()
|
||||
|
||||
|
||||
def chg_str(self, path, row, pos, new_value):
|
||||
"""Opens the csv-file in write mode to change a value, e.g. if a recipes is changed."""
|
||||
self.__imp = open(path)
|
||||
self.__raw = csv.reader(self.__imp, delimiter=',')
|
||||
self.__impl = list(self.__raw)
|
||||
self.__strpop = self.__impl.pop(row)
|
||||
self.__strpop.pop(pos)
|
||||
self.__strpop.insert(pos, new_value)
|
||||
self.__impl.insert(row, self.__strpop)
|
||||
with open(path, "w") as changedata:
|
||||
self.__change = csv.writer(changedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
|
||||
self.__change.writerow(self.__impl.pop(0))
|
||||
while len(self.__impl) > 0:
|
||||
with open(path, "a") as changedata:
|
||||
self.__removing = csv.writer(changedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
|
||||
self.__removing.writerow(self.__impl.pop(0))
|
||||
self.__imp.close()
|
||||
changedata.close()
|
||||
|
||||
def chg_str_rem(self, path, row, pos):
|
||||
"""Opens the csv-file in write mode to change a value, e.g. if a recipes is changed."""
|
||||
self.__imp = open(path)
|
||||
self.__raw = csv.reader(self.__imp, delimiter=',')
|
||||
self.__impl = list(self.__raw)
|
||||
self.__strpop = self.__impl.pop(row)
|
||||
self.__strpop.pop(pos)
|
||||
self.__strpop.pop(pos)
|
||||
self.__impl.insert(row, self.__strpop)
|
||||
with open(path, "w") as changedata:
|
||||
self.__change = csv.writer(changedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
|
||||
self.__change.writerow(self.__impl.pop(0))
|
||||
while len(self.__impl) > 0:
|
||||
with open(path, "a") as changedata:
|
||||
self.__removing = csv.writer(changedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
|
||||
self.__removing.writerow(self.__impl.pop(0))
|
||||
self.__imp.close()
|
||||
changedata.close()
|
||||
|
||||
def chg_str_add(self, path, row, new_value1, new_value2):
|
||||
"""Opens the csv-file in write mode to change a value, e.g. if a recipes is changed."""
|
||||
self.__imp = open(path)
|
||||
self.__raw = csv.reader(self.__imp, delimiter=',')
|
||||
self.__impl = list(self.__raw)
|
||||
self.__strpop = self.__impl.pop(row)
|
||||
self.__strpop.append(new_value1)
|
||||
self.__strpop.append(new_value2)
|
||||
self.__impl.insert(row, self.__strpop)
|
||||
with open(path, "w") as changedata:
|
||||
self.__change = csv.writer(changedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
|
||||
self.__change.writerow(self.__impl.pop(0))
|
||||
while len(self.__impl) > 0:
|
||||
with open(path, "a") as changedata:
|
||||
self.__removing = csv.writer(changedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
|
||||
self.__removing.writerow(self.__impl.pop(0))
|
||||
self.__imp.close()
|
||||
changedata.close()
|
||||
|
||||
def app_str(self, path, value):
|
||||
"""Opens the csv-file in append mode and writes given input. CsvWrite.app_str(path, value).
|
||||
Path can be specified both as direct or relative. value is a list. Will return an error if type of value is
|
||||
not a list."""
|
||||
with open(path, "a") as appenddata:
|
||||
self.__appending = csv.writer(appenddata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
|
||||
self.__appending.writerow(value)
|
||||
appenddata.close()
|
||||
|
||||
def write_str(self, path, value):
|
||||
with open(path, "w") as writedata:
|
||||
self.__change = csv.writer(writedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
|
||||
self.__change.writerow(value)
|
||||
writedata.close()
|
||||
77
backend/midi_management.py
Normal file
77
backend/midi_management.py
Normal file
@@ -0,0 +1,77 @@
|
||||
from mido import MidiFile
|
||||
import pyperclip as pc
|
||||
|
||||
|
||||
class MidiManagement:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def addToClipboard(self, text):
|
||||
pc.copy(text)
|
||||
|
||||
def analyse_track(self, path, trackname):
|
||||
self.midi_imp = MidiFile(path, clip=True)
|
||||
self.tracks = []
|
||||
for self.track in self.midi_imp.tracks:
|
||||
self.tracks.append(str(self.track))
|
||||
self.tracks.pop(0)
|
||||
self.track_ext = self.tracks.pop(0)
|
||||
self.trackn = 0
|
||||
while self.track_ext != trackname:
|
||||
self.track_ext = self.tracks.pop(0)
|
||||
self.trackn += 1
|
||||
self.extracted_track = self.track_ext
|
||||
self.__output_list = []
|
||||
for self.msg in self.midi_imp.tracks[self.trackn]:
|
||||
self.ext = str(self.msg)
|
||||
self.note = self.ext[23:25]
|
||||
try:
|
||||
self.note_height = int(self.note)
|
||||
self.note_decod_oct = self.note_height // 12
|
||||
self.note_decode_tone = self.note_height % 12
|
||||
if self.note_decode_tone == 1:
|
||||
self.note_ext = "C"
|
||||
elif self.note_decode_tone == 2:
|
||||
self.note_ext = "C#"
|
||||
elif self.note_decode_tone == 3:
|
||||
self.note_ext = "D"
|
||||
elif self.note_decode_tone == 4:
|
||||
self.note_ext = "D#"
|
||||
elif self.note_decode_tone == 5:
|
||||
self.note_ext = "E"
|
||||
elif self.note_decode_tone == 6:
|
||||
self.note_ext = "F"
|
||||
elif self.note_decode_tone == 7:
|
||||
self.note_ext = "F#"
|
||||
elif self.note_decode_tone == 8:
|
||||
self.note_ext = "G"
|
||||
elif self.note_decode_tone == 9:
|
||||
self.note_ext = "G#"
|
||||
elif self.note_decode_tone == 10:
|
||||
self.note_ext = "A"
|
||||
elif self.note_decode_tone == 11:
|
||||
self.note_ext = "A#"
|
||||
elif self.note_decode_tone == 12:
|
||||
self.note_ext = "H"
|
||||
|
||||
self.ext_shortened = self.ext[40:]
|
||||
self.pos = 0
|
||||
for buchstabe in self.ext_shortened:
|
||||
if buchstabe == "=":
|
||||
self.pos += 1
|
||||
break
|
||||
else:
|
||||
self.pos += 1
|
||||
|
||||
self.timing_exp = self.ext_shortened[self.pos:]
|
||||
self.__output = self.note_ext
|
||||
self.__output += f":{self.timing_exp}"
|
||||
self.__output_list.append(str(self.__output))
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
self.addToClipboard(str(self.__output_list))
|
||||
|
||||
|
||||
|
||||
1
backend/temp.csv
Normal file
1
backend/temp.csv
Normal file
@@ -0,0 +1 @@
|
||||
/home/janis/Desktop/Victory.mid
|
||||
|
32
dev/hr.py
Normal file
32
dev/hr.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from kivy.lang import Builder
|
||||
|
||||
from kivymd.app import MDApp
|
||||
|
||||
KV = '''
|
||||
#:import KivyLexer kivy.extras.highlight.KivyLexer
|
||||
#:import HotReloadViewer kivymd.utils.hot_reload_viewer.HotReloadViewer
|
||||
|
||||
|
||||
BoxLayout:
|
||||
HotReloadViewer:
|
||||
size_hint_x: .3
|
||||
path: app.path_to_kv_file
|
||||
errors: True
|
||||
errors_text_color: 1, 1, 0, 1
|
||||
errors_background_color: app.theme_cls.bg_dark
|
||||
'''
|
||||
|
||||
|
||||
class Example(MDApp):
|
||||
path_to_kv_file = "../gui/loading_screen.kv"
|
||||
|
||||
def build(self):
|
||||
self.theme_cls.theme_style = "Dark"
|
||||
return Builder.load_string(KV)
|
||||
|
||||
def update_kv_file(self, text):
|
||||
with open(self.path_to_kv_file, "w") as kv_file:
|
||||
kv_file.write(text)
|
||||
|
||||
|
||||
Example().run()
|
||||
22
gui/filechooser.kv
Normal file
22
gui/filechooser.kv
Normal file
@@ -0,0 +1,22 @@
|
||||
FileChooserScreen:
|
||||
name: "ChooseFile"
|
||||
md_bg_color: (0, 0, 0, 1)
|
||||
BoxLayout:
|
||||
size: root.size
|
||||
pos: root.pos
|
||||
orientation: "vertical"
|
||||
FileChooserListView:
|
||||
id: filechooser
|
||||
|
||||
BoxLayout:
|
||||
size_hint_y: None
|
||||
height: 30
|
||||
Button:
|
||||
text: "Cancel"
|
||||
on_release:
|
||||
app.root.current = "Home"
|
||||
root.manager.transition.direction = "right"
|
||||
|
||||
Button:
|
||||
text: "Load"
|
||||
on_release: root.load(filechooser.path, filechooser.selection)
|
||||
20
gui/gui.kv
Normal file
20
gui/gui.kv
Normal file
@@ -0,0 +1,20 @@
|
||||
HomeScreen:
|
||||
name: "Home"
|
||||
md_bg_color: app.theme_cls.accent_color
|
||||
GridLayout:
|
||||
cols:1
|
||||
Label:
|
||||
text: "MIDI to Micro:bit Sound converter"
|
||||
font_size: 30
|
||||
bold: True
|
||||
italic: True
|
||||
color: (50, 50, 255, 1)
|
||||
Label:
|
||||
id: infobox
|
||||
text: "Output will be automatically added to your clipboard once you have selected a file"
|
||||
Button:
|
||||
text: "choose file to start"
|
||||
background_color: app.theme_cls.primary_color
|
||||
on_release:
|
||||
app.root.current = "ChooseFile"
|
||||
root.manager.transition.direction = "left"
|
||||
24
gui/loading_screen.kv
Normal file
24
gui/loading_screen.kv
Normal file
@@ -0,0 +1,24 @@
|
||||
TrackChooseScreen:
|
||||
name: "Track"
|
||||
md_bg_color: app.theme_cls.accent_color
|
||||
GridLayout:
|
||||
cols:1
|
||||
Label:
|
||||
text: "Track selection"
|
||||
font_size: 30
|
||||
bold: True
|
||||
italic: True
|
||||
color: (50, 50, 255, 1)
|
||||
FloatLayout:
|
||||
Spinner:
|
||||
id: track_spinner
|
||||
size_hint: 0.7, 0.2
|
||||
pos_hint: {"x": 0.15, "y":0.5}
|
||||
background_color: (0, 0, 0, 1)
|
||||
text: "Select a track"
|
||||
values: ["Test"]
|
||||
Button:
|
||||
text: "confirm"
|
||||
background_color: app.theme_cls.primary_color
|
||||
on_release:
|
||||
root.extract()
|
||||
80
midi_converter.py
Normal file
80
midi_converter.py
Normal file
@@ -0,0 +1,80 @@
|
||||
from kivy.uix.screenmanager import ScreenManager
|
||||
from kivymd.uix.screen import MDScreen
|
||||
from kivy.uix.popup import Popup
|
||||
from kivy.properties import ObjectProperty
|
||||
from kivy.lang import Builder
|
||||
from kivy.uix.label import Label
|
||||
from kivymd.app import MDApp
|
||||
from mido import MidiFile
|
||||
import os
|
||||
import backend.midi_management
|
||||
import backend.csv_parsers
|
||||
import time
|
||||
|
||||
|
||||
class HomeScreen(MDScreen):
|
||||
pass
|
||||
|
||||
|
||||
class FileChooserScreen(MDScreen):
|
||||
loadfile = ObjectProperty(None)
|
||||
def load(self, path, filename):
|
||||
try:
|
||||
self.path = os.path.join(path, filename[0])
|
||||
try:
|
||||
self.mid = MidiFile(self.path, clip=True)
|
||||
backend.csv_parsers.CsvWrite().write_str("./backend/temp.csv", [self.path])
|
||||
self.tracks = []
|
||||
for self.track in self.mid.tracks:
|
||||
self.tracks.append(str(self.track))
|
||||
self.tracks.pop(0)
|
||||
if len(self.tracks) > 1:
|
||||
screen_manager.get_screen("Track").ids.track_spinner.values = self.tracks
|
||||
screen_manager.current = "Track"
|
||||
screen_manager.transition.direction = "up"
|
||||
else:
|
||||
screen_manager.current = "Home"
|
||||
screen_manager.transition.direction = "right"
|
||||
except:
|
||||
self.popup_fe = Popup(title="FileError", content=Label(text="Please select a MIDI-File!"),
|
||||
size_hint=(0.4, 0.4), auto_dismiss=True)
|
||||
self.popup_fe.open()
|
||||
|
||||
except:
|
||||
self.popup_foldererror = Popup(title="FileError", content=Label(text="Only MIDI-Files allowed, not folder"),
|
||||
size_hint=(0.4, 0.4), auto_dismiss=True)
|
||||
self.popup_foldererror.open()
|
||||
|
||||
|
||||
class TrackChooseScreen(MDScreen):
|
||||
def extract(self):
|
||||
self.chosen_track = self.ids.track_spinner.text
|
||||
if self.chosen_track == "Select a track":
|
||||
self.popup_ns = Popup(title="NoSelectionError", content=Label(text="Please select a Track!"),
|
||||
size_hint=(0.4, 0.4), auto_dismiss=True)
|
||||
self.popup_ns.open()
|
||||
else:
|
||||
self.path = backend.csv_parsers.CsvRead().importing("./backend/temp.csv").pop(0)
|
||||
self.path_transmit = self.path.pop(0)
|
||||
backend.midi_management.MidiManagement().analyse_track(str(self.path_transmit), self.chosen_track)
|
||||
screen_manager.get_screen("Home").ids.infobox.text = "The command has been copied to the clipboard"
|
||||
screen_manager.current = "Home"
|
||||
screen_manager.transition.direction = "right"
|
||||
|
||||
|
||||
|
||||
class MidiConverter(MDApp):
|
||||
global screen_manager
|
||||
screen_manager = ScreenManager()
|
||||
|
||||
def build(self):
|
||||
self.title = "Midi-Microbit-Converter"
|
||||
self.theme_cls.primary_palette = "Blue"
|
||||
self.theme_cls.accent_palette = "BlueGray"
|
||||
screen_manager.add_widget(Builder.load_file("./gui/gui.kv"))
|
||||
screen_manager.add_widget(Builder.load_file("./gui/filechooser.kv"))
|
||||
screen_manager.add_widget(Builder.load_file("./gui/loading_screen.kv"))
|
||||
|
||||
return screen_manager
|
||||
|
||||
MidiConverter().run()
|
||||
Reference in New Issue
Block a user