Created
January 4, 2012 16:12
-
-
Save fredbogg/1560732 to your computer and use it in GitHub Desktop.
ABCplayerCodea v0.1.9
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ABCMusic = class() | |
function ABCMusic:init(_ABCTune,LOOP,DEBUG,DUMP) | |
self.DEBUG = DEBUG | |
if self.DEBUG == nil then self.DEBUG = false end | |
if DUMP == nil then DUMP = false end | |
if _ABCTune == nil then | |
print("No tune provided. Use ABCMusic(tunename)") | |
end | |
self.LOOP = LOOP | |
--watch("self.tempo") | |
self.soundTablePointer=1 | |
self.soundTable = {} | |
self.timeElapsedSinceLastNote = 0 | |
self.duration = 1 | |
--tempDuration = 1 | |
self.tempo = 240 -- if no tempo is specified in the file, use this | |
self.noteLength = (1/8) -- if no default note length is specified in the file, use this | |
-- This is the cycle of fifths. It helps us figure out which accidentals to use | |
-- for a given key. | |
cycleOfFifths = {"Cb","Gb","Db","Ab","Eb","Bb","F", | |
"C","G","D","A","E","B","F#","C#","G#","D#","A#"} | |
-- These are the names of the notes and their (trial and error) equivalent seed numbers | |
-- for the sound() function. The , or ' tells us which octave it is. | |
-- Further mapping of the seed address space would improve tonal quality. | |
-- d' is highest note, D, is lowest, but 4 octaves are listed in case the tunes need them. | |
notes = { | |
["C,"]=85,["D,"]=90,["E,"]=274,["F,"]=487,["^F,"]=192,["_G,"]=192,["G,"]=552, | |
["^G,"]=376,["_A,"]=376,["A,"]=191,["^A,"]=384,["_B,"]=384,["B,"]=118,["_C,"]=118, | |
["C"]=85,["^C"]=662,["_D"]=662,["D"]=321,["^D"]=224,["_E"]=224,["E"]=48,["F"]=63,["^F"]=451, | |
["_G"]=451,["G"]=60,["^G"]=687,["_A"]=687,["A"]=68,["^A"]=522,["_B"]=522,["B"]=11,["_C"]=11, | |
["c"]=84,["^c"]=268,["_d"]=268,["d"]=96,["^d"]=280,["_e"]=280,["e"]=194,["f"]=993, | |
["^f"]=386,["_g"]=386,["g"]=1372,["^g"]=857,["_a"]=857,["a"]=1028,["^a"]=1481,["_b"]=1481, | |
["b"]=774,["_c"]=774,["c'"]=2742,["d'"]=737,["e'"]=1176,["f'"]=198,["g'"]=342, | |
["a'"]=582,["b'"]=422} | |
-- These are the 'Guitar chords' and the notes making up each one. | |
-- Further work needed to expand the range of chords known. | |
chordList = { | |
["C"]={"C","E","G"}, | |
["C7"]={"C","E","G","^A"}, | |
["D"]={"D","^F","A"}, | |
["D7"]={"D","^F","A","c"}, | |
["Dm"]={"D","F","A"}, | |
["Dm7"]={"D","F","A","c"}, | |
["E"]={"E","^G","B"}, | |
["Em"]={"E","G","B"}, | |
["F"]={"F","A","c"}, | |
["G"]={"G","B","D"}, | |
["G7"]={"G","B","D","F"}, | |
["A"]={"A","^C","E"}, | |
["Am"]={"A","C","E"}, | |
["Am7"]={"A","C","E","G"}, | |
["Bb"]={"_B","D","F"}, | |
["Bm"]={"B","D","^F"}} | |
-- Print the raw ABC tune for debugging | |
if DEBUG then print(_ABCtune) end | |
-- This is a table of patterns that we use to match against the ABC tune. | |
-- We use these to find the next, biggest meaningful bit of the tune. | |
-- Lua patterns is like RegEx, in that we can specify parts of the match to be captured with | |
-- sets of parentheses. | |
-- Not all tokens have been implemented yet, but at least we understand | |
-- musically what is going on. | |
tokenList = { | |
TOKEN_REFERENCE = "^X:%s?(.-)\n", | |
TOKEN_TITLE = "^T:%s?(.-)\n", | |
TOKEN_KEY = "%[?K:%s?(%a[b#]?)%s?(%a*)[%]\n]", -- matches optional inline [K:...] | |
TOKEN_METRE = "%[?M:%s?(.-)[%]\n]", | |
TOKEN_DEFAULT_NOTE_LENGTH = "%[?L:%s?(%d-)%/(%d-)[%]\n]", | |
TOKEN_TEMPO = "%[?Q:%s?(%d*%/?%d*)%s?=?%s?(%d*)[%]\n]", -- matches deprecated, see standard | |
TOKEN_CHORD_DURATION = '%[([%^_=]?[a-gA-G][,\']?%d*/?%d?.-)%]', | |
TOKEN_GUITAR_CHORD = '"(%a+%d?)"', | |
TOKEN_START_REPEAT = '|:', | |
TOKEN_END_REPEAT = ':|', | |
TOKEN_END_REPEAT_START = ":|?:", | |
TOKEN_NUMBERED_REPEAT_START = "[|%[]%d", | |
TOKEN_NOTE_DURATION = '([%^_=]?[a-gA-G][,\']?)(%d*/?%d?)', | |
TOKEN_PREV_DOTTED_NEXT_HALVED = ">", | |
TOKEN_PREV_HALVED_NEXT_DOTTED = "<", | |
TOKEN_SPACE = "%s", | |
TOKEN_BARLINE = "|", | |
TOKEN_DOUBLE_BARLINE = "||", | |
TOKEN_THIN_THICK_BARLINE = "|%]", | |
TOKEN_NEWLINE = "\n", | |
TOKEN_DOUBLE_FLAT = "__", | |
TOKEN_DOUBLE_SHARP = "%^^", | |
TOKEN_ACCIDENTAL = "[_=\^]", | |
TOKEN_REST_DURATION = "(z)(%d?/?%d?)", | |
TOKEN_REST_MULTIMEASURE = "(Z)(%d?)", | |
TOKEN_TRILL = "~", | |
TOKEN_START_SLUR = "%(", | |
TOKEN_END_SLUR = "%)", | |
TOKEN_STACATO = "%.", | |
TOKEN_TUPLET = "%(([1-9])([%^_=]?[a-gA-G][,']?[%^_=]?[a-gA-G]?[,']?[%^_=]?[a-gA-G]?[,']?)", | |
TOKEN_TIE = "([%^_=]?[a-gA-G][,\']?)%d?/?%d?%-.*(%1%d?/?%d?)", | |
TOKEN_MISC_FIELD = "^[(ABCDEFGHIJNOPRSUVWYZmrsw)]:(.-)\n"} -- no overlap with | |
-- already specified fields like METRE or KEY | |
self:parseTune(_ABCTune) | |
self:createSoundTable() | |
if DUMP then | |
dump(self.soundTable) -- for debugging | |
end | |
end | |
function ABCMusic:parseTune(destructableABCtune) | |
self.destructableABCtune = destructableABCtune | |
-- Go through each token and find the first match in the tune. Use the biggest lowest | |
-- starting index and then discard the characters that matched. | |
local lastLongest = 0 | |
self.parsedTune = {} | |
-- We create a copy of the tune to whittle away at. | |
--destructableABCtune = ABCtune | |
local lastToken | |
local lastTokenMatch | |
local captureFinal1 | |
local captureFinal2 | |
-- Iterate through the tune until none left | |
while true do | |
-- Loop through all tokens to see which one matches the start of the whittled tune. | |
for key, value in pairs(tokenList) do | |
local token = value | |
-- Find the start and end index of the token match, plus record what was in the | |
-- pattern capture parentheses. I pulled out a max two captures for each match, which | |
-- seemed adequate. | |
local startIndex | |
local endIndex | |
local capture1 | |
local capture2 | |
startIndex, endIndex, capture1, capture2 = string.find(self.destructableABCtune, token) | |
if startIndex == nil then startIndex = 0 end | |
if endIndex == nil then endIndex = 0 end | |
-- Get the actual match from the tune | |
local tokenMatch = string.sub(self.destructableABCtune,startIndex, endIndex) | |
-- Take the one that matches the start of the whittled tune. | |
if startIndex == 1 then | |
-- In case there are two possible matches, then take the biggest one. | |
-- This shouldn't happen if the token patterns are right. | |
if endIndex > lastLongest then | |
lastLongest = endIndex | |
lastToken = key | |
lastTokenMatch = tokenMatch | |
captureFinal1 = capture1 | |
captureFinal2 = capture2 | |
end | |
end | |
end | |
if lastTokenMatch == "" then | |
print("No match found for character ".. string.sub(self.destructableABCtune,1,1) ) | |
print("Remaining characters: ".. #self.destructableABCtune) | |
-- set the whittler to trim the strange character away | |
lastLongest = 1 | |
else | |
-- Build a table containing the parsed tune. | |
-- Due to iterative delays in the print function needed for debugging, we will use | |
-- a 4-strided list for quicker printing it later with table.concat(). | |
table.insert(self.parsedTune,lastToken) | |
table.insert(self.parsedTune,lastTokenMatch) | |
-- Where no captures occurred, we will just fill the table item with 1, | |
-- which will be the default duration of a note that has no length modifier. | |
if captureFinal1 == "" or captureFinal1 == nil then captureFinal1 = 1 end | |
if captureFinal2 == "" or captureFinal2 == nil then captureFinal2 = 1 end | |
table.insert(self.parsedTune,captureFinal1) | |
table.insert(self.parsedTune,captureFinal2) | |
end | |
-- Whittle off the match | |
self.destructableABCtune = string.sub(self.destructableABCtune, lastLongest + 1) | |
-- Stop the loop once we have no tune left to parse | |
if string.len(self.destructableABCtune) == 0 then | |
break | |
end | |
-- Clear the variables | |
lastLongest = 0 | |
lastToken = "" | |
lastTokenMatch = "" | |
end | |
-- Go back over the tune to replace ties within chords with their proper durations | |
-- Find next chord with tie and the following note that matches the tied note | |
local pointer = 1 | |
local note | |
local dur | |
local nextRawMatch | |
local endTieNoteStart | |
local endTieNoteEnd | |
local endTieDur | |
while pointer <= #self.parsedTune do | |
if self.parsedTune[pointer] == "TOKEN_CHORD_DURATION" then | |
local rawMatch = self.parsedTune[pointer + 1] | |
local tiePos = string.find(rawMatch,"-") | |
if tiePos ~= nil then | |
note, dur = string.match(rawMatch,tokenList["TOKEN_NOTE_DURATION"].."-") | |
--print("Note is ".. note) | |
if dur == nil or dur == "" then dur = 1 end | |
--print("dur is "..dur) | |
-- print("fund tie chord " .. rawMatch) | |
-- Look ahead | |
local nextPointer = pointer | |
while nextPointer <= #self.parsedTune do | |
-- print("skipping alog") | |
nextPointer = nextPointer + 4 | |
if self.parsedTune[nextPointer] == "TOKEN_NOTE_DURATION" then | |
local endTieNote = self.parsedTune[nextPointer + 2] | |
local endTieDur = self.parsedTune[nextPointer + 3] | |
-- print("found next note") | |
if note == endTieNote then | |
-- print("matched next chord with tie") | |
-- delete that record | |
local z | |
for z = 1, 4 do | |
table.remove(self.parsedTune,nextPointer) | |
end | |
break | |
end | |
end | |
if self.parsedTune[nextPointer] == "TOKEN_CHORD_DURATION" then | |
local notePattern = note .. '[_%^=]?(%d*/?%d?)' | |
nextRawMatch = self.parsedTune[nextPointer + 1] | |
endTieNoteStart, endTieNoteEnd, endTieDur = string.find(nextRawMatch, notePattern) | |
if endTieDur == nil or endTieDur == ""then endTieDur = 1 end | |
end | |
if endTieNoteStart ~= nil then | |
-- delete that bit | |
nextRawMatch = string.sub(nextRawMatch,1,endTieNoteStart-1).. | |
string.sub(nextRawMatch,endTieNoteEnd+1) | |
self.parsedTune[nextPointer + 1] = nextRawMatch | |
endTieNoteStart = nil | |
pointer = pointer + 4 | |
break | |
end | |
end | |
-- add durations | |
dur = tonumber(dur) + tonumber(endTieDur) | |
-- replace the - with a duration made from the sum of the first and second notes | |
rawMatch = string.sub(rawMatch,1,tiePos-1)..dur.. | |
string.sub(rawMatch,tiePos+1) | |
self.parsedTune[(pointer - 4)+ 1] = rawMatch | |
end | |
end | |
pointer = pointer + 4 | |
end | |
-- For debugging purposes, print the whole parsed tune. | |
if self.DEBUG then print(table.concat(self.parsedTune,"\n")) end | |
end | |
function ABCMusic:createSoundTable() | |
-- Here we interpret the parsed tune into a table of notes to play and for how long. | |
-- The upside of an intermediate process is that there will be no parsing delays to lag | |
-- things if we are playing music in the middle of a game. It is also easier to debug! | |
-- On the other hand, ABC format allows for inline tempo or metre changes. To comply | |
-- we would need to either switch duration to seconds rather than beats, or implement another | |
-- parsing thing during playback... | |
local duration | |
local tempChord={} | |
local parsedTunePointer = 1 | |
while true do | |
if self.parsedTune[parsedTunePointer] == nil then break end | |
-- Break out our 4-strided list into the token, what it actually matched, and the | |
-- two captured values. | |
token = self.parsedTune[parsedTunePointer] | |
rawMatch = self.parsedTune[parsedTunePointer + 1] | |
value1 = self.parsedTune[parsedTunePointer + 2] | |
value2 = self.parsedTune[parsedTunePointer + 3] | |
-- Doing anything here seems to take forever. | |
-- print(token.."\n"..rawMatch.."\n"..value1.."\n"..value2) end | |
-- this is so cool: setting the key sig | |
if token == "TOKEN_KEY" then | |
if value2 == 1 then | |
self.mode = "major" | |
else | |
self.mode = value2 | |
end | |
-- search cycle for marching tonic. | |
for i = 1, #cycleOfFifths do | |
if cycleOfFifths[i] == value1 then | |
cycleOfFifthsIndex = i | |
break | |
end | |
end | |
if self.DEBUG then print("index of key of cycle is "..cycleOfFifthsIndex) end | |
if self.DEBUG then print("mode is "..self.mode) end | |
self.accidentals = "" | |
if cycleOfFifthsIndex~= nil then | |
if self.mode == "minor" then | |
cycleOfFifthsIndex = cycleOfFifthsIndex - 3 | |
end | |
if cycleOfFifthsIndex > 8 then -- if on the right hand side of circle | |
for x = 7, (cycleOfFifthsIndex - 2) do | |
self.accidentals = self.accidentals .. cycleOfFifths[x] | |
end | |
end | |
-- if the key is C major or A minor, the centre of the cycle, | |
-- no accidentals are needed. | |
if cycleOfFifthsIndex < 8 then -- if on the left hand side of circle | |
for x = 6, (cycleOfFifthsIndex - 1), -1 do | |
self.accidentals = self.accidentals .. cycleOfFifths[x] | |
end | |
end | |
if self.DEBUG then print("Looking for these sharps: " .. self.accidentals) end | |
end | |
end | |
if token == "TOKEN_TEMPO" then | |
if string.find(value1,"/") then | |
self.tempo = tonumber(value2) | |
else | |
self.tempo = tonumber(value1) | |
end | |
iparameter("ptempo", 40, 480, self.tempo) | |
end | |
if token == "TOKEN_DEFAULT_NOTE_LENGTH" then | |
noteLength = value2 | |
-- Set the tempo, eg if you wanted one quarter note or crotchet per second | |
-- you would set Q:60 and L:1/4 | |
self.tempo = self.tempo * (noteLength/4) | |
if self.DEBUG then print("internal Tempo is " .. self.tempo) end | |
end | |
if token == "TOKEN_NOTE_DURATION" then | |
duration = value2 | |
-- because the ABC standard allows /4 to mean 1/4, we fix that here | |
if string.sub(duration,1,1) == "/" then | |
duration = "1"..duration | |
end | |
if duration == "1/" then | |
duration = "1/2" | |
end | |
if string.find(duration, "/") ~= nil then | |
local numerator = tonumber(string.sub(duration,1,string.find(duration,"/")-1)) | |
local denominator = tonumber(string.sub(duration,string.find(duration,"/")+1)) | |
duration = numerator / denominator | |
end | |
-- hack for key signature | |
firstChar = string.upper(string.sub(value1,1,1)) | |
if firstChar ~= "^" and firstChar ~= "_" then | |
if string.find(self.accidentals,firstChar) ~= nil then | |
if cycleOfFifthsIndex > 8 then | |
value1 = "^" .. value1 | |
end | |
if cycleOfFifthsIndex < 8 then | |
value1 = "_" .. value1 | |
end | |
end | |
end | |
if firstChar == "=" then | |
value1 = string.sub(value1,2) | |
end | |
-- If there are chords to play at the same time, they will be in the tempChord table. | |
table.insert(tempChord,{value1, duration}) | |
table.insert(self.soundTable,tempChord) | |
tempChord = {} | |
end | |
if token == "TOKEN_REST_DURATION" then | |
duration = value2 | |
if string.sub(duration,1,1) == "/" then | |
duration = "1"..duration | |
end | |
duration = tonumber(duration) | |
table.insert(self.soundTable,{{"z", duration}}) | |
end | |
if token == "TOKEN_TIE" then | |
value1, duration1 = string.match(value1,tokenList["TOKEN_NOTE_DURATION"]) | |
value2, duration2 = string.match(value2,tokenList["TOKEN_NOTE_DURATION"]) | |
if self.DEBUG then print("val1 " .. value1.. " value2 ".. value2) end | |
if string.sub(duration1,1,1) == "/" then | |
duration1 = "1"..duration1 | |
end | |
if string.find(duration1, "/") ~= nil then | |
local numerator = tonumber(string.sub(duration1,1,string.find(duration1,"/")-1)) | |
local denominator = tonumber(string.sub(duration1,string.find(duration1,"/")+1)) | |
duration1 = numerator / denominator | |
end | |
if string.sub(duration2,1,1) == "/" then | |
duration2 = "1"..duration2 | |
end | |
if string.find(duration2, "/") ~= nil then | |
local numerator = tonumber(string.sub(duration2,1,string.find(duration2,"/")-1)) | |
local denominator = tonumber(string.sub(duration2,string.find(duration2,"/")+1)) | |
duration2 = numerator / denominator | |
end | |
if duration1 == nil or duration1 == "" then duration1 = 1 end | |
if duration2 == nil or duration2 == "" then duration2 = 1 end | |
if self.DEBUG then print("dur1 ".. duration1 .. "dur2 ".. duration2) end | |
duration = tonumber(duration1) + tonumber(duration2) | |
table.insert(self.soundTable,{{value1, duration}}) | |
end | |
if token == "TOKEN_TUPLET" then | |
-- More types of tuplets exist, up to 9, but need more work. | |
if value1 == "2" then | |
duration = 1.5 -- the 2 signals two notes in the space of three | |
-- We reprocess the notes making up the tuplet | |
for i = 1, string.len(value2) do | |
note,noteLength = string.match(value2,tokenList["TOKEN_NOTE_DURATION"],i) | |
table.insert(self.soundTable,{{note, duration}}) | |
end | |
end | |
if value1 == "3" then | |
duration = 1/3 -- the 3 signals three notes in the space of two | |
-- We reprocess the notes making up the tuplet | |
for i = 1, string.len(value2) do | |
note,noteLength = string.match(value2,tokenList["TOKEN_NOTE_DURATION"],i) | |
table.insert(self.soundTable,{{note, duration}}) | |
end | |
end | |
end | |
if token == "TOKEN_GUITAR_CHORD" then | |
-- The ABC standard leaves it up to the software how to interpret guitar chords, | |
-- but they should precede notes in the ABC tune. I'm just going with a vamp. | |
duration = 0 | |
self.tempChord = {} | |
if chordList[value1] == nil then | |
print("Chord ".. value1.. " not found in chord table.") | |
else | |
for key, value in pairs(chordList[value1]) do | |
-- This places the notes of the chord into a temporary table which will | |
-- be appended to by the next non-chord note. | |
table.insert(self.tempChord,{value, duration}) | |
end | |
end | |
end | |
if token == "TOKEN_CHORD_DURATION" then | |
-- These are arbitrary notes sounded simultaneously. If their durations are | |
-- different that could cause trouble. | |
while true do | |
-- Do this loop unless we have already whittled away the chord into notes. | |
if string.len(rawMatch) <= 1 then | |
break | |
end | |
-- Reprocess the chord into notes and durations. | |
startIndex, endIndex, note, noteDuration = | |
string.find(rawMatch,tokenList["TOKEN_NOTE_DURATION"]) | |
if noteDuration == "" or noteDuration == nil then | |
noteDuration = 1 | |
else | |
if string.find(noteDuration, "/") ~= nil then | |
if string.sub(noteDuration,1,1) == "/" then | |
noteDuration = "1"..noteDuration | |
end | |
if noteDuration == "1/" then | |
noteDuration = "1/2" | |
end | |
local numerator = | |
tonumber(string.sub(noteDuration,1,string.find(noteDuration,"/")-1)) | |
local denominator = | |
tonumber(string.sub(noteDuration,string.find(noteDuration,"/")+1)) | |
noteDuration = numerator / denominator | |
end | |
end | |
if note == nil then break end | |
-- hack for key signature | |
--print("note is ".. note) | |
firstChar = string.upper(string.sub(note,1,1)) | |
if firstChar ~= "^" and firstChar ~= "_" then | |
if string.find(self.accidentals,firstChar) ~= nil then | |
if cycleOfFifthsIndex > 8 then | |
note = "^" .. note | |
--print("added sharp to "..note) | |
end | |
if cycleOfFifthsIndex < 8 then | |
note = "_" .. note | |
-- print("added flat to "..note) | |
end | |
end | |
end | |
if firstChar == "=" then | |
note = string.sub(note,2) | |
end | |
-- This places the notes of the chord into a temporary table which will | |
-- be appended to the sound table at the end of the chord. | |
table.insert(tempChord,{note, noteDuration}) | |
-- Whittle away the chord | |
rawMatch = string.sub(rawMatch, endIndex + 1) | |
end | |
-- Append chord to sound table. | |
table.insert(self.soundTable,tempChord) | |
tempChord = {} | |
end | |
-- Move to the next token in our strided list of 4. | |
parsedTunePointer = parsedTunePointer + 4 | |
end | |
end | |
function ABCMusic:fromTheTop() | |
self.soundTablePointer = 1 | |
end | |
function ABCMusic:play() | |
-- Step through the parsed tune and decide whether to play the next bit yet. | |
if ptempo ~= nil then | |
self.tempo = ptempo | |
end | |
-- This normalises the tempo to smooth out lag between cumlative frames. Meant to be the | |
-- same idea for smoothing out animation under variable processing loads. | |
self.timeElapsedSinceLastNote = self.timeElapsedSinceLastNote + DeltaTime | |
if duration == nil then duration = 0 end | |
local framesToBeSkipped = ( 60 / ((self.tempo) / 60 ) ) * (duration/2) -- tempo = bpm, so / by 60 for bps | |
-- If there is still a tune and it's time for the next set of notes | |
if framesToBeSkipped <= (self.timeElapsedSinceLastNote * 60 ) -- multiply time by 60 to get frames | |
and self.soundTablePointer <= #self.soundTable then -- because draw() is 60 fps | |
-- Step through the set of notes nested in the sound table, finding each note and | |
-- its duration. If we had volume, we would also want to record it in the most nested | |
-- table. | |
-- The operator # gives us the number of elements in a table until a blank one - see Lua | |
-- documentation. | |
-- Luckily our table will never have holes in it, or the notes would fall through. | |
-- The sound table looks like: | |
-- 1: 1: 1: C | |
-- 2: 4 | |
-- 2: 1: E | |
-- 2: 4 | |
-- 2: etc... | |
oldTempDuration=0 | |
tempDuration = 0 | |
for i = 1, #self.soundTable[self.soundTablePointer] do | |
oldTempDuration = tempDuration | |
-- This line plays the note currently being pointed to. If it is part of a set | |
-- to be played at once, this will loop around without delay. | |
gsNoteBeingPlayed = self.soundTable[self.soundTablePointer][i][1] | |
gnNoteBeingPlayed = notes[self.soundTable[self.soundTablePointer][i][1]] | |
if gnNoteBeingPlayed ~= nil then | |
sound(SOUND_BLIT,gnNoteBeingPlayed) | |
end | |
tempDuration = tonumber(self.soundTable[self.soundTablePointer][i][2]) | |
--print("temp dur was ".. tempDuration) | |
-- Keep the shortest note duration of the set of notes to be played together, | |
-- to be used as one of the inputs for the delay until the next note. | |
if oldTempDuration ~= 0 and oldTempDuration < tempDuration then | |
tempDuration = oldTempDuration | |
end | |
end | |
-- print("shortest was " .. duration) | |
duration = tempDuration | |
-- Looping music... we need a better way to do this... | |
--print(duration) | |
if self.LOOP ~= nil and self.soundTablePointer == #self.soundTable then | |
self.soundTablePointer = 1 | |
else | |
-- Increment the pointer in our sound table. | |
self.soundTablePointer = self.soundTablePointer + 1 | |
end | |
-- Reset counters rather than going to infinity and beyond. | |
self.timeElapsedSinceLastNote = 0 | |
end | |
end | |
function ABCMusic:noteBeingPlayed() | |
return gsNoteBeingPlayed | |
end | |
-- Handy function from Pixel to only use for debugging and if the ABCtube is a line long, | |
-- 'cos it is slow. | |
-- print contents of a table, with keys sorted. | |
-- second parameter is optional, used for indenting subtables | |
function dump(t,indent) | |
local names = {} | |
if not indent then indent = "" end | |
for n,g in pairs(t) do | |
table.insert(names,n) | |
end | |
table.sort(names) | |
for i,n in pairs(names) do | |
local v = t[n] | |
if type(v) == "table" then | |
if(v==t) then -- prevent endless loop if table contains reference to itself | |
print(indent..tostring(n)..": <-") | |
else | |
print(indent..tostring(n)..":") | |
dump(v,indent.." ") | |
end | |
else | |
if type(v) == "function" then | |
print(indent..tostring(n).."()") | |
else | |
print(indent..tostring(n)..": "..tostring(v)) | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment