Trying to edit ADSR on the fly

,

Hey there, I'm looking forward to receiving my Playdate some time next week, and learning to make some simple games. My background in programming is from some Structured Text and SFC in school, and I've been working in a mix of C# and vb.net for some years now. I played around in Lua in minecraft almost 10 years ago as well, I think, but overall this is very new to me some I'm somewhat struggling getting used to things.

So far I've managed to get some MIDI music playing, and I want to try changing the ADSR of the different instruments while it plays so I can understand what the values sound like and maybe tweak it to values I like. I've been editing the default demo script to play around with. I have the name of the instrument replacing the "template" word and have added the name of the voice value I am changing in the center of the screen. Then, by pressing B, I can cycle between Attack, Decay, Sustain and Release, and by pressing A I can cycle through the 6 instruments.

I ended up making an Instrument Class, but I don't really know what I am doing and when I try to adjust any value with the up or down arrow key, I get the following error:

tempo = 150
koro = sfx.sequence.new('music/korob.mid')
adsr = {"Attack", "Decay", "Sustain", "Release"}
insts = {"squareInst1", "squareInst2", "waveInst1", "noiseInst1", "squareInst3", "squareInst4"}
adsrSelected = 1
instSelected = 1

local function speedUpSound()
	koro:setTempo(tempo)
	koro:play()
end

local function setUpSound()
	local ch00 = sfx.channel.new()

	local track1 = koro:getTrackAtIndex(1)
	square1 = sfx.synth.new(sfx.kWaveSquare)
	--square1:setADSR(3, 1, 1, 3)
	local squareInst1 = sfx.instrument.new(square1)
	track1:setInstrument(squareInst1)
	ch00:addSource(squareInst1)

	local track2 = koro:getTrackAtIndex(2)
	local square2 = sfx.synth.new(sfx.kWaveSquare)
	--square2:setADSR(3, 1, 1, 3)
	local squareInst2 = sfx.instrument.new(square2)
	track2:setInstrument(squareInst2)
	ch00:addSource(squareInst2)

	local track3 = koro:getTrackAtIndex(3)
	local wave1 = sfx.synth.new(sfx.kWaveSine)
	local waveInst1 = sfx.instrument.new(wave1)
	track3:setInstrument(waveInst1)
	ch00:addSource(waveInst1)

	local track4 = koro:getTrackAtIndex(4)
	local noise1 = sfx.synth.new(sfx.kWaveNoise)
	local noiseInst1 = sfx.instrument.new(noise1)
	track4:setInstrument(noiseInst1)
	ch00:addSource(noiseInst1)

	local track5 = koro:getTrackAtIndex(5)
	local square3 = sfx.synth.new(sfx.kWaveSquare)
	local squareInst3 = sfx.instrument.new(square3)
	track5:setInstrument(squareInst3)
	ch00:addSource(squareInst3)

	local track6 = koro:getTrackAtIndex(6)
	local square4 = sfx.synth.new(sfx.kWaveSquare)
	local squareInst4 = sfx.instrument.new(square4)
	track6:setInstrument(squareInst4)
	ch00:addSource(squareInst4)

	inst = {squareInst1, squareInst2, waveInst1, noiseInst1, squareInst3, squareInst4}
	
	koro:setTempo(tempo)
	koro:play()
end
class('inst').extends()

function inst:init(instrument, attack, sustain, decay, release)
	self.instrument = instrument
	self.attack = attack
	self.sustain = sustain
	self.decay = decay
	self.release = release
end

inst1 = inst(squareInst1, 1, 1, 1, 1)
inst2 = inst(squareInst2, 1, 1, 1, 1)
inst3 = inst(waveInst1, 1, 1, 1, 1)
inst4 = inst(noiseInst1, 1, 1, 1, 1)
inst5 = inst(squareInst3, 1, 1, 1, 1)
inst6 = inst(squareInst4, 1, 1, 1, 1)
instList = {inst1, inst2, inst3, inst4, inst5, inst6}

local function applyADSR()
	local oInst = instList[instSelected]
	oInst.instrument:setADSR(oInst.attack, oInst.sustain, oInst.decay, oInst.release)
end
function playdate.AButtonDown()
	if instSelected == 6 then
		instSelected = 1
	else
		instSelected += 1
	end
end

function playdate.BButtonDown()
	if adsrSelected == 4 then
		adsrSelected = 1
	else
		adsrSelected += 1
	end
end

function playdate.upButtonDown()
	local oInst = instList[instSelected]
	if adsrSelected == 3 then
		if oInst.sustain <= 0.9 then
			oInst.sustain += 0.1
		elseif oInst.sustain >= 1.0 then
			oInst.sustain = 1.0
		end
	else
		local param
		if adsrSelected == 1 then
			param = oInst.attack
		elseif adsrSelected == 2 then
			param = oInst.decay
		elseif adsrSelected == 4 then
			param = oInst.release
		end
		param += 0.5
	end
	applyADSR()
end

function playdate.downButtonDown()
	local oInst = instList[instSelected]
	if adsrSelected == 3 then
		if oInst.sustain >= 0.1 then
			oInst.sustain -= 0.1
		elseif oInst.sustain <= 0.0 then
			oInst.sustain = 0.0
		end
	else
		local param
		if adsrSelected == 1 then
			param = oInst.attack
		elseif adsrSelected == 2 then
			param = oInst.decay
		elseif adsrSelected == 4 then
			param = oInst.release
		end
		if param >= 0.5 then
			param -= 0.5
		else
			param = 0
		end
	end
	applyADSR()
end
function dvd:draw()
    local label = self.label;
    gfx.drawTextInRect(insts[instSelected], label.x, label.y, label.width, label.height)
	local label2 = self.label2;
    gfx.drawTextInRect(adsr[adsrSelected], label2.x, label2.y, label2.width, label2.height)
end

looks like the problem is that the instruments (squareInst1, etc.) are local to the setUpSound() function, so when you get those variables in the inst() calls they're nil. I noticed you put those in a global array called inst so I was going to say you could use inst[1] instead of squareInst1, but declaring inst as a class overwrites that array. Removing local on those instrument initializers is one way to fix that problem.

Hi Dave, thanks so much for responding.

Yeah, that definitely makes sense and was at least part of the issue, but it turns out I had also mistakenly been using the instruments when setADSR actually needs to be called on the synth voice. Oops.

Unfortunately that wasn't what was causing the crash either. I managed to get it working, but had to remove calling setADSR through the instances of the inst class and instead call them directly. I'm still not quite sure why.

class('inst').extends()

function inst:init(synth, attack, sustain, decay, release)
	self.synth = synth
	self.attack = attack
	self.sustain = sustain
	self.decay = decay
	self.release = release
end

inst1 = inst(square1, 1, 1, 1.0, 1)
inst2 = inst(square2, 1, 1, 1.0, 1)
inst3 = inst(wave1, 1, 1, 1.0, 1)
inst4 = inst(noise1, 1, 1, 1.0, 1)
inst5 = inst(square3, 1, 1, 1.0, 1)
inst6 = inst(square4, 1, 1, 1.0, 1)
instList = {inst1, inst2, inst3, inst4, inst5, inst6}
square1:setADSR(inst1.attack, inst1.sustain, inst1.decay, inst1.release)
square2:setADSR(inst2.attack, inst2.sustain, inst2.decay, inst2.release)
wave1:setADSR(inst3.attack, inst3.sustain, inst3.decay, inst3.release)
noise1:setADSR(inst4.attack, inst4.sustain, inst4.decay, inst4.release)
square3:setADSR(inst5.attack, inst5.sustain, inst5.decay, inst5.release)
square4:setADSR(inst6.attack, inst6.sustain, inst6.decay, inst6.release)

local function applyADSR()
	--local oInst = instList[instSelected]
	--oInst.synth:setADSR(oInst.attack, oInst.sustain, oInst.decay, oInst.release)
	if instSelected == 1 then
		square1:setADSR(inst1.attack, inst1.sustain, inst1.decay, inst1.release)
	elseif instSelected == 2 then
		square2:setADSR(inst2.attack, inst2.sustain, inst2.decay, inst2.release)
	elseif instSelected == 3 then
		wave1:setADSR(inst3.attack, inst3.sustain, inst3.decay, inst3.release)
	elseif instSelected == 4 then
		noise1:setADSR(inst4.attack, inst4.sustain, inst4.decay, inst4.release)
	elseif instSelected == 5 then
		square3:setADSR(inst5.attack, inst5.sustain, inst5.decay, inst5.release)
	elseif instSelected == 6 then
		square4:setADSR(inst6.attack, inst6.sustain, inst6.decay, inst6.release)
	end
end
1 Like