I’m building my first app with Corona which is a kind of touchscreen autoharp and it seems relatively straightforward so far. Essentially, there are several chord buttons and a strumming area; when a chord button is tapped, all the notes in the strumming area change to those of the correct chord.
At the moment I can get one of two effects: either call audio.stop() on all channels when a new chord is pressed, silencing the strumming area, or allow the strumming sample to ring out in the original key even after the new chord is pressed. What I want, however, is for the strumming sample to instantly change to the notes of the new chord, but for the sample duration to remain the same (is this making sense?)
So what I need to do is detect at what time during the sample the chord was changed, then start playing the new sample from that point.
There doesn’t appear to be an API to facilitate this, so what’s the best way to accomplish it?
Any help would be much appreciated, I’m very new to Lua and to Corona and have decided to learn by just jumping in and building something - this is the first time I’ve got really stuck!
You could mesure the time since a strumm and then use audio.seek() - if I’m getting this right.
Maybe better would be to start all chords simultaneously on different channels and change only the channel. When strummed all chords must be started from new. If you have timing problems, gaps etc. try to use media() api.
Maybe better would be to start all chords simultaneously on different channels and change only the channel. When strummed all chords must be started from new. If you have timing problems, gaps etc. try to use media() api.
Not sure if I’m understanding this correctly - would I audio.play() on all possible notes for the contact but, for instance, have the volume set to zero for all but the active one? Is this not really memory-intensive?
Alternatively, would I measure the system time at the start and end of a strum and then use audio.seek() using the returned data? This makes more sense to me for some reason.
I wondered if I could use this somehow but am not sure how to implement it. If event.completed returns false when another function is run, then I have to somehow seek to the same point at which it was stopped. I’m trying to do this by getting the system time at the start and end of a function, subtracting one from the other and seeking to that point in the next piece of audio played, but it seems to be working inconsistently.
So I have this, not using event.completed yet but assuming the strumplate samples are 0.5 seconds long each. (Hopefully annotated clearly enough - when a chord change button is pressed it should change the notes played on the strumplate) - this is just two chords, C and G, and two individual notes on the strumplate:
timeTotal = 0 print(timeTotal) -------------------------- -- CHORD CHANGER - C MAJOR -------------------------- function playC (event) audio.stop({channel=1}) timeStart = system.getTimer() audio.play(chords[1], { channel=1, loops=-1 }) plate1 = strum[1] plate2 = strum[2] timeFinish = system.getTimer() timeTotal = timeFinish - timeStart print(timeTotal) end -------------------------- -- CHORD CHANGER - G MAJOR -------------------------- function playG (event) audio.stop({channel=1}) timeStart = system.getTimer() audio.play(chords[2], { channel=1, loops=-1 }) plate1 = strum[3] plate2 = strum[4] timeFinish = system.getTimer() timeTotal = timeFinish - timeStart print(timeTotal) end ------------------------ -- STRUMPLATE PLATE 1 ------------------------ function plate1 (event) if timeTotal \> 0 and timeTotal \< 0.5 then audio.seek(timeTotal, { channel=2 }) audio.play(plate1, { channel=2 }) else audio.play(plate1, { channel=2}) end end ----------------------- --STRUMPLATE PLATE 2 ----------------------- function plate2 (event) if timeTotal \> 0 and timeTotal \< 0.5 then audio.seek(timeTotal, { channel=3 }) audio.play(plate2, { channel=3 }) else audio.play(plate2, { channel=3}) end end ------------------------------------------ --EVENT LISTENERS ------------------------------------------ buttons.cbutton:addEventListener('tap', playC) buttons.gbutton:addEventListener('tap', playG) strumplate.plate1:addEventListener('tap', plate1) strumplate.plate2:addEventListener('tap', plate2)
Have I screwed something up or is there something wrong with my understanding of either the system time or audio.seek?
The “event.completed” property will be set false if you stopped that audio channel yourself.
But come to think of it, is it really necessary for you to stop audio channels yourself when strumming the harp? Wouldn’t it be more natural to let each chord play from beginning to end when strumming multiple strings? And perhaps playback should only be stopped when you *hold* your finger on a string. Just throwing the idea out there.
And to throw in another monkey wrench into this, if you plan on making this app available on Android, then you’ll have to deal with that platform’s audio latency issues that a lot of the devices on the market are plagued with. While our audio.* APIs provide a lot of powerful features, the native APIs they use on Android is well known to have latency issues on many Android devices. It’s fine for background music, but for quick sound effects like this, it’s going to be a problem. In which case, I would recommend that you use Corona’s media.playEventSound() API on Android instead… and *only* on Android. That API uses an Android native API that is documented to have the least latency, but it has less capabilities. I would recommend that you use our audio.* APIs for all other platforms, particularly on iOS, since I believe media.playEventSound() on iOS can only play 1 sound effect at a time.
So, with that said, I think the best approach is to set up all of your chord WAV files to have the same duration and set up your code to assume how long they’ll play. You can then use Corona’s timer.performWithDelay() API to keep track when the sound ends.
Thing is, I’m basing it on an existing (now-defunct) piece of hardware which takes much of its idiot-proofness from the fact that if the strum sound is incomplete when the chord changes, the notes change with it. I’m kind of being stubborn about certain aspects of the interface because it has a bit of a cult following.
Android I know is an issue (is that still the case on Lollipop?) and had kind of planned on crossing that bridge when I’d got the iOS version out. I don’t currently have an Android device to test on which is also a factor. I know that’s a cop-out, but baby steps.
I’m wondering if picking an audio-heavy project for my first attempt at Corona might have been a terrible mistake, whether there are other SDKs better-suited to it or if I should have just sucked it up and learned Objective-C - but it seems like this should be doable (and I love Lua), I’m just not sure how to accomplish it!
Okay. I think I understand what you’re after now. The only difficult part here is that Corona does not have a Lua API that tells you the current playback position. So, you have no choice but to guess, kind of like what you are doing, but I would store the start time instead. This way, when you tap on another chord, you can subtract current time from the stored start time and get the currently playing audio’s playback position (not accurate, but it’ll due) and use that as the seek time for the next chord you want to play. And since you already know the duration of all chords, then if currentTime minus startTime is greater than the duration, then you know you don’t need to seek. It’s a simplistic approach, but it should work.
And another cool aspect of using our audio APIs is that we do have an undocumented API set that allows you to control the audio playback’s pitch, if that’s something you’re interested in. Although, increasing/decreasing the pitch effectively speeds up or slows down playback respectively, which changes the overall duration of playback. That’ll make the above a bit more complicated.
And yes, audio latency on Android is still an issue. For some devices, it might just be a hardware or driver issue. I’ve never heard of any latency issues on Google’s Nexus devices, but it’s all of the other devices that may be hit-or-miss. The worst latency I’ve seen (heard?) was with the 1st generation Kindle Fire, whose latency was about 300-400 milliseconds… and there was no work-around; it was an issue with the hardware. Now, there is an AndroidManifest.xml settings, which you inject via Corona’s “build.settings” file, that will only allow devices with low latency audio to purchase/download your app from the app store, but I’m not sure if that’s worth using or not. It might severely limit the availability of your app to Android users. Or at least make it confusing to them as to why they can’t download your app that they can see on Google Play’s store via their PC browser.
Is there an API better than system.getTimer to store the start time, given that it stores the time in milliseconds? Or is it how I’m declaring it in the function that’s incorrect?
I recommend that you use the system.getTimer() function. Internally, it fetches current time in microseconds and returns it as *fractional* milliseconds.
Is there a better way to do it than getting the time at the start and end of the function as below and in my previous example, though? I feel like there’s something really fundamentally wrong with how I’m understanding how Lua works temporally but I’m not sure what it is. Should I be getting timeFinish at the start of the *next* function and then doing the maths?
The audio.play() function is not blocking. It plays audio asynchronously via another thread. So, the issue here is that your “timeFinish” and “timStart” variables will be set to nearly the same time. I would get rid of the “timeFinish” and “timeTotal” lines from your code. If you want to know when audio playback ends, then you’ll need to use the “onComplete” listener.
Argh. So I think I’ve figured the maths out. However, I can’t work out how to have the chord pass the information about the new note to the strum function before it’s completed. Right now, the strumplate note will always finish playing in the original key, even if its value is changed in the chord change function. I’m not sure how to force it to change if the chord change occurs before 2000ms (the length of all the strum samples).
Basically after the event listener for the strumplate plates has already fired, I need to be able to stop, seek and play again, and it isn’t working, and I can’t figure out why!
I am getting a bit worried that the way I’ve structured the entire app is completely awful but I can’t find anything similar to compare it to.
----------------------------------------------- --MAJOR BUTTONS ----------------------------------------------- local buttons = {} buttons.killbutton = display.newImage ("c-button.png") buttons.killbutton:scale ( 0.65, 0.65 ) buttons.killbutton.x = 40 buttons.killbutton.y = 100 buttons.cbutton = display.newImage ("c-button.png") buttons.cbutton:scale ( 0.75, 0.75 ) buttons.cbutton.x = 40 buttons.cbutton.y = 190 buttons.gbutton = display.newImage ("g-button.png") buttons.gbutton:scale ( 0.75, 0.75 ) buttons.gbutton.x = 90 buttons.gbutton.y = 190 ------------------------------ -- STRUMPLATE ------------------------------ local strumplate = {} strumplate.plate1 = display.newImage ("strumplate.png") strumplate.plate1.x = 400 strumplate.plate1.y = 280 strumplate.plate2 = display.newImage ("strumplate.png") strumplate.plate2.x = 400 strumplate.plate2.y = 270 strumplate.plate3 = display.newImage ("strumplate.png") strumplate.plate3.x = 400 strumplate.plate3.y = 260 strumplate.plate4 = display.newImage ("strumplate.png") strumplate.plate4.x = 400 strumplate.plate4.y = 250 strumplate.plate5 = display.newImage ("strumplate.png") strumplate.plate5.x = 400 strumplate.plate5.y = 240 strumplate.plate6 = display.newImage ("strumplate.png") strumplate.plate6.x = 400 strumplate.plate6.y = 230 ---------------------------------------------- -- LOAD MAIN BUTTON SOUNDS -------------------------------------- local cmaj = audio.loadSound ("cmaj.wav") local gmaj = audio.loadSound ("gmaj.wav") local chords = { cmaj, gmaj, dmaj, amaj, emaj, bmaj } -------------------------------------- -- STRUMPLATE SOUNDS LOADED -------------------------------------- local b1 = audio.loadStream ("strumb1.wav") local c1 = audio.loadStream ("strumc1.wav") local d1 = audio.loadStream ("strumd1.wav") local e1 = audio.loadStream ("strume1.wav") local g1 = audio.loadStream ("strumg1.wav") local c2 = audio.loadStream ("strumc2.wav") local d2 = audio.loadStream ("strumd2.wav") local e2 = audio.loadStream ("strume2.wav") local g2 = audio.loadStream ("strumg2.wav") local b2 = audio.loadStream ("strumb2.wav") local strum = { b1, c1, d1, e1, g1, b2, c2, d2, e2, g2 } --------------------------------------- -- FUNCTIONS --------------------------------------- timeStart = 0 print(timeStart) audio.setVolume( 0.2, { channel=1 } ) function playC (event) audio.stop({channel=1}) lastTime = timeStart timeStart = system.getTimer() currentTime = timeStart - lastTime print(currentTime) audio.play(chords[1], { channel=1, loops=-1 }) plate1 = strum[2] plate2 = strum[4] plate3 = strum[5] plate4 = strum[7] plate5 = strum[9] plate6 = strum[10] end function playG (event) audio.stop({channel=1}) lastTime = timeStart timeStart = system.getTimer() currentTime = timeStart - lastTime print(currentTime) audio.play(chords[2], { channel=1, loops=-1 }) plate1 = strum[1] plate2 = strum[3] plate3 = strum[5] plate4 = strum[6] plate5 = strum[8] plate6 = strum[10] end function plate1 (event) if currentTime \< 2000 then audio.seek( currentTime, {channel=2} ) audio.play(plate1, { channel=2, loops=-1 }) else audio.play(plate1, { channel=2, loops=-1 }) end end function plate2 (event) if currentTime \< 2000 then audio.seek( currentTime, {channel=3} ) audio.play(plate2, { channel=3, loops=-1 }) else audio.play(plate2, { channel=3, loops=-1 }) end end function plate3 (event) if currentTime \< 2000 then audio.seek( currentTime, {channel=4} ) audio.play(plate3, { channel=4, duration=2000 }) else audio.play(plate3, { channel=4, duration=2000 }) end end function plate4 (event) if currentTime \< 2000 then audio.seek( currentTime, {channel=5} ) audio.play(plate4, { channel=5, duration=2000 }) else audio.play(plate4, { channel=5, duration=2000 }) end end function plate5 (event) if currentTime \< 2000 then audio.seek( currentTime, {channel=6} ) audio.play(plate5, { channel=6, duration=2000 }) else audio.play(plate5, { channel=6, duration=2000 }) end end function plate6 (event) if currentTime \< 2000 then audio.seek( currentTime, {channel=7} ) audio.play(plate6, { channel=7, duration=2000 }) else audio.play(plate6, { channel=7, duration=2000 }) end end local function killall (event) audio.stop() timeStart = 0 end ------------------------------------------ --EVENT LISTENERS ------------------------------------------ buttons.killbutton:addEventListener ("tap", killall) buttons.cbutton:addEventListener('tap', playC) buttons.gbutton:addEventListener('tap', playG) strumplate.plate1:addEventListener('tap', plate1) strumplate.plate2:addEventListener('tap', plate2) strumplate.plate3:addEventListener('tap', plate3) strumplate.plate4:addEventListener('tap', plate4) strumplate.plate5:addEventListener('tap', plate5) strumplate.plate6:addEventListener('tap', plate6)
In essence I’m trying to ‘swap out’ the audio and I can’t figure out how to do so while it is still playing. Do I need a callback or something? Where do I put it? I’m suddenly completely confused and don’t know how to link the strumplate and chord buttons together.
You have some *scoping* issues with duplicate variable and function names. Particularly with your “plateX” functions and variables. For example…
function plate1 (event) if currentTime \< 2000 then audio.seek( currentTime, {channel=2} ) audio.play(plate1, { channel=2, loops=-1 }) else audio.play(plate1, { channel=2, loops=-1 }) end end
In function plate1() above, you are also passing plate1 to the audio.play() function. Since Lua is also a functional language, it’ll attempt to pass the plate1() function as the first argument instead, which of course isn’t going to work. You’ll need to pass in what is returned by audio.loadSound() or loadStream(). Also, I would recommend that you rename your plateX() functions to something like onPlateX() instead since they’re really event listeners and it avoids the whole scoping issues with duplicate variable/function names.
Also, you must call audio.seek() *after* you call audio.play(). Calling it *before* means it will seek the previous playing sound and then calling audio.play() afterwards will make the new sound start from the beginning.
Since you haven’t defined the plate1, plate2, plate3, etc. variables above this function, then Lua will make these temporary global variable until it exits the function, at which point they’ll be garbage collected. You should make those “local” variables at the top of your file. And perhaps rename them to plate1Sound, plate2Sound, etc. because it’s a little confusing to see a “strumplate.plate1” and plate1() function and a plate1 global (hard to tell what is what when I first looked over your code).
Also, you can use the Lua print() function to help debug your code and see what the values really are. The print() function will output the Corona Simulator’s console window.
I’m definitely a little confused. Because the value of the plate1Sound, plate2Sound, plate3Sound etc. variables changes depending on what chord has been triggered and it pulls that value from the local ‘strum’ table higher up in the code.
How can I load plate1Sound/plate2Sound etc as local variables when the value is not fixed?
This is the entire main.lua file. When a chord button is touched, the value of each of the plates changes and can be triggered by a touch event. Have I just done this completely and utterly wrong in a structural sense? I just don’t understand how I can have plateSound be a local variable when it is pulling the sounds from the strum table of local variables.
Sorry for all the questions. I wonder if I have misunderstood something bigger than I realised.
EDIT: wait, do you mean I can set up an empty local variable for each plateSound at the top of the file, as below? This is getting incredibly unwieldy and I haven’t even set up all 24 plates yet O_O
-- Set width and height \_W = display.contentWidth \_H = display.contentHeight --activate multitouch system.activate = ("multitouch") ---------------------------------------------- -- LOAD IMAGES ---------------------------------------------- local background = display.newImage ("background.png") background:scale( 2, 2 ) ----------------------------------------------- --MAJOR BUTTONS ----------------------------------------------- local buttons = {} buttons.killbutton = display.newImage ("c-button.png") buttons.killbutton:scale ( 0.65, 0.65 ) buttons.killbutton.x = 40 buttons.killbutton.y = 100 buttons.cbutton = display.newImage ("c-button.png") buttons.cbutton:scale ( 0.75, 0.75 ) buttons.cbutton.x = 40 buttons.cbutton.y = 190 buttons.gbutton = display.newImage ("g-button.png") buttons.gbutton:scale ( 0.75, 0.75 ) buttons.gbutton.x = 90 buttons.gbutton.y = 190 ------------------------------ -- STRUMPLATE ------------------------------ local strumplate = {} strumplate.plate1 = display.newImage ("strumplate.png") strumplate.plate1.x = 400 strumplate.plate1.y = 280 strumplate.plate2 = display.newImage ("strumplate.png") strumplate.plate2.x = 400 strumplate.plate2.y = 270 strumplate.plate3 = display.newImage ("strumplate.png") strumplate.plate3.x = 400 strumplate.plate3.y = 260 strumplate.plate4 = display.newImage ("strumplate.png") strumplate.plate4.x = 400 strumplate.plate4.y = 250 strumplate.plate5 = display.newImage ("strumplate.png") strumplate.plate5.x = 400 strumplate.plate5.y = 240 strumplate.plate6 = display.newImage ("strumplate.png") strumplate.plate6.x = 400 strumplate.plate6.y = 230 ---------------------------------------------- -- LOAD MAIN BUTTON SOUNDS -------------------------------------- local ebmaj = audio.loadSound ("ebmaj.wav") local bbmaj = audio.loadSound ("bbmaj.wav") local fmaj = audio.loadSound ("fmaj.wav") local cmaj = audio.loadSound ("cmaj.wav") local gmaj = audio.loadSound ("gmaj.wav") local dmaj = audio.loadSound ("dmaj.wav") local amaj = audio.loadSound ("amaj.wav") local emaj = audio.loadSound ("dmaj.wav") local bmaj = audio.loadSound ("amaj.wav") local ebmin = audio.loadSound ("ebmaj.wav") local bbmin = audio.loadSound ("bbmaj.wav") local fmin = audio.loadSound ("fmaj.wav") local cmin = audio.loadSound ("cmaj.wav") local gmin = audio.loadSound ("gmaj.wav") local dmin = audio.loadSound ("dmaj.wav") local amin = audio.loadSound ("amaj.wav") local emin = audio.loadSound ("dmaj.wav") local bmin = audio.loadSound ("amaj.wav") local eb7 = audio.loadSound ("ebmaj.wav") local bb7 = audio.loadSound ("bbmaj.wav") local f7 = audio.loadSound ("fmaj.wav") local c7 = audio.loadSound ("cmaj.wav") local g7 = audio.loadSound ("gmaj.wav") local d7 = audio.loadSound ("dmaj.wav") local a7 = audio.loadSound ("amaj.wav") local e7 = audio.loadSound ("dmaj.wav") local b7 = audio.loadSound ("amaj.wav") local chords = { ebmaj, bbmaj, fmaj, cmaj, gmaj, dmaj, amaj, emaj, bmaj, ebmin, bbmin, fmin, cmin, gmin, dmin, amin, emin, bmin, eb7, bb7, f7, c7, g7, d7, a7, e7, b7 } -------------------------------------- -- STRUMPLATE SOUNDS LOADED -------------------------------------- local bb1 = audio.loadSound ("strumbb1.wav") local b1 = audio.loadSound ("strumb1.wav") local c1 = audio.loadSound ("strumc1.wav") local cs1 = audio.loadSound ("strumcs1.wav") local d1 = audio.loadSound ("strumd1.wav") local eb1 = audio.loadSound ("strumds1.wav") local e1 = audio.loadSound ("strume1.wav") local f1 = audio.loadSound ("strumf1.wav") local fs1 = audio.loadSound ("strumfs1.wav") local g1 = audio.loadSound ("strumg1.wav") local gs1 = audio.loadSound ("strumgs1.wav") local a1 = audio.loadSound ("struma1.wav") local bb2 = audio.loadSound ("strumbb2.wav") local b2 = audio.loadSound ("strumb2.wav") local c2 = audio.loadSound ("strumc2.wav") local cs2 = audio.loadSound ("strumcs2.wav") local d2 = audio.loadSound ("strumd2.wav") local eb2 = audio.loadSound ("strumds2.wav") local e2 = audio.loadSound ("strume2.wav") local f2 = audio.loadSound ("strumf2.wav") local fs2 = audio.loadSound ("strumfs2.wav") local g2 = audio.loadSound ("strumg2.wav") local gs2 = audio.loadSound ("strumgs2.wav") local a2 = audio.loadSound ("struma1.wav") local bb3 = audio.loadSound ("strumbb3.wav") local b3 = audio.loadSound ("strumb3.wav") local c3 = audio.loadSound ("strumc3.wav") local cs3 = audio.loadSound ("strumcs3.wav") local d3 = audio.loadSound ("strumd3.wav") local eb3 = audio.loadSound ("strumds3.wav") local e3 = audio.loadSound ("strume3.wav") local f3 = audio.loadSound ("strumf3.wav") local fs3 = audio.loadSound ("strumfs3.wav") local g3 = audio.loadSound ("strumg3.wav") local gs3 = audio.loadSound ("strumgs3.wav") local a3 = audio.loadSound ("struma1.wav") local bb4 = audio.loadSound ("strumbb4.wav") local b4 = audio.loadSound ("strumb4.wav") local c4 = audio.loadSound ("strumc4.wav") local cs4 = audio.loadSound ("strumcs4.wav") local d4 = audio.loadSound ("strumd4.wav") local eb4 = audio.loadSound ("strumds4.wav") local e4 = audio.loadSound ("strume4.wav") local f4 = audio.loadSound ("strumf4.wav") local fs4 = audio.loadSound ("strumfs4.wav") local g4 = audio.loadSound ("strumg4.wav") local gs4 = audio.loadSound ("strumgs4.wav") local a4 = audio.loadSound ("struma1.wav") local strum = { bb1, b1, c1, cs1, d1, eb1, e1, f1, fs1, g1, gs1, a1, bb2, b2, c2, cs2, d2, eb2, e2, f2, fs2, g2, gs2, a2, bb3, b3, c3, cs3, d3, eb3, e3, f3, fs3, g3, gs3, a3, bb4, b4, c4, cs4, d4, eb4, e4, f4, fs4, g4, gs4, a4 } local plate1Sound = nil local plate2Sound = nil local plate3Sound = nil local plate4Sound = nil local plate5Sound = nil local plate6Sound = nil --------------------------------------- -- FUNCTIONS --------------------------------------- audio.setVolume( 0.2, { channel=1 } ) function playC (event) audio.stop({channel=1}) audio.play(chords[1], { channel=1, loops=-1 }) plate1Sound = strum[2] plate2Sound = strum[4] plate3Sound = strum[5] plate4Sound = strum[7] plate5Sound = strum[9] plate6Sound = strum[10] local isPlate1Playing = audio.isChannelPlaying( 2 ) if isPlate1Playing then plate1Stop = system.getTimer() plate1Current = plate1Stop - plate1Start print("plate 1 " .. plate1Current) if plate1Current \< 2000 then audio.stop( 2 ) audio.play(plate1, { channel=2 }) audio.seek( plate1Current, {channel=2} ) end end local isPlate2Playing = audio.isChannelPlaying( 3 ) if isPlate2Playing then plate2Stop = system.getTimer() plate2Current = plate2Stop - plate2Start print("plate 2 " .. plate2Current) if plate2Current \< 2000 then audio.stop( 3 ) audio.play(plate2, { channel=3 }) audio.seek( plate2Current, { channel=3 } ) end end end function playG (event) audio.stop({channel=1}) audio.play(chords[2], { channel=1, loops=-1 }) plate1Sound = strum[1] plate2Sound = strum[3] plate3Sound = strum[5] plate4Sound = strum[6] plate5Sound = strum[8] plate6Sound = strum[10] local isPlate1Playing = audio.isChannelPlaying( 2 ) if isPlate1Playing then plate1Stop = system.getTimer() plate1Current = plate1Stop - plate1Start print("plate 1 " .. plate1Current) if plate1Current \< 2000 then audio.stop( 2 ) audio.seek(plate1Current, {channel=2} ) audio.play(plate1, { channel=2 }) end end local isPlate2Playing = audio.isChannelPlaying( 3 ) if isPlate2Playing then plate2Stop = system.getTimer() plate2Current = plate2Stop - plate2Start print("plate 2 " .. plate2Current) if plate2Current \< 2000 then audio.stop( 3 ) audio.seek( plate2Current, {channel=3} ) audio.play(plate2, { channel=3 }) end end end function onPlate1 (event) audio.play(plate1Sound, { channel=2, duration=2000 }) plate1Start = system.getTimer() end function onPlate2 (event) audio.play(plate2Sound, { channel=3, duration=2000 }) plate2Start = system.getTimer() end function onPlate3 (event) audio.play(plate3Sound, { channel=4, duration=2000 }) end function onPlate4 (event) audio.play(plate4Sound, { channel=5, duration=2000 }) end function onPlate5 (event) audio.play(plate5Sound, { channel=6, duration=2000 }) end function onPlate6 (event) audio.play(plate6Sound, { channel=7, duration=2000 }) end local function killall (event) audio.stop() currentTime = 0 end ------------------------------------------ --EVENT LISTENERS ------------------------------------------ buttons.killbutton:addEventListener ("tap", killall) buttons.cbutton:addEventListener('tap', playC) buttons.gbutton:addEventListener('tap', playG) strumplate.plate1:addEventListener('touch', onPlate1) strumplate.plate2:addEventListener('touch', onPlate2) strumplate.plate3:addEventListener('touch', onPlate3) strumplate.plate4:addEventListener('touch', onPlate4) strumplate.plate5:addEventListener('touch', onPlate5) strumplate.plate6:addEventListener('touch', onPlate6)
Well first, let me ask you this. Did you make audio playback work the way you wanted to now?
If so, then perhaps the next thing to do is instead of creating separate plateXSound and plateXStart local variables, you can store all of this information in a Lua table/array where you can access plate information by their number. Something like this…
-- Creates a plate table/object. local function newPlate() local self = {} self.onTouch = function(event) audio.play(self.sound, { channel = self.audioChannel, duration = 2000 }) self.startTime = system.getTimer() end return self end -- Create all plates an store them to a table. local plates = {} for plateNumber=1,24 do plates[plateNumber] = newPlate() plates[plateNumber].audioChannel = plateNumber + 1 end function playC(event) -- ... plates[1].sound = strum[2] plates[2].sound = strum[4] plates[3].sound = strum[5] plates[4].sound = strum[7] plates[5].sound = strum[9] plates[6].sound = strum[10] -- ... end strumplate.plate1:addEventListener("touch", plates[1].onTouch)
And maybe instead of creating a separate plates{} table like it did above, perhaps it should be made part of your strumplates table. You can merge the 2 concepts together to make more manageable.
Er. I thought I had but it actually isn’t working at all when I use the seek function (if I comment out the seek lines it works, but obviously plays from the start of the sample).
Is this the correct way to stop, play, and seek to a new point when the chord changes? The documentation only gives examples of audio that’s already playing.
The for loop is incredibly useful, thank you. That’s what I’ve been trying to do all along, I think. Will try it out once I’ve resolved what’s going on with the seeking.
You can call audio.seek() before calling audio.play(). You just need to pass in the handle returned by audio.loadSound() to audio.seek() instead of the channel. You would then call audio.play() after audio.seek() using the same handle. We don’t document this, but it’s definitely supported. So, it should look something like this…
The above works for me. And you definitely want to call seek() before play() or else you’ll hear a tiny bit of the beginning of the sound before the seek takes effect.
My understanding is that this is only an issue if you are playing the same sound/audioHandle on multiple channels at the same time. I don’t believe this will ever be an issue in your app.