aplaymidi_gui/main.py

153 lines
5.9 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# main.py
#
# Copyright 2021 Patrick Marsee <patrickm@patrick-pc>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
#
from tkinter import *
from tkinter import ttk
from tkinter import filedialog as fd
from tkinter import messagebox as mb
import subprocess as sp
import os
import threading
class MidiPort:
def __init__(self, port, client_name, port_name):
self.port = port
self.client_name = client_name
self.port_name = port_name
def __str__(self):
return "{} {}".format(self.port, self.port_name)
class App(ttk.Frame):
def __init__(self, master):
super(App, self).__init__(master)
self.devices = self.get_midi_list()
self.create_widgets()
self.midi_port_var.set(str(self.devices[0]))
# play control
self.play_mutex = threading.Lock()
self.is_playing = False
self.stop_after = False
self.files = ()
def create_widgets(self):
self.midi_port_label = ttk.Label(self, text="Midi Device")
self.midi_port_label.grid(column=0, row=0)
self.midi_port_var = StringVar()
self.midi_port_selector = ttk.Combobox(self, textvariable=self.midi_port_var)
self.midi_port_selector.state(["readonly"])
self.midi_port_selector['values'] = [str(i) for i in self.devices]
self.midi_port_selector.grid(column=1, row=0, columnspan=2, sticky=(E, W))
self.midi_file_label = ttk.Label(self, text="File")
self.midi_file_label.grid(column=0, row=1)
self.midi_file_var = StringVar()
self.midi_file_text = Listbox(self, listvariable=self.midi_file_var, width=80, height=20, selectmode="extended")
self.midi_file_text.grid(column=1, row=1)
self.midi_file_button = ttk.Button(self,
text="Open...",
command=self.open_file)
self.midi_file_button.grid(column=3, row=1, sticky=(E, W))
self.midi_file_scroll = ttk.Scrollbar(self, orient=VERTICAL, command=self.midi_file_text.yview)
self.midi_file_text.configure(yscrollcommand=self.midi_file_scroll.set)
self.midi_file_scroll.grid(column=2, row=1, sticky=(N, S, W))
self.play_button = ttk.Button(self, text="Play", command=self.play_midi)
self.play_button.grid(column=1, row=2)
self.stop_after_var = StringVar()
self.stop_after_var.set("go")
self.stop_after_button = ttk.Checkbutton(self, text="Stop after current", command=self.stop_after_current, variable=self.stop_after_var, onvalue="stop", offvalue="go")
self.stop_after_button.grid(column=2, row=2, columnspan=2)
self.columnconfigure(1, weight=1)
@staticmethod
def get_midi_list() -> list:
result = sp.run(["aplaymidi", "-l"], stdout=sp.PIPE, encoding="utf-8")
devices = result.stdout.split('\n')
port_start = devices[0].find("Port")
client_name_start = devices[0].find("Client name")
port_name_start = devices[0].find("Port name")
return [MidiPort(i[port_start:client_name_start].strip(), i[client_name_start:port_name_start].strip(), i[port_name_start:].strip()) for i in devices[1:]]
def stop_after_current(self):
self.play_mutex.acquire()
self.stop_after = self.stop_after_var.get() == "stop"
self.play_mutex.release()
def play_midi(self):
self.play_mutex.acquire()
if self.is_playing:
self.play_mutex.release()
mb.showinfo(message="Another song cannot play until the previous song is finished playing.")
else:
self.play_mutex.release()
port = self.devices[self.midi_port_selector.current()].port
midis = [self.files[f] for f in self.midi_file_text.curselection()]
if len(midis) == 0:
midis = self.files
play_thread = threading.Thread(target=self.play_thread, args=(port, midis))
play_thread.start()
def play_thread(self, port: str, midis: list):
self.play_mutex.acquire()
self.is_playing = True
self.play_mutex.release()
for midi in midis:
sp.run(["aplaymidi", "-p", port, midi])
self.play_mutex.acquire()
if self.stop_after:
self.play_mutex.release()
break;
self.play_mutex.release()
self.play_mutex.acquire()
self.is_playing = False
self.play_mutex.release()
def open_file(self):
initial_dir = os.getcwd()
if 'HOME' in os.environ:
initial_dir = os.environ['HOME']
self.files = fd.askopenfilename(defaultextension=".mid", filetypes=[("MIDI", ".mid")], initialdir=initial_dir, multiple=True)
self.midi_file_var.set(self.files)
def main(args):
root = Tk()
root.title('aplaymidi_gui')
#root.geometry("1024x768")
#root.resizable(FALSE, FALSE)
root.option_add('*tearOff', FALSE)
root.columnconfigure(0, weight = 1)
root.rowconfigure(0, weight = 1)
newApp = App(root)
newApp.grid(sticky = (N, S, E, W))
root.mainloop()
return 0
if __name__ == '__main__':
import sys
sys.exit(main(sys.argv))