stt/stt.py

266 lines
8.5 KiB
Python
Raw Permalink Normal View History

2020-06-17 06:35:34 +02:00
#!/usr/bin/env python3
# include standard modules
import argparse
import os
import sys
import re
import json
import urllib.request
2020-06-17 06:35:34 +02:00
mydir = os.path.dirname(os.path.realpath(__file__))
# Lookup value is value of note for ( string * 1000 ) + value of fret
lookuptable = {
57: ["Cr", "x"],
49: ["Cr", "x"],
1042: ["Hh", "x"],
46: ["Hh", "o"],
51: ["Ri", "x"],
1051: ["Ri", "x"],
3048: ["HT", "o"],
1048: ["MT", "o"],
3047: ["MT", "o"],
3045: ["MT", "o"], # LT
31: ["St", "o"], # Sticks
2038: ["Sn", "o"],
4038: ["Sn", "o"],
4040: ["Sn", "o"],
3041: ["FT", "o"], # LFT
4041: ["FT", "o"], # LFT
2043: ["FT", "o"],
3043: ["FT", "o"],
4043: ["FT", "o"],
5035: ["Bd", "o"],
5036: ["Bd", "o"],
44: ["Hf", "x"],
5044: ["Hf", "x"]
}
instr_index = {
"Cr": "Cr - Crash ",
"Hh": "Hh - HiHat ",
"Ri": "Ri - Ride ",
"HT": "HT - High Tom ",
"MT": "MT - Mid Tom ",
"Sn": "Sn - Snare ",
"FT": "FT - Floor Tom ",
"Bd": "Bd - Kick ",
"Hf": "Hf - Foot pedal "
}
### DrumBurp notation Wikipedia notation Wikipedia notation
### Cr - Crash Drums Cymbals
### Hh - HiHat |-o-| Strike |-x-| Strike cymbal or hi-hat
### Ri - Ride |-O-| Accent |-X-| Strike loose hi-hat, or hit crash hard
### HT - High Tom |-g-| Ghost note |-o-| Open hi-hat
### MT - Mid Tom |-f-| Flam |-#-| Choke cymbal (grab cymbal with hand after striking it)
### Sn - Snare |-d-| Drag |-s-| Splash cymbal
### FT - Floor Tom |-b-| Soft one-handed roll |-c-| China cymbal
### Bd - Kick |-B-| Accented one-handed roll |-b-| Bell of ride
### Hf - Foot pedal |-@-| Snare rim |-x-| Click hi-hat with foot
### DrumBurp: https://whatang.org/
### Wikipedia: https://en.wikipedia.org/wiki/Drum_tablature
2020-06-17 06:35:34 +02:00
def commandline():
# initiate the parser
parser = argparse.ArgumentParser(description="Make ASCII tabs from songsterr tabs.")
2020-06-17 06:35:34 +02:00
# option without argument via 'store_true'
parser.add_argument("-V", "--version", help="show program version", action="store_true")
parser.add_argument("--dump", "-d", help="dump json and exit", action="store_true")
parser.add_argument("--width", "-w", help="set output width in measures (default: 2)")
2020-06-17 06:35:34 +02:00
parser.add_argument("--input", "-i", help="set input file")
parser.add_argument("--url", "-u", help="set input url")
parser.add_argument("--exclude", "-x", help="exclude unused instruments", action="store_true")
parser.add_argument("--compress", "-c", help="compress empty intro", action="store_true")
2020-06-17 06:35:34 +02:00
# read arguments from the command line
args = parser.parse_args()
# handle arguments
# check for --version or -V
if args.version:
print("Version 0.1")
2020-06-17 06:35:34 +02:00
# check for --width
if not args.width:
args.width = 2
if args.input and args.url:
print("Give either a url, or an input file, not both.")
exit()
if not args.input and not args.url:
print("Give either a url, or an input file.")
exit()
2020-06-17 06:35:34 +02:00
return args
def read_content():
content = ""
if args.input:
with open(args.input, 'r') as content_file:
content = content_file.read()
elif args.url:
fp = urllib.request.urlopen(args.url)
mybytes = fp.read()
content = mybytes.decode("utf8")
fp.close()
return content
2020-06-17 06:35:34 +02:00
def get_json(html):
result = re.search(r'<script id="state" type="application/json">(?P<json>.*?)</script', html, re.MULTILINE)
if result:
data = json.loads(result.group("json"))
else:
print("Can't find the magic")
return data
def dump_json(html):
result = re.search(r'<script id="state" type="application/json">(?P<json>.*?)</script', html, re.MULTILINE)
if result:
print(result.group("json"))
exit()
def print_meta(jsondata):
print( "Artist: " + jsondata["meta"]["artist"] )
print( "Title: " + jsondata["meta"]["title"] )
print( "Instrument: " + jsondata["data"]["part"]["instrument"] )
print( "BPM: " + str( jsondata["data"]["part"]["measures"][0]["voices"][0]["beats"][0]["tempo"]["bpm"] ) )
print("")
2020-06-17 06:35:34 +02:00
return
2020-06-17 06:35:34 +02:00
def get_skip(tp, typelength):
2020-06-17 21:13:18 +02:00
if tp < typelength:
skip = int( ( typelength / tp ) )
2020-06-17 21:13:18 +02:00
else:
skip = 1
2020-06-17 21:13:18 +02:00
return skip
2020-06-17 21:13:18 +02:00
def get_typelength(jsondata):
typelength = 0
for i in range(len(jsondata["data"]["part"]["measures"])):
for j in range(len(jsondata["data"]["part"]["measures"][i]["voices"][0]["beats"])):
beat = jsondata["data"]["part"]["measures"][i]["voices"][0]["beats"][j]
if typelength < beat["type"]:
typelength = beat["type"]
return typelength
def parse_instruments(jsondata):
# In the end I want an array of measures for each instrument
# That'll make for easy printing
used_instr = {
"Cr": False,
"Hh": False,
"Ri": False,
"HT": False,
"MT": False,
"Sn": False,
"FT": False,
"Bd": False,
"Hf": False
}
instruments = {
"Cr": [],
"Hh": [],
"Ri": [],
"HT": [],
"MT": [],
"Sn": [],
"FT": [],
"Bd": [],
"Hf": []
}
typelength = get_typelength(jsondata)
#print("Typelength: " + str(typelength))
#print("")
first_instrument = -1
for measurecnt in range(len(jsondata["data"]["part"]["measures"])):
count = 0
# add one measure to each instrument
for instr in ["Cr", "Hh", "Ri", "HT", "MT", "Sn", "FT", "Bd", "Hf"]:
instruments[instr].append([])
for i in range(typelength):
instruments[instr][-1].append("-")
for beatcnt in range(len(jsondata["data"]["part"]["measures"][measurecnt]["voices"][0]["beats"])):
beat = jsondata["data"]["part"]["measures"][measurecnt]["voices"][0]["beats"][beatcnt]
skip = get_skip(beat["type"], typelength)
for note in beat["notes"]:
if note != {'rest': True}:
lookupval = ( note["string"] * 1000 ) + note["fret"]
if lookupval in lookuptable:
if first_instrument < 0:
first_instrument = measurecnt
inst, marker = lookuptable[lookupval]
if inst != None and inst in instruments:
instruments[inst][-1][count] = marker
used_instr[inst] = True
else:
print("Unhandled: " + str(note) + " in measure " + str(measurecnt+1))
print("Add: " + str(( note["string"] * 1000 ) + note["fret"]) + " to lookuptable")
count += skip
return instruments, used_instr, first_instrument
def print_instr_index(used_instr):
for instr in ["Cr", "Hh", "Ri", "HT", "MT", "Sn", "FT", "Bd", "Hf"]:
if not args.exclude or used_instr[instr]:
print(instr_index[instr])
print("")
return
def print_instruments(instruments, used_instr, first_instrument):
measuresnr = len(instruments["Cr"])
width = int(args.width)
rows = ( measuresnr / width )
count = 0
if args.compress and first_instrument > 0:
for instr in ["Cr", "Hh", "Ri", "HT", "MT", "Sn", "FT", "Bd", "Hf"]:
if not args.exclude or used_instr[instr]:
sys.stdout.write(instr + " |")
sys.stdout.write("".join(instruments[instr][0]) + "|")
print("")
print("Repeat " + str(first_instrument) + " times")
print("")
count = first_instrument
while count < measuresnr:
for instr in ["Cr", "Hh", "Ri", "HT", "MT", "Sn", "FT", "Bd", "Hf"]:
if not args.exclude or used_instr[instr]:
sys.stdout.write(instr + " |")
for i in range(count, count+width):
if i < measuresnr:
sys.stdout.write("".join(instruments[instr][i]) + "|")
print("")
print("")
count += width
args = commandline()
content = read_content()
if args.dump:
dump_json(content)
jsondata = get_json(content)
print_meta(jsondata)
instr, used_instr, first_instrument = parse_instruments(jsondata)
print_instr_index(used_instr)
print_instruments(instr, used_instr, first_instrument)