PEP8 happiness (almost)

This commit is contained in:
Gergely Polonkai 2016-06-09 21:31:46 +02:00
parent ed2d3ed42a
commit e87b60252a

View File

@ -1,10 +1,33 @@
#! /usr/bin/env python #! /usr/bin/env python
# -*- coding: utf-8 # -*- coding: utf-8
"""
Generate sound for Git repositories
"""
from __future__ import print_function
import argparse import argparse
import sys import sys
import os import os
try:
import pygame
import pygame.mixer
PYGAME_AVAILABLE = True
except ImportError:
PYGAME_AVAILABLE = False
try:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GLib
GTK_AVAILABLE = True
except ImportError:
GTK_AVAILABLE = False
import shutil import shutil
from midiutil.MidiFile import MIDIFile from midiutil.MidiFile import MIDIFile
@ -14,7 +37,7 @@ from git import Repo
from git.objects.blob import Blob from git.objects.blob import Blob
from git.exc import InvalidGitRepositoryError from git.exc import InvalidGitRepositoryError
scales = { SCALES = {
'c-major': ('C Major', [60, 62, 64, 65, 67, 69, 71]), 'c-major': ('C Major', [60, 62, 64, 65, 67, 69, 71]),
'a-harmonic-minor': ('A Harmonic Minor', [68, 69, 71, 72, 74, 76, 77]), 'a-harmonic-minor': ('A Harmonic Minor', [68, 69, 71, 72, 74, 76, 77]),
'chromatic': ('Chromatic', [60, 61, 62, 63, 64, 65, 66, 67, 68, 69]), 'chromatic': ('Chromatic', [60, 61, 62, 63, 64, 65, 66, 67, 68, 69]),
@ -22,7 +45,7 @@ scales = {
'd-major': ('D Major', [62, 64, 65, 67, 69, 71, 72]), 'd-major': ('D Major', [62, 64, 65, 67, 69, 71, 72]),
} }
programs = { PROGRAMS = {
'sitar-tablah': { 'sitar-tablah': {
'name': 'Sitar and Tablah', 'name': 'Sitar and Tablah',
'commit': { 'commit': {
@ -94,44 +117,88 @@ programs = {
def get_file_sha(commit, file_name): def get_file_sha(commit, file_name):
elements = file_name.split(os.sep) """
t = commit.tree Get the SHA1 ID of a file by its name, in the given commit.
"""
elements = file_name.split(os.sep)
tree = commit.tree
while True: while True:
try: try:
t = t[elements.pop(0)] tree = tree[elements.pop(0)]
except (KeyError, IndexError): except (KeyError, IndexError):
# The file has been deleted, return the hash of an empty file # The file has been deleted, return the hash of an empty file
return 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391' return 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'
if isinstance(t, Blob): if isinstance(tree, Blob):
break break
return t.hexsha return tree.hexsha
class GitSoundWindow(object): class GitSoundWindow(object):
"""
GIU class for git-sound.
"""
def __init__(self): def __init__(self):
import gi self.builder = Gtk.Builder()
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
self.Gtk = Gtk
from gi.repository import GLib
self.GLib = GLib
self.builder = self.Gtk.Builder()
self.builder.add_from_file('git-sound.ui') self.builder.add_from_file('git-sound.ui')
self.win = self.builder.get_object('main-window') self.win = self.builder.get_object('main-window')
self.play_button = self.builder.get_object('play-button') self.play_button = self.builder.get_object('play-button')
self.stop_button = self.builder.get_object('stop-button') self.stop_button = self.builder.get_object('stop-button')
self.vol_spin = self.builder.get_object('vol-spin')
self.program_combo = self.builder.get_object('program-combo')
self.progressbar = self.builder.get_object('generate-progress')
self.branch_combo = self.builder.get_object('branch-combo')
self.statusbar = self.builder.get_object('statusbar')
self.pos_label = self.builder.get_object('position-label')
self.skip_spin = self.builder.get_object('skip-spin')
self.scale_combo = self.builder.get_object('scale-combo')
self.chooser_button = self.builder.get_object('repo-chooser')
def read_branches(self, chooser_button):
self.gitmidi = None self.gitmidi = None
repo_path = chooser_button.get_file().get_path()
program_store = self.builder.get_object('program-list')
for program_id, program in PROGRAMS.items():
program_store.append([program['name'], program_id])
renderer = Gtk.CellRendererText()
self.program_combo.pack_start(renderer, True)
self.program_combo.add_attribute(renderer, "text", 0)
scale_store = self.builder.get_object('scale-list')
for scale_id, scale in SCALES.items():
scale_store.append([scale[0], scale_id])
renderer = Gtk.CellRendererText()
self.scale_combo.pack_start(renderer, True)
self.scale_combo.add_attribute(renderer, "text", 0)
self.builder.connect_signals({
'read_branches': lambda button: self.read_branches(),
'settings_changed': lambda button: self.settings_changed(),
'generate_repo': lambda button: self.generate_repo(),
'play_midi': lambda button: self.play_midi(),
'stop_midi': lambda button: self.stop_midi(),
'save_midi': lambda button: self.save_midi(),
})
self.win.connect("delete-event", Gtk.main_quit)
def read_branches(self):
"""
Callback for the repository chooser. Upon change, this reads
all the branches from the selected repository.
"""
# Make sure the Play, Stop and Save buttons are disabled
self.gitmidi = None
repo_path = self.chooser_button.get_file().get_path()
self.branch_combo.remove_all() self.branch_combo.remove_all()
self.branch_combo.set_button_sensitivity(False) self.branch_combo.set_button_sensitivity(False)
self.set_buttons_sensitivity(disable_all=True) self.set_buttons_sensitivity(disable_all=True)
@ -139,11 +206,11 @@ class GitSoundWindow(object):
try: try:
repo = Repo(repo_path) repo = Repo(repo_path)
except InvalidGitRepositoryError: except InvalidGitRepositoryError:
dialog = self.Gtk.MessageDialog( dialog = Gtk.MessageDialog(
chooser_button.get_toplevel(), self.chooser_button.get_toplevel(),
self.Gtk.DialogFlags.MODAL, Gtk.DialogFlags.MODAL,
self.Gtk.MessageType.ERROR, Gtk.MessageType.ERROR,
self.Gtk.ButtonsType.OK, Gtk.ButtonsType.OK,
"{} is not a valid Git repository".format( "{} is not a valid Git repository".format(
repo_path)) repo_path))
@ -160,16 +227,31 @@ class GitSoundWindow(object):
self.branch_combo.append_text(head.name) self.branch_combo.append_text(head.name)
def set_status(self, text): def set_status(self, text):
"""
Change the status bar text.
"""
self.statusbar.push(self.statusbar.get_context_id("git-sound"), text) self.statusbar.push(self.statusbar.get_context_id("git-sound"), text)
def settings_changed(self, button): def settings_changed(self):
"""
Callback to use if anything MIDI-related is changed
(repository, branch, scale or program).
"""
self.gitmidi = None self.gitmidi = None
self.set_buttons_sensitivity() self.set_buttons_sensitivity()
self.stop_midi() self.stop_midi()
def set_buttons_sensitivity(self, disable_all=False): def set_buttons_sensitivity(self, disable_all=False):
"""
Set buttons sensitivity based on different conditions.
It checks if a repository and a branch is selected or if MIDI
data is already generated.
"""
generate_button = self.builder.get_object('generate-button') generate_button = self.builder.get_object('generate-button')
stop_button = self.builder.get_object('stop-button')
save_button = self.builder.get_object('save-button') save_button = self.builder.get_object('save-button')
if disable_all: if disable_all:
@ -198,9 +280,12 @@ class GitSoundWindow(object):
self.stop_button.set_sensitive(False) self.stop_button.set_sensitive(False)
save_button.set_sensitive(False) save_button.set_sensitive(False)
def generate_repo(self, button): def generate_repo(self):
chooser_button = self.builder.get_object('repo-chooser') """
repo_path = chooser_button.get_file().get_path() Generate repository data for MIDI data.
"""
repo_path = self.chooser_button.get_file().get_path()
branch_selected = self.branch_combo.get_active_text() branch_selected = self.branch_combo.get_active_text()
program_selected = self.program_combo.get_active_id() program_selected = self.program_combo.get_active_id()
scale_selected = self.scale_combo.get_active_id() scale_selected = self.scale_combo.get_active_id()
@ -213,8 +298,8 @@ class GitSoundWindow(object):
self.gitmidi = GitMIDI(repository=repo_path, self.gitmidi = GitMIDI(repository=repo_path,
branch=branch_selected, branch=branch_selected,
verbose=False, verbose=False,
scale=scales[scale_selected][1], scale=SCALES[scale_selected][1],
program=programs[program_selected], program=PROGRAMS[program_selected],
volume_range=vol_deviation, volume_range=vol_deviation,
skip=skip) skip=skip)
@ -225,15 +310,24 @@ class GitSoundWindow(object):
self.set_buttons_sensitivity(disable_all=False) self.set_buttons_sensitivity(disable_all=False)
def genrepo_cb(self, max_count=None, current=None): def genrepo_cb(self, max_count=None, current=None):
"""
Generate repository data. This is called when the user presses
the Generate button.
"""
if max_count is None or current is None: if max_count is None or current is None:
self.progressbar.pulse() self.progressbar.pulse()
else: else:
self.progressbar.set_fraction(current / max_count) self.progressbar.set_fraction(current / max_count)
# Make sure the progress bar gets updated # Make sure the progress bar gets updated
self.Gtk.main_iteration_do(False) Gtk.main_iteration_do(False)
def update_play_pos(self): def update_play_pos(self):
"""
Update playback position label.
"""
if self.gitmidi is None: if self.gitmidi is None:
return return
@ -257,88 +351,73 @@ class GitSoundWindow(object):
return True return True
def play_midi(self): def play_midi(self):
"""
Start MIDI playback.
"""
self.set_status(u"Playing…") self.set_status(u"Playing…")
self.gitmidi.play(track=True) self.gitmidi.play(track=True)
self.GLib.timeout_add_seconds(1, self.update_play_pos) GLib.timeout_add_seconds(1, self.update_play_pos)
self.play_button.set_sensitive(False) self.play_button.set_sensitive(False)
self.stop_button.set_sensitive(True) self.stop_button.set_sensitive(True)
def stop_midi(self): def stop_midi(self):
"""
Stop MIDI playback.
"""
if self.gitmidi is not None: if self.gitmidi is not None:
self.gitmidi.stop() self.gitmidi.stop()
def __save(self, dialog, response_id): def __save(self, dialog, response_id):
if response_id == self.Gtk.ResponseType.OK: """
Do the actual MIDI saving after the user chose a file.
"""
if response_id == Gtk.ResponseType.OK:
save_file = dialog.get_file().get_path() save_file = dialog.get_file().get_path()
dialog.destroy() dialog.destroy()
self.gitmidi.export_file(save_file) self.gitmidi.export_file(save_file)
def save_midi(self): def save_midi(self):
dialog = self.Gtk.FileChooserDialog( """
Save MIDI data.
"""
dialog = Gtk.FileChooserDialog(
u"Save As…", u"Save As…",
self.win, self.win,
self.Gtk.FileChooserAction.SAVE, Gtk.FileChooserAction.SAVE,
("Save", self.Gtk.ResponseType.OK)) ("Save", Gtk.ResponseType.OK))
dialog.set_do_overwrite_confirmation(True) dialog.set_do_overwrite_confirmation(True)
dialog.connect('response', self.__save) dialog.connect('response', self.__save)
dialog.run() dialog.run()
def start(self): def start(self):
program_store = self.builder.get_object('program-list') """
self.program_combo = self.builder.get_object('program-combo') Start the GUI.
"""
for program_id, program in programs.items():
program_store.append([program['name'], program_id])
renderer = self.Gtk.CellRendererText()
self.program_combo.pack_start(renderer, True)
self.program_combo.add_attribute(renderer, "text", 0)
scale_store = self.builder.get_object('scale-list')
self.scale_combo = self.builder.get_object('scale-combo')
for scale_id, scale in scales.items():
scale_store.append([scale[0], scale_id])
renderer = self.Gtk.CellRendererText()
self.scale_combo.pack_start(renderer, True)
self.scale_combo.add_attribute(renderer, "text", 0)
self.branch_combo = self.builder.get_object('branch-combo')
self.statusbar = self.builder.get_object('statusbar')
self.pos_label = self.builder.get_object('position-label')
self.skip_spin = self.builder.get_object('skip-spin')
self.vol_spin = self.builder.get_object('vol-spin')
self.builder.connect_signals({
'read_branches': lambda button: self.read_branches(button),
'settings_changed': lambda button: self.settings_changed(button),
'generate_repo': lambda button: self.generate_repo(button),
'play_midi': lambda button: self.play_midi(),
'stop_midi': lambda button: self.stop_midi(),
'save_midi': lambda button: self.save_midi(),
})
self.progressbar = self.builder.get_object('generate-progress')
self.win.connect("delete-event", self.Gtk.main_quit)
self.win.show_all() self.win.show_all()
self.Gtk.main() Gtk.main()
sys.exit(0) sys.exit(0)
def start_gui():
win = GitSoundWindow()
win.start()
class GitMIDI(MIDIFile): class GitMIDI(MIDIFile):
"""
Class to hold repository data, and MIDI data based on that repository.
"""
LOG_CHANNEL = 0 LOG_CHANNEL = 0
FILE_CHANNEL = 1 FILE_CHANNEL = 1
def __setup_midi(self, track_title=None): def __setup_midi(self, track_title=None):
"""
Initialise the MIDI file.
"""
if self.__verbose: if self.__verbose:
print("Preparing MIDI track…") print("Preparing MIDI track…")
@ -346,8 +425,7 @@ class GitMIDI(MIDIFile):
# TODO: Change this to something that connects to the repo # TODO: Change this to something that connects to the repo
self.addTrackName(0, 0, "Sample Track") self.addTrackName(0, 0, "Sample Track")
# TODO: Make this configurable self.addTempo(0, 0, self.__tempo)
self.addTempo(0, 0, 120)
if self.__need_commits: if self.__need_commits:
self.addProgramChange(0, self.LOG_CHANNEL, self.addProgramChange(0, self.LOG_CHANNEL,
@ -358,32 +436,38 @@ class GitMIDI(MIDIFile):
0, self.__program['file']['program']) 0, self.__program['file']['program'])
def __setup_repo(self): def __setup_repo(self):
"""
Setup repository and get the specified branch.
"""
if self.__verbose: if self.__verbose:
print("Analyzing repository…") print("Analyzing repository…")
repo = Repo(self.__repo_dir) repo = Repo(self.__repo_dir)
self.branch_head = repo.heads[self.__branch].commit self.__branch_head = repo.heads[self.__branch].commit
def __init__(self, def __init__(self,
repository='.', repository=None,
branch='master', branch=None,
verbose=False, verbose=False,
scale=None, scale=None,
program=None, program=None,
volume_range=107, volume_range=107,
skip=0, skip=0,
note_duration=0.3) note_duration=0.3,
max_beat_len=None,
tempo=120):
MIDIFile.__init__(self, 1) MIDIFile.__init__(self, 1)
self.__verbose = verbose self.__verbose = verbose
self.__written = False self.__written = False
self.__repo_dir = repository self.__repo_dir = repository or '.'
self.__repo = None self.__repo = None
self.__branch = branch self.__branch = branch or 'master'
self.branch_head = None self.__branch_head = None
self.__repo_data = None self.__repo_data = None
self.git_log = [] self.__git_log = []
self.mem_file = StringIO() self.__mem_file = StringIO()
self.__scale = scale self.__scale = scale
self.__program = program self.__program = program
self.__volume_deviation = min(abs(63 - volume_range), 63) self.__volume_deviation = min(abs(63 - volume_range), 63)
@ -391,6 +475,8 @@ class GitMIDI(MIDIFile):
self.__playing = False self.__playing = False
self.__skip = skip self.__skip = skip
self.__note_duration = note_duration self.__note_duration = note_duration
self.__max_beat_len = max_beat_len or 0.3
self.__tempo = tempo
self.__need_commits = self.__program['commit']['program'] is not None self.__need_commits = self.__program['commit']['program'] is not None
self.__need_files = self.__program['file']['program'] is not None self.__need_files = self.__program['file']['program'] is not None
@ -413,12 +499,20 @@ class GitMIDI(MIDIFile):
63 - deletions + insertions + modifier)) 63 - deletions + insertions + modifier))
def sha_to_note(self, sha): def sha_to_note(self, sha):
"""
Calculate note based on an SHA1 hash
"""
note_num = reduce(lambda res, digit: res + int(digit, 16), note_num = reduce(lambda res, digit: res + int(digit, 16),
list(str(sha)), 0) % len(self.__scale) list(str(sha)), 0) % len(self.__scale)
return self.__scale[note_num] return self.__scale[note_num]
def gen_beat(self, commit, callback=None): def gen_beat(self, commit, callback=None):
"""
Generate data for a beat based on a commit and its files.
"""
stat = commit.stats stat = commit.stats
file_notes = [] file_notes = []
@ -428,12 +522,15 @@ class GitMIDI(MIDIFile):
callback(max_count=None, current=None) callback(max_count=None, current=None)
volume_mod = self.__program['file'].get('volume', 0) volume_mod = self.__program['file'].get('volume', 0)
file_notes.append({ file_note = self.sha_to_note(get_file_sha(commit, file_name)) + \
'note': self.sha_to_note(get_file_sha(commit, file_name)) + self.__program['file']['octave'] * 12
self.__program['file']['octave'] * 12, file_volume = self.gen_volume(file_stat['deletions'],
'volume': self.gen_volume(file_stat['deletions'],
file_stat['insertions'], file_stat['insertions'],
volume_mod), volume_mod)
file_notes.append({
'note': file_note,
'volume': file_volume,
}) })
if callback is not None: if callback is not None:
@ -443,7 +540,7 @@ class GitMIDI(MIDIFile):
return { return {
'commit_note': self.sha_to_note(commit.hexsha) + 'commit_note': self.sha_to_note(commit.hexsha) +
self.__program['commit']['octave'] * 12, self.__program['commit']['octave'] * 12,
'commit_volume': self.gen_volume(stat.total['deletions'], 'commit_volume': self.gen_volume(stat.total['deletions'],
stat.total['insertions'], stat.total['insertions'],
volume_mod), volume_mod),
@ -465,7 +562,7 @@ class GitMIDI(MIDIFile):
self.__repo_data = [] self.__repo_data = []
counter = 0 counter = 0
to_process = [self.branch_head] to_process = [self.__branch_head]
while len(to_process) > 0: while len(to_process) > 0:
counter += 1 counter += 1
@ -474,14 +571,14 @@ class GitMIDI(MIDIFile):
if counter % 500 == 0 and self.__verbose: if counter % 500 == 0 and self.__verbose:
print("Done with {} commits".format(counter)) print("Done with {} commits".format(counter))
commit = to_process.pop() current_commit = to_process.pop()
if callback is not None: if callback is not None:
callback(max_count=None, current=None) callback(max_count=None, current=None)
if not commit in self.__repo_data: if current_commit not in self.__repo_data:
self.__repo_data.append(commit) self.__repo_data.append(current_commit)
to_process += commit.parents to_process += current_commit.parents
if self.__verbose: if self.__verbose:
print("{} commits found".format(counter)) print("{} commits found".format(counter))
@ -492,32 +589,45 @@ class GitMIDI(MIDIFile):
if self.__verbose: if self.__verbose:
print("Generating MIDI data…") print("Generating MIDI data…")
self.git_log = map( self.__git_log = [self.gen_beat(commit, callback=callback)
lambda commit: self.gen_beat( for commit in self.__repo_data[self.__skip:]]
commit,
callback=callback),
self.__repo_data[self.__skip:])
@property @property
def repo_data(self): def repo_data(self):
"""
Get repository data for MIDI generation.
"""
if self.__repo_data is None: if self.__repo_data is None:
self.gen_repo_data(force=True) self.gen_repo_data(force=True)
return self.__repo_data return self.__repo_data
def write_mem(self): def write_mem(self):
self.writeFile(self.mem_file) """
Write MIDI data to the memory file.
"""
self.writeFile(self.__mem_file)
self.__written = True self.__written = True
def export_file(self, filename): def export_file(self, filename):
"""
Export MIDI data to a file.
"""
if not self.__written: if not self.__written:
self.write_mem() self.write_mem()
with open(filename, 'w') as f: with open(filename, 'w') as midi_file:
self.mem_file.seek(0) self.__mem_file.seek(0)
shutil.copyfileobj(self.mem_file, f) shutil.copyfileobj(self.__mem_file, midi_file)
def generate_midi(self, callback=None): def generate_midi(self, callback=None):
"""
Generate MIDI data.
"""
if self.__verbose: if self.__verbose:
print("Creating MIDI…") print("Creating MIDI…")
@ -526,11 +636,11 @@ class GitMIDI(MIDIFile):
log_channel = 0 log_channel = 0
decor_channel = 1 decor_channel = 1
log_length = len(self.git_log) log_length = len(self.__git_log)
current = 0 current = 0
# WRITE THE SEQUENCE # WRITE THE SEQUENCE
for section in self.git_log: for section in self.__git_log:
current += 1 current += 1
section_len = len(section['file_notes']) * self.__note_duration section_len = len(section['file_notes']) * self.__note_duration
@ -553,52 +663,72 @@ class GitMIDI(MIDIFile):
time += section_len time += section_len
def __init_pygame(self): def __init_pygame(self):
if self.__pygame_inited: """
Initialise pygame.
"""
if not PYGAME_AVAILABLE or self.__pygame_inited:
return return
# Import pygame stuff here # Initialise pygame
import pygame
import pygame.mixer
self.pygame = pygame
self.mixer = pygame.mixer
# PLAYBACK
pygame.init() pygame.init()
pygame.mixer.init() pygame.mixer.init()
def play(self, track=False): def play(self, track=False):
"""
Start MIDI playback. If pygame is not available, dont do anything.
"""
if not PYGAME_AVAILABLE:
return "pygame is not available, cannot start playback"
if self.__verbose: if self.__verbose:
print("Playing!") print("Playing!")
self.__init_pygame() self.__init_pygame()
self.mem_file.seek(0) self.__mem_file.seek(0)
self.mixer.music.load(self.mem_file) pygame.mixer.music.load(self.__mem_file)
self.mixer.music.play() pygame.mixer.music.play()
self.__playing = True self.__playing = True
if not track: if not track:
while self.mixer.music.get_busy(): while pygame.mixer.music.get_busy():
sleep(1) sleep(1)
self.__playing = False self.__playing = False
def stop(self): def stop(self):
self.mixer.music.stop() """
Stop MIDI playback.
"""
if not PYGAME_AVAILABLE:
return
pygame.mixer.music.stop()
def get_play_pos(self): def get_play_pos(self):
"""
Get current playback position from the mixer
"""
if not self.__playing: if not self.__playing:
return None return None
if self.mixer.music.get_busy(): if pygame.mixer.music.get_busy():
return self.mixer.music.get_pos() return pygame.mixer.music.get_pos()
else: else:
self.__playing = False self.__playing = False
return None return None
if __name__ == '__main__':
def main():
"""
Main function, used if we are not imported.
"""
parser = argparse.ArgumentParser(description='Voice of a Repo', parser = argparse.ArgumentParser(description='Voice of a Repo',
epilog='Use the special value list for ' + epilog='Use the special value list for ' +
'scale and program to list the ' + 'scale and program to list the ' +
@ -644,8 +774,10 @@ if __name__ == '__main__':
args = parser.parse_args() args = parser.parse_args()
if args.scale is None and args.program is None: if args.scale is None and args.program is None and GTK_AVAILABLE:
start_gui() GitSoundWindow().start()
sys.exit(0)
if args.scale is None and args.program != 'list': if args.scale is None and args.program != 'list':
print("Please specify a scale!") print("Please specify a scale!")
@ -658,24 +790,24 @@ if __name__ == '__main__':
sys.exit(1) sys.exit(1)
if args.scale == 'list': if args.scale == 'list':
for scale in scales.keys(): for scale in SCALES.keys():
print(scale) print(scale)
sys.exit(0) sys.exit(0)
if args.program == 'list': if args.program == 'list':
for program in programs.keys(): for program in PROGRAMS.keys():
print(program) print(program)
sys.exit(0) sys.exit(0)
if args.scale not in scales: if args.scale not in SCALES:
print("{} is an unknown scale.".format(args.scale)) print("{} is an unknown scale.".format(args.scale))
print("Use 'list' to list the available scales.") print("Use 'list' to list the available scales.")
sys.exit(1) sys.exit(1)
if args.program not in programs: if args.program not in PROGRAMS:
print("{} is an unknown program.".format(args.program)) print("{} is an unknown program.".format(args.program))
print("Use 'list' to list the available programs.") print("Use 'list' to list the available programs.")
@ -685,13 +817,13 @@ if __name__ == '__main__':
repo_midi = GitMIDI(repository=args.repository, repo_midi = GitMIDI(repository=args.repository,
branch=args.branch, branch=args.branch,
verbose=args.verbose, verbose=args.verbose,
scale=scales[args.scale][1], scale=SCALES[args.scale][1],
program=programs[args.program], program=PROGRAMS[args.program],
volume_range=args.volume_range, volume_range=args.volume_range,
skip=args.skip) skip=args.skip)
except InvalidGitRepositoryError: except InvalidGitRepositoryError:
print("{} is not a valid Git repository" \ print("{} is not a valid Git repository"
.format(os.path.abspath(args.repository))) .format(os.path.abspath(args.repository)))
sys.exit(1) sys.exit(1)
@ -713,3 +845,6 @@ if __name__ == '__main__':
if args.play: if args.play:
repo_midi.play() repo_midi.play()
if __name__ == '__main__':
main()