mirror of
https://github.com/stronk-dev/Guitar-Sheet-Parser.git
synced 2025-07-05 08:25:09 +02:00
Added support for writing to txt and rawtxt (which is quick to parse as input)
Added config options for choosing which extensions to read and which to write to
This commit is contained in:
parent
4eef1ff044
commit
8d03f6d665
@ -30,8 +30,13 @@ def initConfig():
|
|||||||
# Else load defaults
|
# Else load defaults
|
||||||
else:
|
else:
|
||||||
config['input'] = {'inputFolders': os.getcwd(),
|
config['input'] = {'inputFolders': os.getcwd(),
|
||||||
'supportedExtensions': ".txt",
|
'maxDepth': 2,
|
||||||
'maxDepth': 3
|
'readtxt': 1,
|
||||||
|
'readraw': 1
|
||||||
|
}
|
||||||
|
config['options'] = {'exporttoimg': 1,
|
||||||
|
'exporttotxt': 0,
|
||||||
|
'exporttoraw': 0
|
||||||
}
|
}
|
||||||
config['output'] = {'metafontfamily': 'fonts/CourierPrime-Regular.ttf',
|
config['output'] = {'metafontfamily': 'fonts/CourierPrime-Regular.ttf',
|
||||||
'metaFontWeight': 16,
|
'metaFontWeight': 16,
|
||||||
|
@ -184,6 +184,7 @@ class Song:
|
|||||||
self.inputFile = ""
|
self.inputFile = ""
|
||||||
# Path to folder
|
# Path to folder
|
||||||
self.outputLocation = ""
|
self.outputLocation = ""
|
||||||
|
self.fileExtension = ""
|
||||||
# Title - based on input file
|
# Title - based on input file
|
||||||
self.title = ""
|
self.title = ""
|
||||||
# List of Section objects
|
# List of Section objects
|
||||||
@ -216,7 +217,9 @@ class Song:
|
|||||||
self.rightMargin = int(configObj['rightMargin'])
|
self.rightMargin = int(configObj['rightMargin'])
|
||||||
self.fontLyrics = ImageFont.truetype(configObj['lyricfontfamily'], self.fontSize)
|
self.fontLyrics = ImageFont.truetype(configObj['lyricfontfamily'], self.fontSize)
|
||||||
self.fontTablature = ImageFont.truetype(configObj['tablaturefontfamliy'], self.fontSize)
|
self.fontTablature = ImageFont.truetype(configObj['tablaturefontfamliy'], self.fontSize)
|
||||||
self.configObj = configObj
|
self.fontFamilyLyrics = configObj['lyricfontfamily']
|
||||||
|
self.fontFamilyTablature = configObj['tablaturefontfamliy']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""!@brief Calculates dimensions of metadata
|
"""!@brief Calculates dimensions of metadata
|
||||||
@ -247,8 +250,8 @@ class Song:
|
|||||||
def resizeAllSections(self, mutator):
|
def resizeAllSections(self, mutator):
|
||||||
#print("Resizing font by {} to {}".format(mutator, self.fontSize))
|
#print("Resizing font by {} to {}".format(mutator, self.fontSize))
|
||||||
self.fontSize += mutator
|
self.fontSize += mutator
|
||||||
self.fontLyrics = ImageFont.truetype(self.configObj['lyricfontfamily'], self.fontSize)
|
self.fontLyrics = ImageFont.truetype(self.fontFamilyLyrics, self.fontSize)
|
||||||
self.fontTablature = ImageFont.truetype(self.configObj['tablaturefontfamliy'], self.fontSize)
|
self.fontTablature = ImageFont.truetype(self.fontFamilyTablature, self.fontSize)
|
||||||
self.prerenderSections()
|
self.prerenderSections()
|
||||||
|
|
||||||
"""!@brief Calculates the expected dimensions of all sections
|
"""!@brief Calculates the expected dimensions of all sections
|
||||||
@ -378,6 +381,13 @@ class Song:
|
|||||||
# No more sections left, so the current buffered image is ready to be written to file
|
# No more sections left, so the current buffered image is ready to be written to file
|
||||||
curPage.totalHeight = currentHeight
|
curPage.totalHeight = currentHeight
|
||||||
self.pages.append(curPage)
|
self.pages.append(curPage)
|
||||||
|
|
||||||
|
"""!@brief Parses self.rawData into Section objects and metadata
|
||||||
|
Assumes the raw data is preprocessed, so it parses it using set rules instead of guessing line attributes
|
||||||
|
@return None
|
||||||
|
"""
|
||||||
|
def initPreprocessed(self):
|
||||||
|
pass
|
||||||
|
|
||||||
"""!@brief Parses self.rawData into Section objects and metadata
|
"""!@brief Parses self.rawData into Section objects and metadata
|
||||||
@return None
|
@return None
|
||||||
@ -404,7 +414,7 @@ class Song:
|
|||||||
# Get header on the first line
|
# Get header on the first line
|
||||||
delimiterIndex = parseData.find("]\r\n")
|
delimiterIndex = parseData.find("]\r\n")
|
||||||
if delimiterIndex == -1:
|
if delimiterIndex == -1:
|
||||||
print("Cannot parse input file, delimitor did not match '[<sectionName>]'")
|
print("Cannot parse input file, delimiter did not match '[<sectionName>]'")
|
||||||
return
|
return
|
||||||
# Skip the ']\r\n' characters
|
# Skip the ']\r\n' characters
|
||||||
thisSection.header = parseData[:delimiterIndex+3]
|
thisSection.header = parseData[:delimiterIndex+3]
|
||||||
|
@ -27,6 +27,7 @@ import os
|
|||||||
def initSong(filePath):
|
def initSong(filePath):
|
||||||
thisSong = lib.dataStructures.Song()
|
thisSong = lib.dataStructures.Song()
|
||||||
thisSong.inputFile = filePath
|
thisSong.inputFile = filePath
|
||||||
|
thisSong.fileExtension = filePath[filePath.rfind('.')+1:]
|
||||||
# set base folder name - depending on selected outputs the output folder name changes
|
# set base folder name - depending on selected outputs the output folder name changes
|
||||||
thisSong.outputLocation = filePath[:filePath.rfind('.')]
|
thisSong.outputLocation = filePath[:filePath.rfind('.')]
|
||||||
# title is just the name of the .txt file
|
# title is just the name of the .txt file
|
||||||
@ -71,11 +72,11 @@ def getSongObjects():
|
|||||||
# get all files we can find, then filter on supported extensions
|
# get all files we can find, then filter on supported extensions
|
||||||
for inputFolder in configObj['inputfolders'].split(','):
|
for inputFolder in configObj['inputfolders'].split(','):
|
||||||
for filePath in walkDirectory(inputFolder, recursionDepth):
|
for filePath in walkDirectory(inputFolder, recursionDepth):
|
||||||
if(filePath[filePath.rfind('.'):] in configObj['supportedextensions']):
|
if ((filePath[filePath.find('.'):] == ".txt" ) and configObj['readtxt']) or ((filePath[filePath.find('.'):] == ".rawtxt" ) and configObj['readraw']):
|
||||||
#print("Found .txt file '{}'".format(filePath))
|
#print("Found supported file '{}'".format(filePath))
|
||||||
txtFileLocations.append(filePath)
|
txtFileLocations.append(filePath)
|
||||||
#else:
|
#else:
|
||||||
#print("Skipping file '{}' for it is not a .txt file".format(filePath))
|
#print("Skipping file '{}' for it is not a supported file".format(filePath))
|
||||||
# create list of Song objects
|
# create list of Song objects
|
||||||
while(txtFileLocations):
|
while(txtFileLocations):
|
||||||
filePath = txtFileLocations.pop()
|
filePath = txtFileLocations.pop()
|
||||||
|
60
main.py
60
main.py
@ -26,37 +26,63 @@ import lib.initSongs
|
|||||||
import lib.transpose
|
import lib.transpose
|
||||||
import lib.config
|
import lib.config
|
||||||
import output2img
|
import output2img
|
||||||
|
import output2txt
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# Init config file
|
# Init config file
|
||||||
lib.config.initConfig()
|
lib.config.initConfig()
|
||||||
# Init Song objects for all songs with compatible inputs
|
# Init Song objects for all songs with compatible inputs
|
||||||
songs = lib.initSongs.getSongObjects()
|
songs = lib.initSongs.getSongObjects()
|
||||||
|
# Get what programs we are going to run
|
||||||
|
configObj = lib.config.config['options']
|
||||||
|
exportToImg = configObj['exporttoimg']
|
||||||
|
exportToTxt = configObj['exporttotxt']
|
||||||
|
exportToRaw = configObj['exporttoraw']
|
||||||
|
|
||||||
# Convert all songs into sections
|
# Convert all songs into sections
|
||||||
for song in songs:
|
for song in songs:
|
||||||
print("Start parsing of file '{}'...".format(song.inputFile))
|
print("Start parsing of file '{}'...".format(song.inputFile))
|
||||||
# Initialise internal data structures
|
# Initialise internal data structures
|
||||||
song.initSections()
|
if song.fileExtension == 'txt':
|
||||||
|
song.initSections()
|
||||||
|
elif song.fileExtension == 'raw':
|
||||||
|
pass#song.initPreprocessed()
|
||||||
|
# If input is .raw output. If output to raw is set, overwrite itself
|
||||||
|
# ready quickly using rules
|
||||||
if not song.isParsed:
|
if not song.isParsed:
|
||||||
print("Song was not initialized correctly. Skipping...")
|
print("Song was not initialized correctly. Skipping...")
|
||||||
continue
|
continue
|
||||||
# Fit all sections on each page, resizes down if it does not fit on width
|
|
||||||
song.fitSectionsByWidth()
|
if exportToTxt:
|
||||||
# Prerender: calculate Pages, and move sections into Pages
|
# Create subdirectory where we will output our images
|
||||||
song.sectionsToPages()
|
targetDirectory = song.outputLocation + "-txt"
|
||||||
# Optimalisation: try to fill whitespace
|
print("Successfully parsed file. Writing output to '{}'\n".format(targetDirectory))
|
||||||
while song.canFillWhitespace():
|
# Write out metadata and sections, as many as can fit on one page
|
||||||
print("Resizing down to fit whitespace more efficiently")
|
output2txt.outputToTxt(targetDirectory, False, song)
|
||||||
song.resizeAllSections(-1)
|
if exportToRaw:
|
||||||
|
# Create subdirectory where we will output our images
|
||||||
|
targetDirectory = song.outputLocation + "-txt"
|
||||||
|
print("Successfully parsed file. Writing output to '{}'\n".format(targetDirectory))
|
||||||
|
# Write out metadata and sections, as many as can fit on one page
|
||||||
|
output2txt.outputToTxt(targetDirectory, True, song)
|
||||||
|
if exportToImg:
|
||||||
|
# Fit all sections on each page, resizes down if it does not fit on width
|
||||||
|
song.fitSectionsByWidth()
|
||||||
|
# Prerender: calculate Pages, and move sections into Pages
|
||||||
song.sectionsToPages()
|
song.sectionsToPages()
|
||||||
# Optimalisation: increase font size as long as the amount of pages does not increase or we cause an overflow on width
|
# Optimalisation: try to fill whitespace
|
||||||
song.increaseWhileSameAmountOfPages()
|
while song.canFillWhitespace():
|
||||||
# Parse as PNG a4
|
print("Resizing down to fit whitespace more efficiently")
|
||||||
# Create subdirectory where we will output our images
|
song.resizeAllSections(-1)
|
||||||
targetDirectory = song.outputLocation + "-a4-png"
|
song.sectionsToPages()
|
||||||
print("Successfully parsed file. Writing output to '{}'\n".format(targetDirectory))
|
# Optimalisation: increase font size as long as the amount of pages does not increase or we cause an overflow on width
|
||||||
# Write out metadata and sections, as many as can fit on one page
|
song.increaseWhileSameAmountOfPages()
|
||||||
output2img.outputToImage(targetDirectory, song)
|
# Parse as PNG a4
|
||||||
|
# Create subdirectory where we will output our images
|
||||||
|
targetDirectory = song.outputLocation + "-a4-png"
|
||||||
|
print("Successfully parsed file. Writing output to '{}'\n".format(targetDirectory))
|
||||||
|
# Write out metadata and sections, as many as can fit on one page
|
||||||
|
output2img.outputToImage(targetDirectory, song)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
@ -86,7 +86,7 @@ def outputToImage(folderLocation, songObj):
|
|||||||
# Margin between each section
|
# Margin between each section
|
||||||
currentHeight += songObj.topMargin
|
currentHeight += songObj.topMargin
|
||||||
# Got all sections in the page, so write it
|
# Got all sections in the page, so write it
|
||||||
outputLocation = folderLocation + "/" + str(imageNumber) + ".png"
|
outputLocation = folderLocation + "/" + songObj.title + '-' + str(imageNumber) + ".png"
|
||||||
a4image.save(outputLocation)
|
a4image.save(outputLocation)
|
||||||
a4image = Image.new('RGB',(songObj.imageWidth, songObj.imageHeight),(songObj.backgroundColour))
|
a4image = Image.new('RGB',(songObj.imageWidth, songObj.imageHeight),(songObj.backgroundColour))
|
||||||
draw = ImageDraw.Draw(a4image)
|
draw = ImageDraw.Draw(a4image)
|
||||||
|
95
output2txt.py
Normal file
95
output2txt.py
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
##
|
||||||
|
# @file output2img.py
|
||||||
|
#
|
||||||
|
# @brief This program converts the internal data structure to an image file
|
||||||
|
#
|
||||||
|
# @section description Description
|
||||||
|
# Generates PNG images of a specific dimension (currently A4) of tablature data
|
||||||
|
# Dynamically resizes specific sections to maximize using the entire paper (and avoid awkward page flips)
|
||||||
|
#
|
||||||
|
# @section notes Notes
|
||||||
|
# -
|
||||||
|
#
|
||||||
|
# @section todo TODO
|
||||||
|
# - Various prints should be printed at specific log levels, to easily switch between debug, info or warnings only
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
"""!@brief Exports the song object to a txt file
|
||||||
|
Perfect to use as source file for any program which requires
|
||||||
|
tabs as input, due to the predictable layout of
|
||||||
|
metadata
|
||||||
|
[section title]
|
||||||
|
<non-lyric line>
|
||||||
|
<lyric line>
|
||||||
|
...
|
||||||
|
@param folderLocation path to where we want the text file
|
||||||
|
@param printRaw if set, prints empty lines as well and saves as .raw
|
||||||
|
if false, will print in a more readable format
|
||||||
|
@param songObj lib.dataStructures.Song object
|
||||||
|
@return None
|
||||||
|
"""
|
||||||
|
def outputToTxt(folderLocation, printRaw, songObj):
|
||||||
|
# Create target Directory if doesn't exist
|
||||||
|
if not os.path.exists(folderLocation):
|
||||||
|
os.mkdir(folderLocation)
|
||||||
|
print("Directory " , folderLocation , " Created ")
|
||||||
|
#else:
|
||||||
|
#print("Directory " , folderLocation , " already exists")
|
||||||
|
|
||||||
|
output = ""
|
||||||
|
# Write metadata
|
||||||
|
for line in songObj.metadata.split('\n'):
|
||||||
|
# remove any unwanted characters from metadata
|
||||||
|
line = line.rstrip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
#print("meta line '{}'".format(line))
|
||||||
|
output += line + '\r\n'
|
||||||
|
# If exporting raw, do not include the whitespace between metadata and sections
|
||||||
|
if not printRaw:
|
||||||
|
output += '\r\n'
|
||||||
|
# Draw all pages
|
||||||
|
for section in songObj.sections:
|
||||||
|
lineIterator = 0
|
||||||
|
amountOfLines = len(section.lyrics)
|
||||||
|
if (amountOfLines != len(section.tablatures)):
|
||||||
|
print("Cannot write this section to file, since it was not processed correctly. There are {} tablature lines and {} lyric lines. Aborting...".format(len(section.chords), amountOfLines))
|
||||||
|
return
|
||||||
|
# write section title
|
||||||
|
output += section.header.rstrip() + '\r\n'
|
||||||
|
# Write each line tablature&lyric data
|
||||||
|
while lineIterator < amountOfLines:
|
||||||
|
tabline = section.tablatures[lineIterator].rstrip()
|
||||||
|
lyricline = section.lyrics[lineIterator].rstrip()
|
||||||
|
if printRaw or len(tabline):
|
||||||
|
output += tabline + '\r\n'
|
||||||
|
if printRaw or len(lyricline):
|
||||||
|
output += lyricline + '\r\n'
|
||||||
|
lineIterator += 1
|
||||||
|
# If exporting raw, do not include the whitespace between sections
|
||||||
|
if not printRaw:
|
||||||
|
output += '\r\n'
|
||||||
|
# Finished, so print some trailing endlines
|
||||||
|
outputLocation = ""
|
||||||
|
if not printRaw:
|
||||||
|
output += '\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n'
|
||||||
|
outputLocation = folderLocation + "/" + songObj.title + ".txt"
|
||||||
|
else:
|
||||||
|
outputLocation = folderLocation + "/" + songObj.title + ".rawtxt"
|
||||||
|
with open(outputLocation, "w") as fileOut:
|
||||||
|
fileOut.write(output)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user