From ef023bd9f0eaa7c50664e89eabc3db539fc38211 Mon Sep 17 00:00:00 2001 From: Robin Haberkorn Date: Sun, 3 Jul 2016 16:37:21 +0200 Subject: added InstrumentStream (Stream:instrument) * allows a stream of note velocities to trigger a note-on and note-off stream. * usually used on a MIDIVelocityStream * allows a single note to be triggered including attack, sustain and decay phases * by letting the note-on and note-off streams depend on the note/velocity stream, the note can use the NOTE ON velocity. * The InstrumentStream constructor will also accept functions on the note_stream, so it is possible to define the note-on/off streams depending on the velocity stream on a single line of source code. * A NOTE OFF velocity is currently not used, though that could be achieved by letting MIDIVelocityStream generate negative numbers for NOTE OFF velocities. * This will only work with a single note (ie. a fixed frequency); when wanting to play polyphony, users will have to mix many different InstrumentStreams, e.g. iota(69, 79):map(function(note) return MIDIVelocityStream(note, 1):instrument(function(v) return InstOn(note, v) end, InstOff(note)) end):fold(function(x,y) return x+y end):ravel() --- applause.lua | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/applause.lua b/applause.lua index 404efef..171bd2d 100644 --- a/applause.lua +++ b/applause.lua @@ -1682,6 +1682,64 @@ end function Stream:ftom() return self:map(ftom) end +InstrumentStream = DeriveClass(MuxableStream) + +InstrumentStream.sig_last_stream = 1 + +function InstrumentStream:muxableCtor(note_stream, on_stream, off_stream) + note_stream = tostream(note_stream):cache() + + if type(on_stream) == "function" then + on_stream = on_stream(self.note_stream) + else + on_stream = tostream(on_stream) + end + if type(off_stream) == "function" then + off_stream = off_stream(self.note_stream) + else + -- The "off" stream is optional + off_stream = off_stream and tostream(off_stream) + end + + self.streams = {note_stream, on_stream, off_stream} +end + +function InstrumentStream:tick() + local note_tick = self.streams[1]:tick() + local on_stream = self.streams[2] + local off_stream = self.streams[3] + local on_tick + local function off_tick() return 0 end + + return function() + local note = note_tick() + if not note then return end + + if on_tick == nil then -- no note + if note == 0 then return off_tick() or 0 end + + -- FIXME: This is not strictly real-time safe + on_tick = on_stream:tick() + return on_tick() or 0 + else -- note on + if note ~= 0 then return on_tick() or 0 end + + -- FIXME: This is not strictly real-time safe + on_tick = nil + off_tick = off_stream and off_stream:tick() + return off_tick() or 0 + end + end +end + +function InstrumentStream:len() + return self.streams[1]:len() +end + +function Stream:instrument(on_stream, off_stream) + return InstrumentStream:new(self, on_stream, off_stream) +end + -- primitives function tostream(v) -- cgit v1.2.3