Commit af130e9d authored by Alyx's avatar Alyx
Browse files

initial commit

parents
Loading
Loading
Loading
Loading
Loading

midmix.py

0 → 100644
+48 −0
Original line number Diff line number Diff line
#k, 2019
import subprocess
import time
from player_functions import *
import argparse

# Arguments
parser = argparse.ArgumentParser(description='a tool to play a mix of your favourite midi files')
parser.add_argument('port',type=str,default="20:0",help='aplaymidi port number, can be found with `aplaymidi -l`')
parser.add_argument('-d','--dir',type=dir_path,default=str(Path.home())+"/Music",help='(default: ~/Music), note: will traverse subdirectories')
parser.add_argument('-p','--playlist',type=playlist,default=None,help='A playlist file')
parser.add_argument('-r','--playlistrepeat',help='Repeat Playlist',action='store_true')
args = parser.parse_args()

# Initialize synth in GS Mode; The Roland RA30 will by default be in Electric Piano reset
# mode until it either recieves a "GS Reset" or "GM System On" system exclusive
# message.
# Set device to GS Mode
def Init():
    print("Initializing synthisizer in mode")
    resetDevice(findAMIDIDevice())
    time.sleep(1)

# The Main Function.
def Main():
    fn_prefix="msmidi.py/Main"
    if(args.playlist is not None):
        print(fn_prefix+": opening playlist "+str(args.playlist))
        playlist=open(args.playlist)
    while True:
        if(args.playlist is not None):
            next_track=playlist.readline()
            # If we have reached the end of the playlist, restart it.
            if not next_track:
                if(args.playlistrepeat==True):
                    print(fn_prefix+": reached end of playlist. Repeat flag is true, repeating from the top.")
                    playlist = open(args.playlist)
                else:
                    print(fn_prefix+": reached end of playlist. Repeat flag is not set, terminating.")
                    return
        else:
            next_track=queueRandom(args.dir);
            print(":Random selection is "+str(next_track))
        playTrack(next_track,args.port)
        # wait just a sec
        time.sleep(1)
Init();
Main();

player_functions.py

0 → 100644
+90 −0
Original line number Diff line number Diff line
#k, 2019
import subprocess
import time
import requests
import os
import sys
import random
from pathlib import Path

# Find the ID of the first MIDI device since I only have one
def findAMIDIDevice():
    # get the output of amidi -l which lists onboard midi devices
    result=subprocess.check_output(['amidi','-l'])
    # return the ID of the first one
    return result[result.find("hw:".encode('ascii')):result.find(",".encode('ascii'))].decode('ascii')

# GM Mode= 'F07E7F0901F7'
# GS Mode= 'F04110421240007F0041F7'
# use amidi to send a reset command
# The resetDevice function sends a GM or GS reset sysex command to the synthesizer.
def resetDevice(device_id,mode='GS'):
    fn_prefix="player_functions.py/resetDevice"
    if(mode=="GM"):
        # send the sysex command for a GM Start to the synth using amidi
        subprocess.run(['amidi','-p',device_id,'-S','F07E7F0901F7'])
        print(fn_prefix+":Reset device to GM Mode")
    elif mode=="GS":
        # send the sysex command for a GS Reset to the synth using amidi
        subprocess.run(['amidi','-p',device_id,'-S','F04110421240007F0041F7'])
        print(fn_prefix+": Reset device to GS Mode")
    else:
        print(fn_prefix+": Invalid reset mode; must be GM or GS")
        return 0
    return

# The main player function
def playTrack(track_path,port):
    fn_prefix="player_functions.py/playTrack"
    track_path=str(track_path).rstrip()
    if Path(track_path).is_file():
        # reset the synth
        resetDevice(findAMIDIDevice())
        print(fn_prefix+": starting aplaymidi subprocess on file "+str(track_path))
        # Start the aplaymidi subprocess
        proc = subprocess.Popen(['aplaymidi', '--port', port, track_path],
                                stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT)
        print(fn_prefix+": aplaymidi started, beginning playback")
        # Checks periodically to see if the track has ended
        # or if a stop command has been recieved
        try:
            retry=1
            while retry==1:
                time.sleep(.5)
                if(proc.poll() is not None):
                    retry=0
        finally:
            # When we've stopped retrying, either because of stop command
            # or a finished track, terminate the subprocess if it hasn't already been
            proc.terminate()
            print(fn_prefix+": Recieved stop command")
            # send a GS reset to the synth
            resetDevice(findAMIDIDevice(),"GS")
            return
    else:
        print(fn_prefix+": failed to find file "+track_path+", skipping.")
        return

# Get a random thing from the music folder
# from https://codereview.stackexchange.com/questions/202749/pick-a-random-file-from-a-directory-tree
def queueRandom(music_path):
    fn_prefix="player_functions.py/queueRandom"
    file_list = list(Path(music_path).glob(f"**/*.mid"))
    if not len(file_list):
        print(fn_prefix+": No files matched")
        return
    rand = random.randint(0, len(file_list) - 1)
    return file_list[rand]
# Validate that a user-entered directory is a valid location to look for MIDIs
def dir_path(string):
    if os.path.isdir(string):
        return string
    else:
        raise NotADirectoryError(string)
# Validate that a user-entered playlist is a valid file
def playlist(string):
    if os.path.isfile(string):
        return string
    else:
        raise NotAFile(string)