diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2015-12-31 10:17:25 +0100 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2015-12-31 14:09:09 +0100 |
commit | fc9c4746a21226723875cebfbf310065038141bf (patch) | |
tree | 6e255faf3700ff5e7761e039d7e66113d8e2061a /applause.lua | |
parent | f5e8f70a7aaab571e1136e2eb789e3c1ebdc26f6 (diff) | |
download | applause2-fc9c4746a21226723875cebfbf310065038141bf.tar.gz |
implemented basic support for MIDI NOTE ON/OFF events
* MIDIVelocityStream will report the velocities on a particular
note. This can be used to build polyphonic instruments.
* MIDINoteStream will report all possible notes on a particular
channel. The velocity of the note last triggered is also encoded
into the stream values. This can be used to build simple
monophonic instruments.
* Map bit methods to stream methods, e.g. Stream:band()
Is especially useful for streams that encode multiple values
into single integers for performance reasons.
* mtof() and ftom() methods for converting from and to MIDI note
numbers. mtof() is very efficient since its values are all
precalculated in a lookup table.
* fixed tostring() method for streams smaller than 1024 samples
Diffstat (limited to 'applause.lua')
-rw-r--r-- | applause.lua | 94 |
1 files changed, 92 insertions, 2 deletions
diff --git a/applause.lua b/applause.lua index e5a9b42..a76fb41 100644 --- a/applause.lua +++ b/applause.lua @@ -1,5 +1,9 @@ local sndfile = require "sndfile" local ffi = require "ffi" +local bit = require "bit" + +-- Make table.new() available +require "table.new" -- -- Define C functions for benchmarking (POSIX libc) @@ -123,6 +127,21 @@ for _, fnc in pairs{"abs", "acos", "asin", "atan", end end +function Stream:bnot() + return self:map(bit.bnot) +end + +-- Register all binary operators of the "bit" module +for _, name in pairs{"bor", "band", "bxor", + "lshift", "rshift", "arshift", + "rol", "ror"} do + local fnc = bit[name] + + Stream[name] = function(self, v) + return self:map(function(x) return fnc(x, v) end) + end +end + -- Scalar operations -- In contrast to stream operations (based on ZipStream), -- these work only with scalars and do not @@ -384,7 +403,7 @@ function Stream:__tostring() t = self:sub(1, 1024):totable() table.insert(t, "...") else - t = self() + t = self:totable() end for i = 1, #t do t[i] = tostring(t[i]) end @@ -884,6 +903,50 @@ function NoiseStream:tick() end end +-- Velocity of NOTE ON for a specific note on a channel +MIDIVelocityStream = DeriveClass(Stream, function(self, note, channel) + self.note = note + self.channel = channel or 0 +end) + +-- implemented in applause.c, private! +function MIDIVelocityStream.getValue(note, channel) + error("C function not registered!") +end + +function MIDIVelocityStream:tick() + local note = self.note + local channel = self.channel + local getValue = self.getValue + + return function() + return getValue(note, channel) + end +end + +-- Stream of integer words representing the last MIDI note +-- triggered on a channel with its corresponding velocity +-- (of the NOTE ON message). +-- The MIDI note is the lower byte and the velocity the +-- upper byte of the word. +MIDINoteStream = DeriveClass(Stream, function(self, channel) + self.channel = channel or 0 +end) + +-- implemented in applause.c, private! +function MIDINoteStream.getValue(channel) + error("C function not registered!") +end + +function MIDINoteStream:tick() + local channel = self.channel + local getValue = self.getValue + + return function() + return getValue(channel) + end +end + MIDICCStream = DeriveClass(Stream, function(self, control, channel) self.control = control self.channel = channel or 0 @@ -894,7 +957,6 @@ function MIDICCStream.getValue(control, channel) error("C function not registered!") end --- FIXME: Perhaps implement tick() directly in C? function MIDICCStream:tick() local control = self.control local channel = self.channel @@ -905,6 +967,34 @@ function MIDICCStream:tick() end end +-- MIDI primitives + +-- There are only 120 different 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(120, 0) +for note = 0, 119 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 + +-- Convert from frequency to closest MIDI note +function ftom(freq) + -- NOTE: math.log/2 is a LuaJIT extension + return math.floor(12*math.log(freq/440, 2) + 0.5)+69 +end + +function Stream:ftom() return self:map(ftom) end + -- primitives function tostream(v) |