diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2016-07-04 23:53:06 +0200 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2016-07-04 23:53:06 +0200 |
commit | 7a9077315a799ca0d750c67685337964318ffd48 (patch) | |
tree | 7d5bf700526469f2f219f73cef63e1ca7000ee73 /applause.lua | |
parent | 613021c30d72e1df7f2ad17c4b4de7b6ad8a204f (diff) | |
download | applause2-7a9077315a799ca0d750c67685337964318ffd48.tar.gz |
added ntom() and mton() for converting from note names to MIDI numbers and vice versa
* mton() can handle MIDINoteStream values just like mtof()
* both are exposed as Stream methods as well (e.g. Stream:mton())
* MIDIVelocityStream accepts note names now as well in addition to MIDI note numbers
* added scoping to the cache used by mtof()
Diffstat (limited to 'applause.lua')
-rw-r--r-- | applause.lua | 68 |
1 files changed, 53 insertions, 15 deletions
diff --git a/applause.lua b/applause.lua index abb17f3..f43c0d3 100644 --- a/applause.lua +++ b/applause.lua @@ -1553,7 +1553,8 @@ end MIDIVelocityStream = DeriveClass(Stream) function MIDIVelocityStream:ctor(note, channel) - self.note = note + -- `note` may be a note name like "A4" + self.note = type(note) == "string" and ntom(note) or note assert(0 <= self.note and self.note <= 127, "MIDI note out of range (0 <= x <= 127)") @@ -1565,6 +1566,8 @@ end -- This is for calling from external code (e.g. from -- streams supporting MIDI natively) function MIDIVelocityStream.getValue(note, channel) + -- `note` may be a note name like "A4" + note = type(note) == "string" and ntom(note) or note -- NOTE: The native function assert() for invalid -- notes or channels to avoid segfaults assert(0 <= note and note <= 127, @@ -1652,23 +1655,58 @@ end -- MIDI primitives --- There are only 128 possible MIDI notes, --- so their frequencies can and should be cached. --- We do this once instead of on-demand, so the lookup --- table consists of consecutive numbers. -local mtof_cache = table.new(128, 0) -for note = 0, 127 do - -- MIDI NOTE 69 corresponds to 440 Hz - mtof_cache[note] = 440*math.pow(2, (note - 69)/12) -end +do + local note_names = { + "C", "C#", "D", "D#", "E", "F", + "F#", "G", "G#", "A", "A#", "B" + } + + -- MIDI note number to name + -- NOTE: mton() can handle the words as generated by MIDINoteStream + function mton(note) + note = bit.band(note, 0xFF) + local octave = math.floor(note / 12)-1 + return note_names[(note % 12)+1]..octave + end + + function Stream:mton() return self:map(mton) end --- Convert from MIDI note to frequency --- NOTE: mtof() can handle the words as generated by MIDINoteStream -function mtof(note) - return mtof_cache[bit.band(note, 0xFF)] + local ntom_offsets = {} + for i, name in ipairs(note_names) do + ntom_offsets[name] = i-1 + -- Saving the offsets for the lower-cased note names + -- avoids a string.upper() call in ntom() + ntom_offsets[name:lower()] = i-1 + end + + -- Note name to MIDI note number + function ntom(name) + local octave = name:byte(-1) - 48 + 1 + return octave*12 + ntom_offsets[name:sub(1, -2)] + end + + function Stream:ntom() return self:map(ntom) end end -function Stream:mtof() return self:map(mtof) end +do + -- There are only 128 possible MIDI notes, + -- so their frequencies can and should be cached. + -- We do this once instead of on-demand, so the lookup + -- table consists of consecutive numbers. + local mtof_cache = table.new(128, 0) + for note = 0, 127 do + -- MIDI NOTE 69 corresponds to 440 Hz + mtof_cache[note] = 440*math.pow(2, (note - 69)/12) + end + + -- Convert from MIDI note to frequency + -- NOTE: mtof() can handle the words as generated by MIDINoteStream + function mtof(note) + return mtof_cache[bit.band(note, 0xFF)] + end + + function Stream:mtof() return self:map(mtof) end +end -- Convert from frequency to closest MIDI note function ftom(freq) |