Need help getting audio to loop seamlessly a certain amount of times

I am attempting to create a functional instrument like the one from Ocarina of Time. While I have been able to make two samples (the start of the note and the hold of the note) go from one another seamlessly, and even infinitely loop the hold of the note, I have been unable to make the held note end after a certain amount of repetitions. (Much a less when a button stops being held) I have attempted quite a few things to get it to work but have not been met with much success.

Some things which I have tried and the issues I ran into:
Using .isPlaying and an if statement:
This is already sort of a difficult thing to work with for my purposes. But even then the loop is not seamless with a noticeable gap in between each loop.

Using an IENumerator with a for loop to play the sound a certain amount of times and wait while the note is playing via yield return null. I’m not 100% sure why this didn’t work, I am not super great at coding. Though even if it worked as I envisioned I think it would run into the same issues as the if statement due to the use of .isPlaying and decided to shelf the idea.

Creating a function that uses a while loop that sets the audio to clip to loop until AudioSettings.dspTime to catches up to the time where it would be at x amount of loops later. I really felt like this would be the one that should work. But however I seem to be coding it causes unity to crash. Most likely because the loop is unable to be exited. But I cant seem to use AudioSettings.dspTime and any amount of math without it crashing. I seem to be unable to get a static number.

Any help, tutorials, or learning resources would be appreciated and I can share my messy code on request for anyone willing to poke aound on my behalf. Thanks!

Try using scheduled scheduling, not polling:

// two sources so one can load while the other plays
AudioSource srcA, srcB;
double startTime;

void PlayNote(int loopCount)
{
    // schedule ~200ms in the future so the audio system can pre-buffer
    startTime = AudioSettings.dspTime + 0.2;

    // schedule the "start" sample on source A
    srcA.clip = startClip;
    srcA.loop = false;
    srcA.PlayScheduled(startTime);

    // schedule the "hold" sample on source B right when A finishes
    double holdStart = startTime + (double)startClip.samples / startClip.frequency;
    srcB.clip = holdClip;
    srcB.loop = true;
    srcB.PlayScheduled(holdStart);

    // tell source B when to stop
    double endTime = holdStart + (double)holdClip.samples / holdClip.frequency * loopCount;
    srcB.SetScheduledEndTime(endTime);
}

void StopNoteEarly()
{
    // for button-release: stop at the end of the current loop
    srcB.loop = false;
    // or cut immediately: srcB.Stop();
}

This is amazing thank you so so much. I might come back with some questions on how to make things a bit more verbose if I can’t figure it out myself but Ill be certain to mess with it on my own first. Thank you thank you!

Hi, So update I have been working with the code for a bit an unfortunately come to the conclusion that I was mistaken in asking for loops for the functionality I am trying to make. Though I was still able to use this code to make the starting sample and the mid sample mix while allowing the player to hold the mid note as long as they want. I have been thoroughly unable to make the end of the note blend seamlessly when the button is released and the mid note hold ends. I was able to make the when using a single button press but that isn’t the dynamic instrument note hold I was hoping to make.

As far as I am aware there is no way to dynamically update the * loops part of the method (though not for a lack of trying on my end). I have tried making it so that a separate method with just the last note gets called but the math of the exact timing seems to not want to work out. Even if I try to use something like a for loop which increments on the same delay as the clip using the .clip.length function in a WaitForSeconds call. Any continued or other help you or anyone else who stumbles on this thread would be very much appreciated.

Check that you’re not actually importing an MP3… those will almost never be able to loop because of how the music is chopped into frames and that last frame will ALWAYS have a chunk of emptiness in it.

Try a WAV format… I don’t know what other OGG-stuff might also be frame-based like MP3 so I don’t recommend that. Wave will be the closest pure PCM

I swear I used to have a DSP-based looper for Unity but I cannot find that code right now, sorry.

You can also write your own mixer of samples using the OnAudioFilterRead(); callback… it’s pretty simple to start from the metronome they give you in the documentation:

WARNING: Just remember the above runs on another thread!!!

Hi, thanks for the response. All of my files are in the .ogg format and are able to sync up to one another. But the thing that seems to be tripping me up is getting the last part of the note to sync up when the button is released. I will be sure to read some of the documentation you provided and give that a shot though.

There will always be some lag. If you simply toggle a looping AudioSource volume from 1 to 0 as you press / release a key, that will essentially be the fastest it can respond.

It will vary by target and OS.

Hi this comment actually made me rethink my approach. Rather than having 3 separate parts of a note I now just have 1 looping note constantly playing which I increase the volume of over a third of a second while the button is held and have it decrease over the same amount of time while the button is released. This approach isn’t perfect, it makes the note playing sound slightly artificial. But for the scope of the project and my own limitations it is rather functional while still being fairly presentable. So thanks for giving me the inspiration to finally put this whole thing to rest.