Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion smdatatools/common/cli_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,27 @@
from smdatatools.data_processing.output_processor import OutputProcessor

class Options:

@staticmethod
def read_SMtoData(filepath: str) -> DataHandler:
data = DataHandler(filepath)
istr = read_file(filepath)
data.note_data, data.valid = InputProcessor.parse_sm_input(istr)
return data

@staticmethod
def read_TXTtoData(filepath: str) -> DataHandler:
data = DataHandler(filepath)
istr = read_file(filepath)
data.note_data = InputProcessor.parse_txt_input(istr)
return data

@staticmethod
def write_DatatoSM(data: DataHandler, filepath: str):
filename = strip_filename(filepath)
ostr = OutputProcessor.pregenerate_sm_output(filename, data.processed_data)
write_file(ostr, filepath)

@staticmethod
def write_DatatoTXT(data: DataHandler, filepath: str):
ostr = OutputProcessor.pregenerate_txt_output(data.note_data)
write_file(ostr, filepath)
13 changes: 7 additions & 6 deletions smdatatools/common/file_utils.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
from os import walk
from os.path import join, split, splitext
from re import sub
from typing import Collection

def read_file(filename):
def read_file(filename: str) -> list[str]:
file_data = []
with open(filename, encoding='ascii', errors='ignore') as f:
file_data = f.read().splitlines()
return file_data

def write_file(output_data, filename):
def write_file(output_data: str, filename: str):
with open(filename, 'w') as f:
f.write(output_data)

def strip_filename(filename):
def strip_filename(filename: str) -> str:
'''
Strips file path
Strips file extension
Expand All @@ -23,7 +24,7 @@ def strip_filename(filename):
tail = splitext(tail)[0]
return sub(' ', '_', sub('[^a-z0-9-_ ]', '', tail.lower()))

def collect_filenames(input_dir: str, extensions: list):
def collect_filenames(input_dir: str, extensions: Collection[str]) -> list[str]:
filenames = []
for root, dirs, files in walk(input_dir):
for filename in files:
Expand All @@ -32,15 +33,15 @@ def collect_filenames(input_dir: str, extensions: list):
filenames.append(join(root, filename).replace("\\","/"))
return filenames

def getFilePaths(input_dir: list, extensions: list):
def getFilePaths(input_dir: str, extensions: Collection[str]) -> list[str]:
filepaths = collect_filenames(input_dir, extensions)

if '.sm' in extensions:
checkFilePaths(filepaths)

return filepaths

def checkFilePaths(sm_filepaths):
def checkFilePaths(sm_filepaths: list[str]):
# checks for static bpm in the .sm file
# and removes filepath from list if not
for sm_file in sm_filepaths:
Expand Down
16 changes: 10 additions & 6 deletions smdatatools/components/measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
from math import gcd, ceil

class Measure:

def calculate_timing(measure, measure_index, bpm, offset):
@staticmethod
def calculate_timing(measure: list[str | None], measure_index: int, bpm: float, offset: float) -> list[str]:
# calculate time in seconds for each line in the measure:
# BPM = beats/minute -> BPS = beats/second = BPM/60
# measure = 4 beats = 4 * 1/4th notes = 1 note
Expand All @@ -16,7 +16,8 @@ def calculate_timing(measure, measure_index, bpm, offset):
# returns the note/timing pair, if the note exists
return [measure[i] + ' ' + str(i * note_256 * fraction_256 + measure_timing - offset) for i, is_set in enumerate(measure) if is_set != None]

def find_gcd(note_positions) -> int:
@staticmethod
def find_gcd(note_positions: list[int]) -> int:
# attempts to fit the note positions into either a
# 256, 128, 64, 32, 16, 8 or 4 note measure based on spacing
# found by getting the greatest common denominator
Expand All @@ -30,7 +31,8 @@ def find_gcd(note_positions) -> int:

return int(gcd_gap)

def generate_measure(notes, note_positions) -> list[str]:
@staticmethod
def generate_measure(notes: list[str], note_positions: list[int]) -> list[str]:

# we'll want to trim as much output as possible
# by reducing the measure size
Expand All @@ -46,7 +48,8 @@ def generate_measure(notes, note_positions) -> list[str]:

return generated_measure

def fit_notes_to_measure(notes, timings, seconds_1_256) -> list[str]:
@staticmethod
def fit_notes_to_measure(notes: list[str], timings: list[float], seconds_1_256: float) -> list[str]:
# if no data is passed, generate current measure
# as "empty" with the smallest size
if not notes or not timings:
Expand Down Expand Up @@ -78,7 +81,8 @@ def fit_notes_to_measure(notes, timings, seconds_1_256) -> list[str]:

return measure

def place_notes(notes_and_timings, bpm) -> list:
@staticmethod
def place_notes(notes_and_timings: list[str], bpm: float) -> list[str]:
placed_notes = []
if not notes_and_timings:
return placed_notes
Expand Down
7 changes: 4 additions & 3 deletions smdatatools/data_processing/data_handler.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
from collections import defaultdict
from typing import Any

from smdatatools.common.file_utils import strip_filename
from smdatatools.components.measure import Measure

class DataHandler:
# each Data Handler will represent the data of one .sm/.txt file

def __init__(self, filepath):
def __init__(self, filepath: str):
self.sm_path = filepath
self.filename = strip_filename(filepath)

self.note_data = defaultdict(list)
self.processed_data = defaultdict(list)
self.note_data: dict[str, Any] = defaultdict(list)
self.processed_data: dict[str, Any] = defaultdict(list)
self.valid = True

def process_data_to_sm_format(self):
Expand Down
22 changes: 13 additions & 9 deletions smdatatools/data_processing/input_processor.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
from collections import defaultdict
from re import sub, split
from typing import Any

from smdatatools.components.measure import Measure

class InputProcessor:

def convert_note(line):
class InputProcessor:
@staticmethod
def convert_note(line: str) -> str:
return sub('4', '1', sub('[MKLF]', '0', line)) #replaces extra notes: M, K, L, F; replaces 4 note

def parse_sm_input(sm_file):
note_data = defaultdict(list)
note_data['notes'] = defaultdict(list) # notes are paired with each difficulty
@staticmethod
def parse_sm_input(sm_file: list[str]) -> tuple[dict[str, Any], bool]:
note_data: dict[str, Any] = defaultdict(list)
note_data['notes']: dict[str, Any] = defaultdict(list) # notes are paired with each difficulty
current_difficulty = ''
measure = []
measure_index = 0
Expand Down Expand Up @@ -72,11 +75,12 @@ def parse_sm_input(sm_file):
measure.append(note) # adds note if found
else:
measure.append(None)

return note_data, valid

def parse_txt_input(txt_file):
note_data = defaultdict(list)
@staticmethod
def parse_txt_input(txt_file: list[str]) -> dict[str, Any]:
note_data: dict[str, Any] = defaultdict(list)
note_data['notes'] = defaultdict(list)
current_difficulty = ''
notes_and_timings = []
Expand Down Expand Up @@ -105,5 +109,5 @@ def parse_txt_input(txt_file):
else:
notes_and_timings.append(line)
note_data['notes'][current_difficulty].extend(notes_and_timings)

return note_data
8 changes: 5 additions & 3 deletions smdatatools/data_processing/output_processor.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from collections import defaultdict
from typing import Any

class OutputProcessor:

def pregenerate_sm_output(file_name: str, note_data: defaultdict(list)) -> str:
@staticmethod
def pregenerate_sm_output(file_name: str, note_data: dict[str, Any]) -> str:
# pre-generate .sm output data
title = '#TITLE:%s;\n' % note_data['title']
artist = '#ARTIST:jhaco vs cpuguy96;\n'
Expand All @@ -26,7 +27,8 @@ def pregenerate_sm_output(file_name: str, note_data: defaultdict(list)) -> str:

return ''.join((title, artist, music, select, bpm, notes))

def pregenerate_txt_output(note_data: defaultdict(list)) -> str:
@staticmethod
def pregenerate_txt_output(note_data: dict[str, Any]) -> str:
# pre-generate output data
title = 'TITLE %s\n' % note_data['title']
bpm = 'BPM %s\n' % str(note_data['bpm'])
Expand Down