summaryrefslogtreecommitdiff
path: root/support/pmxchords/scripts
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/pmxchords/scripts
Initial commit
Diffstat (limited to 'support/pmxchords/scripts')
-rw-r--r--support/pmxchords/scripts/ChordsTr.lua124
-rwxr-xr-xsupport/pmxchords/scripts/pmxchords.lua244
2 files changed, 368 insertions, 0 deletions
diff --git a/support/pmxchords/scripts/ChordsTr.lua b/support/pmxchords/scripts/ChordsTr.lua
new file mode 100644
index 0000000000..737c9ce642
--- /dev/null
+++ b/support/pmxchords/scripts/ChordsTr.lua
@@ -0,0 +1,124 @@
+--[[
+ (c) Copyright 2013 Ondrej Fafejta <fafejtao@gmail.com>
+ https://github.com/fafejtao/pmxChords
+]] --
+--
+-- Chords transposition class
+--
+ChordsTr = {} -- the table representing the class, which will double as the metatable for the instances
+ChordsTr.__index = ChordsTr -- failed table lookups on the instances should fallback to the class table, to get methods
+setmetatable(ChordsTr,
+ {
+ __call = function(cls, ...)
+ return cls.new(...)
+ end,
+ })
+
+--
+-- global constants
+--
+ChordsTr.CHORDS_BEGIN = "\\ch."
+ChordsTr.CHORDS_END = ".\\"
+ChordsTr.BASE_CHORDS_ARR = {
+ -- lua is indexing from 1 I need from 0 ...
+ [0] = "C",
+ [1] = "Cs",
+ [2] = "D",
+ [3] = "Ef",
+ [4] = "E",
+ [5] = "F",
+ [6] = "Fs",
+ [7] = "G",
+ [8] = "Af",
+ [9] = "A",
+ [10] = "Bf",
+ [11] = "B"
+}
+
+--
+-- Create instance of chords transposition class
+-- params iSig input signature e.g. -1 - F major
+-- oSig output signature e.g. 2 - D major
+--
+function ChordsTr.new(iSig, oSig)
+ local self = setmetatable({}, ChordsTr)
+ self.trMap = ChordsTr.__createTransposeMap(oSig - iSig)
+ return self
+end
+
+--
+-- Core of transposition.
+-- Prepare map of transposed chords.
+-- Key is input base chord and value is output base chord.
+--
+function ChordsTr.__createTransposeMap(sigDiff)
+ local size = 12
+ local halfTones = (sigDiff * 7) % size
+ if (halfTones < 0) then
+ halfTones = halfTones + size
+ end
+
+ -- generate transpose map of base chords ...
+ local res = {}
+ local i
+ for i = 0, size - 1, 1 do
+ local key = ChordsTr.BASE_CHORDS_ARR[i]
+ local trIdx = (i + halfTones) % size;
+ res[key] = ChordsTr.BASE_CHORDS_ARR[trIdx]
+ end
+ return res
+end
+
+--
+-- transpose one line.
+-- Find each chords in line and transpose it into output signature.
+-- return transposed line.
+--
+function ChordsTr:lineTranspose(line)
+ local pos = 0;
+ local trLine = "" -- transposed line (result)
+ while true do
+ local i = line:find(ChordsTr.CHORDS_BEGIN, pos)
+ if i == nil then
+ trLine = trLine .. line:sub(pos)
+ break
+ end
+ i = i + 3 -- move after \ch.
+ trLine = trLine .. line:sub(pos, i) -- append non chord string into result
+ pos = line:find(ChordsTr.CHORDS_END, i)
+ if pos == nil then -- TODO check correctly end of chords ... e.g. check wrong line (first chord is not finished correctly): \ch.C. c \ch.G7.\ b
+ io.stderr:write("End of chords was not found!\n")
+ os.exit(-1)
+ end
+ local chord = line:sub(i + 1, pos - 1)
+ trLine = trLine .. self:doTranspose(chord)
+ end
+ return trLine
+end
+
+--
+-- do transpose of one chord.
+--
+function ChordsTr:doTranspose(chord)
+ local base, alt = ChordsTr.splitChord(chord)
+ return self.trMap[base] .. alt
+end
+
+--
+-- Split chord into two part: base chord and chord alteration.
+-- e.g. Fsm7 - base chord is Fs, alteration is m7
+--
+function ChordsTr.splitChord(chord)
+ if (chord.len == 1) then
+ return chord, ""
+ end
+ local secondChar = chord:sub(2, 2)
+ if (secondChar == 's' or secondChar == 'f') then
+ return chord:sub(1, 2), chord:sub(3)
+ else
+ return chord:sub(1, 1), chord:sub(2)
+ end
+end
+--
+-- end of ChordsTr class
+--
diff --git a/support/pmxchords/scripts/pmxchords.lua b/support/pmxchords/scripts/pmxchords.lua
new file mode 100755
index 0000000000..58179a355a
--- /dev/null
+++ b/support/pmxchords/scripts/pmxchords.lua
@@ -0,0 +1,244 @@
+#! /usr/bin/env texlua
+--
+-- global constants
+--
+VERSION = "0.9.3"
+FILE_SUFFIX = "_chtr"
+PMX_CMD = "pmxab"
+
+--[[
+ pmxchords.lua: transpose chords (\ch.C.\) and process pmxab
+
+ (c) Copyright 2013 Ondrej Fafejta <fafejtao@gmail.com>
+ https://github.com/fafejtao/pmxChords
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+--]]
+
+--[[
+ ChangeLog:
+ version 0.9.3 2014-04-21 - fixed transposition problem:
+ If song began in E-minor and signature is changed in half of song to A-minor
+ transposition must be omitted.
+
+ version 0.9.2 2013-12-13 - fixed: stop processing when pmxab fail.
+ - When pmxab is called generated file pmxaerr.dat is not removed.
+
+ version 0.9.1 2013-12-09 - added some options. Improved processing.
+
+ version 0.9 2013-12-05 - rewritted code from perl to lua...
+--]]
+
+function usage()
+ print("Usage: [texlua] pmxchords.lua basename[.pmx] ")
+ print(" 1. transpose chords into new file basename" .. FILE_SUFFIX .. ".pmx")
+ print(" 2. call pmxab on transposed file basename" .. FILE_SUFFIX .. ".pmx")
+ print(" 3. rename basename" .. FILE_SUFFIX .. ".tex to basename.tex")
+ print("options: -v version")
+ print(" -h help")
+ print(" -s stop after fist step. e.g. generate only transposed basename" .. FILE_SUFFIX .. ".pmx file")
+end
+
+function whoami()
+ print("This is pmxchords.lua version " .. VERSION)
+end
+
+whoami()
+if #arg == 0 then
+ usage()
+ os.exit(0)
+end
+
+kpse.set_program_name("luatex")
+require"ChordsTr"
+
+function parseInputSignature(line)
+ -- parse input signature from pmx digits line. Only one line digits format is supported!
+ -- e.g.
+ -- 1 1 2 4 2 4 0 -1
+ -- signature is -1 : F major
+ local res = string.match(line, "^%s*[0-9]+%s+[0-9]+%s+[0-9]+%s+[0-9]+%s+[0-9]+%s+[0-9]+%s+[0-9]+%s+([%+%-]?[0-9]+)%s*$")
+ return tonumber(res)
+end
+
+function parseOutputSignature(line)
+ -- parse output signature from pmx
+ -- e.g.
+ -- K-2+2
+ -- output signature is +2 : D major
+ local res = string.match(line, "^K[%+%-]?[0-9]+([%+%-]?[0-9]+)")
+ return tonumber(res)
+end
+
+--
+-- Strip comments from line
+-- Comments begin with % character
+--
+function lineWithoutComment(line)
+ local i = string.find(line, "%%")
+ if (i == nil) then
+ return line
+ end
+ return line:sub(0, i - 1)
+end
+
+--
+-- Function checks only if the line contains \ch. string.
+-- It does not check correctly if notes already started. It checks only if chords started..
+-- Scenario bellow will fail!
+-- d44 f a r / % first line does not contain chords symbol
+-- % signature is changed
+-- K+0+0
+-- \ch.C.\ c44 e g r4 /
+--
+function checkChordsAlreadyStarted(line)
+ local i = string.find(lineWithoutComment(line), ChordsTr.CHORDS_BEGIN)
+ if (i ~= nil) then
+ return true
+ end
+ return false;
+end
+
+function handleErr(msg)
+ io.stderr:write(msg .. "\n")
+ os.exit(2)
+end
+
+--
+-- get file name without .pmx suffix
+--
+function getBaseFileName(fileName)
+ if fileName ~= "" and string.sub(fileName, -4, -1) == ".pmx" then
+ return string.sub(fileName, 1, -5)
+ else
+ return fileName
+ end
+end
+
+--
+-- Make chords transposition. Generates new pmx file with transposed chords.
+--
+-- param baseName input file name without .pmx suffix
+-- return outputBaseName transposed pmx file name without .pmx suffix
+--
+function makeChordsTransposition(baseName)
+ local inputFileName = baseName .. ".pmx"
+ local inputFile = io.open(inputFileName, "r")
+ if (inputFile == nil) then
+ handleErr("File does not exist: " .. inputFileName)
+ end
+
+ local outputBaseName = baseName .. FILE_SUFFIX -- output base name without suffix. Used for renaming generated .tex file ...
+ local outputFileName = outputBaseName .. ".pmx"
+ local outputFile = io.open(outputFileName, "w")
+ if (outputFile == nil) then
+ handleErr("Can not create temporary file: " .. outputFileName)
+ end
+
+ --
+ -- transpose chords from inputFile into outputFile
+ --
+ -- input and output signature will be parsed from input pmx file
+ --
+ local iSig = nil -- input signature
+ local chTr = nil -- chords transposition class
+
+ -- If song began in E-minor and signature is changed in half of song to A-minor
+ -- transposition must be omitted.
+ local chordsAlreadyStarted = false;
+
+ for line in inputFile:lines() do
+ if (chTr ~= nil) then
+ outputFile:write(chTr:lineTranspose(line) .. "\n")
+ else
+ outputFile:write(line .. "\n")
+ end
+ if (iSig == nil) then
+ iSig = parseInputSignature(line)
+ if (iSig ~= nil) then
+ io.stderr:write("Chords: input signature detected: " .. iSig .. "\n")
+ end
+ elseif (chTr == nil and not chordsAlreadyStarted) then
+ local oSig = parseOutputSignature(line)
+ if (oSig ~= nil) then
+ io.stderr:write("Chords: output signature detected: " .. oSig .. "\n")
+ chTr = ChordsTr(iSig, oSig)
+ elseif (checkChordsAlreadyStarted(line)) then
+ io.stderr:write("Chords: notes already started. Chords transposition is omitted.\n")
+ chordsAlreadyStarted = true;
+ end
+ end
+ end
+ inputFile:close()
+ outputFile:close()
+
+ return outputBaseName
+end
+
+--
+-- Make pmxab process.
+-- param baseName input file name without .pmx suffix
+-- param outputBaseName transposed pmx file name without .pmx suffix
+--
+function pmxabProcess(baseName, outputBaseName)
+ --
+ -- call pmxab to generate .tex file
+ --
+ os.execute(PMX_CMD .. " " .. outputBaseName)
+ -- check error
+ local pmxaerr = io.open("pmxaerr.dat", "r")
+ if (not pmxaerr) then
+ handleErr("No log file.")
+ end
+ local linebuf = pmxaerr:read()
+ local err = tonumber(linebuf)
+ pmxaerr:close()
+ if (err ~= 0) then
+ handleErr("PMX process fail! " .. outputBaseName .. ".pmx")
+ end
+
+ os.rename(outputBaseName .. ".tex", baseName .. ".tex")
+
+ -- remove temporary files
+ os.remove(outputBaseName .. ".pmx")
+ os.remove(outputBaseName .. ".pml")
+end
+
+narg = 1
+onlyTranspose = false
+
+repeat
+ local this_arg = arg[narg]
+ if this_arg == "-v" then
+ os.exit(0)
+ elseif this_arg == "-h" then
+ usage()
+ os.exit(0)
+ elseif this_arg == "-s" then
+ onlyTranspose = true
+ else
+ local baseName = getBaseFileName(this_arg)
+ local outputBaseName = makeChordsTransposition(baseName)
+
+ if (onlyTranspose) then
+ os.exit(0)
+ end
+ pmxabProcess(baseName, outputBaseName)
+ end
+
+ narg = narg + 1
+until narg > #arg
+