Overview
I have music playing - sounds fine. I play a little sound effect, also sounds fine.
After playing the little sound several times, the music is ruined with distortion. I have no idea what could be causing this. Note that when I say 'sound effect' I'm referring to a short noise associated with an on-screen event. Not an effect like a filter or delay.
- Importantly, I can only get this error to occur on-device. I can't recreate it in the simulator.
Video Evidence of the Bug in Question
Context
Not all of this may be relevant to my issue - but I think more context is better than less right? I've spoilered long bits of code.
I load songs using data from a JSON file paired with a midi file. That JSON file looks like this -
my_song.json
[
{
"bwave": "sine",
"badsr": [20, 63, 35, 35],
"btrackindex": 2,
"bvolume": 0.75,
"awave": "square",
"atrackindex": 3,
"aadsr": [8, 33, 64, 52],
"aenvcurv": 0.15,
"avolume": 0.13,
"swave": "povosim",
"sadsr": [20, 60, 35, 35],
"senvcurv": 0.35,
"svolume": 0.75,
"slegato": true,
"strackindex": 1,
"delaymix": 0.24,
"delayfeedback": 0.33,
"delayaddtap": 0.14,
"bandpassmix": 1,
"bandpassfreq": 680,
"bandpassres": 0.13
}
]
Song objects look like this
Song class definition
class("Song").extends()
function Song:init(name)
Song.super.init(self)
self.name = name
self.midi_path = "assets/midi/"..name..".mid"
self.channels = {}
end
function Song:play(playlist)
self.sequence = construct_sequence(self)
self.sequence:play(end_of_song_handler)
end
Playlists look like this:
Playlist class definition
class("Playlist").extends()
function Playlist:init(name)
Playlist.super.init(self)
self.name = name
self.songs = {}
self.current_song = nil
self.pause_music = false
end
My function which constructs songs looks like this
Construct sequence function
local wave_library = {
sine = sfx.kWaveSine,
square = sfx.kWaveSquare,
povosim = sfx.kWavePOVosim
}
function construct_sequence(song)
-- print("constructing sequence "..song.name)
local midi = song.midi_path
local this_sequence = sfx.sequence.new(midi)
local tempo = this_sequence:getTempo()
this_sequence:setTempo(tempo)
local this_channel = sfx.channel.new()
this_channel:setVolume(.90)
table.insert(song.channels, this_channel)
local seq_data_path = "data/music/"..song.name..".json"
local data = json.decodeFile(seq_data_path)
data = data[1]
if data.bwave then
-- construct bass
local bass_track = this_sequence:getTrackAtIndex(data.btrackindex)
local bass_synth = sfx.synth.new(wave_library[data.bwave])
local bass_inst = sfx.instrument.new()
local a = data.badsr[1]/100
local d = data.badsr[2]/100
local s = data.badsr[3]/100
local r = data.badsr[4]/100
bass_synth:setADSR(a,d,s,r)
local volume = .9 or data.bvolume
bass_synth:setVolume(volume)
bass_inst:addVoice(bass_synth)
bass_track:setInstrument(bass_inst)
this_channel:addSource(bass_inst)
end
if data.twave then
-- construct tenor
end
if data.awave then
-- construct alto
local alto_track = this_sequence:getTrackAtIndex(data.atrackindex)
local alto_synth = sfx.synth.new(wave_library[data.awave])
local alto_instrument = sfx.instrument.new()
local a = data.aadsr[1]/100
local d = data.aadsr[2]/100
local s = data.aadsr[3]/100
local r = data.aadsr[4]/100
alto_synth:setADSR(a,d,s,r)
local volume = .9 or data.avolume
alto_synth:setVolume(volume)
alto_synth:setEnvelopeCurvature(data.aenvcurv)
alto_instrument:addVoice(alto_synth)
alto_track:setInstrument(alto_instrument)
this_channel:addSource(alto_instrument)
end
if data.swave then
-- construct soprano
local soprano_track = this_sequence:getTrackAtIndex(data.strackindex)
local soprano_synth = sfx.synth.new(wave_library[data.swave])
local soprano_instrument = sfx.instrument.new()
local a = data.sadsr[1]/100
local d = data.sadsr[2]/100
local s = data.sadsr[3]/100
local r = data.sadsr[4]/100
soprano_synth:setADSR(a,d,s,r)
soprano_synth:setEnvelopeCurvature(data.senvcurv)
local volume = .9 or data.svolume
soprano_synth:setVolume(volume)
soprano_instrument:addVoice(soprano_synth)
soprano_track:setInstrument(soprano_instrument)
this_channel:addSource(soprano_instrument)
end
if data.ppolyphony then
local track_index = data.ptrackindex
local piano_track = this_sequence:getTrackAtIndex(track_index)
local volume = data.pvolume
local polyphony = data.ppolyphony
local synth_stack = table.create(polyphony, 0)
local piano_inst = sfx.instrument.new()
for i=1, polyphony do
local synth = sfx.synth.new(sfx.kWaveSine)
local attack_variance = math.random(50, 100)
attack_variance *= .01
local decay_variance = (attack_variance * 10)/2
local sustain_variance = attack_variance * 5
local release_variance = sustain_variance
synth:setADSR(attack_variance, decay_variance, sustain_variance, release_variance)
synth:setEnvelopeCurvature(.15)
local variance = math.random(15, 24)
variance *= .01
-- print("variance: "..variance)
synth:setVolume(variance)
piano_inst:addVoice(synth)
end
piano_track:setInstrument(piano_inst)
this_channel:addSource(piano_inst)
end
if data.delaymix then
local delay = sfx.delayline.new(.55)
delay:setMix(data.delaymix)
delay:setFeedback(data.delayfeedback)
delay:addTap(data.delayaddtap)
this_channel:addEffect(delay)
end
if data.bandpassmix then
local bandpass = sfx.twopolefilter.new("bandpass")
bandpass:setMix(data.bandpassmix)
bandpass:setFrequency(data.bandpassfreq)
bandpass:setResonance(data.bandpassres)
this_channel:addEffect(bandpass)
end
return this_sequence
end
And my sound effect
Sound Effect
-- timer to try to prevent overlapping the noise on itself
local nav_sound_playing = false
function new_nav_sound()
if nav_sound_playing then
else
nav_sound()
nav_sound_playing = true
pd.timer.new(250, function()
nav_sound_playing = false
end)
end
end
function nav_sound()
my_sfx_channel = sfx.channel.new()
local bandpass = sfx.twopolefilter.new('bandpass')
bandpass:setMix(1)
bandpass:setFrequency(800)
bandpass:setResonance(.06)
my_sfx_channel:addEffect(bandpass)
local noise_synth = sfx.synth.new(sfx.kWaveNoise)
local noise_inst = sfx.instrument.new()
noise_synth:setADSR(0, .01, .01, .01)
noise_synth:setEnvelopeCurvature(.05)
noise_synth:setVolume(.84)
noise_synth:setLegato(true)
noise_inst:addVoice(noise_synth)
local frequency = 'Eb4'
local velocity = .42
local length = .07
local digi_synth = sfx.synth.new(sfx.kWavePODigital)
digi_synth:setParameter(1, .8)
digi_synth:setParameter(2, .8)
local digi_inst = sfx.instrument.new()
digi_synth:setADSR(.01, .02, .10, .01)
digi_synth:setEnvelopeCurvature(.05)
digi_synth:setVolume(.88)
digi_synth:setLegato(true)
digi_inst:addVoice(digi_synth)
frequency = 'Bb3'
velocity = .72
length = .09
my_sfx_channel:addSource(noise_inst)
my_sfx_channel:addSource(digi_inst)
my_sfx_channel:setVolume(0.55)
digi_inst:playNote(frequency, velocity,length)
noise_inst:playNote(frequency, velocity,length)
end
See For Yourself
Navigate to the directory which contains the 'source' folder called 'scapia.'
Type make long
> enter
If you just crank the crank or spam up or down and get that little navigation click sound to play a bunch, the music will start to distort into just a nasty mess. I'm so curious what could be going on here.
scapia-main-3.zip (501.8 KB)
Final Thoughts
- Am I messing with my music channel somehow when playing this sound effect?
- Am I making a mistake by instantiating new instruments/channels/synths/effects every time I play the sound effect? Are those supposed to be pre-made by the time it's time to make a noise?
- Is my sequence somehow getting out of step with itself?