From 3f3d71d1c9dc4c5d903525bca9c81a7c94e23032 Mon Sep 17 00:00:00 2001 From: Josip Miskovic Date: Tue, 24 Mar 2020 18:33:38 +0200 Subject: [PATCH] Add recorder that handles individual tapes --- main.lua | 28 ++++----- tape.lua => recorder.lua | 126 ++++++++++++++++++++++++++------------- 2 files changed, 96 insertions(+), 58 deletions(-) rename tape.lua => recorder.lua (64%) diff --git a/main.lua b/main.lua index b5d8043..e8bc0a3 100755 --- a/main.lua +++ b/main.lua @@ -2,7 +2,7 @@ local l = require('lume') local controls = require('controls') local selector = require('selector') -local recorder = require('tapeRecorder') +local recorder = require('recorder') local efx = require('efx') local mock = require('mock') @@ -10,7 +10,7 @@ local time = 0 local sw, sh, dpi local patch = selector local stream = {} -local tape1, tape2 +local tape1 function love.resize() sw, sh = love.graphics.getDimensions() @@ -24,9 +24,9 @@ function love.load() require('toolset') -- import module only after love.draw is defined controls.load() selector.load('patches') - tape1 = recorder.create() + tape1 = recorder.addTape() + tape2 = recorder.addTape() tape1.position = {-1.65, -0.88} - tape2 = recorder.create() tape2.position = {-1.30, -0.88} mock.load() love.audio.setPosition(0, 0, 0) @@ -61,17 +61,12 @@ function love.update(dt) if love.system.getOS() ~= 'Android' then mock.process(stream) end + + recorder.interpret(stream, patch == selector) patch:process(stream) - local t2r = tape2.recording - tape1:process(stream, patch) - tape2:process(stream, patch) - if t2r and not tape2.recording then - tape2.time = 0 - tape2.time = tape2.length - tape1.length -- align start - tape2.length = tape1.length - end + recorder.process(stream) efx.process(stream) - love.timer.sleep(0.003) + love.timer.sleep(0.053) --stream is garbage collected end @@ -80,12 +75,10 @@ function love.draw() love.graphics.origin() transform() patch:draw(stream) - tape1:draw() - tape2:draw() + recorder.draw() love.graphics.origin() --mock.draw(stream) --drawTable(stream) - track("t %1.1f %1.1f", {tape1.time, tape2.time}) --track('fps %2.1f', love.timer.getFPS()) end @@ -94,8 +87,7 @@ function loadPatch(newPatch) time = 0 -- back to big bang efx.load() -- restore efx to defaults patch = newPatch.load() - tape1:patchChanged(newPatch) - tape2:patchChanged(newPatch) + recorder.patchChanged(newPatch) end diff --git a/tape.lua b/recorder.lua similarity index 64% rename from tape.lua rename to recorder.lua index e8915f8..534b087 100644 --- a/tape.lua +++ b/recorder.lua @@ -1,3 +1,4 @@ +local recorder = {} local tape = {} tape.__index = tape @@ -8,60 +9,101 @@ local colorScheme = { background = {l.hsl(0, 0, 0.1)}, grooves = {l.hsl(0, 0, 0.15)}, head = {l.hsl(0, 0.3, 0.5)}, - spindle = {l.hsl(0, 0, 0.6)}, + spindle = {l.hsl(0, 0.1, 0.3)}, note = {l.hsl(0, 0.6, 0.4)}, } +-- global for all tapes +recorder.speed = 1 +recorder.time = 0 +recorder.tapes = {} +recorder.loadedPatch = nil + --[[ TODO: per-tape volume: low mid high changing tempo syncing between length multiples (4 bars against 1 bar) - track icon pressing doesn't play note + adding echo track with dotted eighth delay adjustable track icon position (on main screen) saving and loading to storage more than 2 tracks, adding and removing ]] -function tape.create() +function recorder.addTape() local self = setmetatable({}, tape) self.position = {-1.5, -0.85} self.recording = false self.notes = {} self.touchId = nil - self.patch = nil + self.patch = nil -- a copy of patch is instantiated just for tape playback self.time = 0 self.timePrev = 0 self.length = math.huge -- huge! - self.lastPlayed = -1 self.recordingStop = false self.canvas = love.graphics.newCanvas(200, 200) + self:drawVinyl() + table.insert(recorder.tapes, self) return self end +function recorder.patchChanged(patch) + recorder.loadedPatch = patch.load() +end + +function recorder.interpret(s, inSelector) + for i,tape in ipairs(recorder.tapes) do + tape:interpret(s, inSelector) + end +end + -function tape:startStop(s) - -- record start / stop +function recorder.process(s) + for i,tape in ipairs(recorder.tapes) do + tape:process(s) + end +end + + +function recorder.draw() + for i,tape in ipairs(recorder.tapes) do + tape:draw() + end +end + + +-- tape functions -- + + +function tape:interpret(s, inSelector) local cnt = 0 for id,touch in pairs(s.touches) do cnt = cnt + 1 local x, y = love.graphics.inverseTransformPoint(touch[1], touch[2]) - if (self.position[1] - x)^2 + (self.position[2] - y)^2 < 0.03 then - -- on new touch toggle recording - if id ~= self.touchId then - if not self.recording then - self.patch = self.loadedPatch - self.recording = true - self.time = 0 - self.notes = {} + if (x - self.position[1])^2 + (y - self.position[2])^2 < 0.03 then + if inSelector then + if y - self.position[2] > 0 then + recorder.speed = recorder.speed * 0.998 else - self.recording = false - self.recordingStop = true - self.length = self.time - self.masterVolume = 0.5 + recorder.speed = recorder.speed * 1.002 + end + else -- on new touch toggle recording + if id ~= self.touchId then + if not self.recording then + self.patch = recorder.loadedPatch + self.recording = true + self.time = 0 + self.notes = {} + else + self.recording = false + self.recordingStop = true + self.length = self.time + self.masterVolume = 0.5 + end + self.touchId = id -- mark id to distinguish new touch from held touch end - self.touchId = id -- mark id to know if it is held touch of new touch end + s.touches[id] = nil -- sneakily remove touch from stream to prevent unintended notes end end if cnt == 0 then @@ -70,12 +112,10 @@ function tape:startStop(s) end -function tape:process(s, patch) - self:startStop(s) +function tape:process(s) -- tape recording if self.recording then table.insert(self.notes, {self.time, s}) - self.time = self.time + s.dt end -- tape playback if not self.recording and #self.notes > 0 and self.patch then @@ -85,17 +125,17 @@ function tape:process(s, patch) self.patch.sampler:processTouches(s.dt, stream.touches) end end + self.timePrev = self.time -- loop tape back to start if self.time > self.length then self.time = 0 + self.timePrev = -s.dt end - self.timePrev = self.time - self.time = self.time + s.dt end + self.time = self.time + s.dt * recorder.speed end - -function tape:drawNotes() +function tape:drawVinyl() love.graphics.origin() love.graphics.setCanvas(self.canvas) love.graphics.clear() @@ -109,10 +149,24 @@ function tape:drawNotes() love.graphics.setLineWidth(0.04) love.graphics.circle('line', 0, 0, l.remap(i, 1, grooveCount, 0.4, 0.85)) end - -- notes + -- start marker + love.graphics.setColor(colorScheme.head) + love.graphics.setLineWidth(0.06) + love.graphics.line(0.7, 0, 0.9, 0) + -- center spindle + love.graphics.setColor(colorScheme.spindle) + love.graphics.circle("fill", 0, 0, 0.25) + love.graphics.setCanvas() +end + +function tape:drawNotes() + love.graphics.origin() + love.graphics.setCanvas(self.canvas) + love.graphics.setBlendMode("alpha") + love.graphics.translate(100,100) + love.graphics.scale(100) love.graphics.setColor(colorScheme.note) - love.graphics.setLineWidth(0.08) - love.graphics.line(0.5, 0, 0.7, 0) + -- notes for i, rec in ipairs(self.notes) do noteTime, stream = unpack(rec) for id, touch in pairs(stream.touches) do @@ -126,10 +180,6 @@ function tape:drawNotes() end end end - -- center spindle - love.graphics.setColor(colorScheme.spindle) - love.graphics.circle("fill", 0, 0, 0.03) - love.graphics.setCanvas() end @@ -138,6 +188,7 @@ function tape:draw() -- when recording stops, map recorded notes onto circle if self.recordingStop then love.graphics.push() + self:drawVinyl() self:drawNotes() self.recordingStop = false love.graphics.pop() @@ -171,9 +222,4 @@ function tape:draw() end -function tape:patchChanged(patch) - self.loadedPatch = patch.load() -end - - -return tape \ No newline at end of file +return recorder \ No newline at end of file