Strange behaviour of sampleplayer:setPlayRange

,

Hello, I have problems using playdate.sound.sampleplayer:setPlayRange(). I have an audio file that is 1 second long and contains 10 samples of 0.1 seconds. At some point in the game, I would like to play one of these samples as an audio effect, selecting which sample to play randomly. Code example:

local pd <const> = playdate
local gfx <const> = pd.graphics
math.randomseed(pd.getSecondsSinceEpoch())

local i = nil
local playTime = 2*1000 -- (ms) Play sample each 2 seconds
local sampleTimer = nil
local mySamples = pd.sound.sampleplayer.new("sounds/test")
local sampleRate = 44100 -- (1/s)
local sampleLength = 0.1 -- (s) Length of one "sample", not the total length of the file
local sampleFrame = sampleRate*sampleLength -- (= 4410) is this the "frame offset"???

local function resetTimer(time)
    sampleTimer = pd.timer.new(time, time, 0, pd.easingFunctions.lin)
end

resetTimer(playTime)

function pd.update()
    if sampleTimer.value == 0 then
        i = math.random(0, 9)
        mySamples:setPlayRange(sampleFrame*i, sampleFrame*(i+1))
        mySamples:play()
        resetTimer(playTime)
    end

    pd.timer.updateTimers()
    gfx.sprite.update()
    gfx.clear()
    gfx.drawText("Timer: " .. sampleTimer.value/1000, 50, 50)
    if i then
        gfx.drawText("Last sample played: " .. i, 50, 75)
    end
end

With that code, the problem that I am having is that the audio is played from the beginning of the file (instead from the point when I want it to start) until the point when I want it to stop. I have also tried fixed values instead of random ones and the same thing happens, it always plays from the beginning of the file. I don't know if this is some kind of bug or if I am doing something wrong (at first I didn't understand the meaning of "frame offset" in the documentation, so maybe I am using the function wrong).

I would appreciate if someone could help me with this. I know that I could just load the 10 samples as 10 individual audio files, but i would like to understand what is happening here.

(I am using the Playdate SDK 1.10.0 with the Lua API, running the Simulator on Windows 10, and I am using this VSCode template for compiling)

I'm not near a computer to check these, sorry. But...

Does playdate.sound.sampleplayer:setOffset(seconds) have the same problem?

You could also try the command immediately after a play, or play+setPaused(true)+setOffset+setPaused(false)

Thank you for the suggestion. I have tried what you propose (the :setPause() variant too) and it doesn't quite work. Indeed, if I add the line

mySamples:setOffset(sampleLength*i)

to the code, the audio played is shorter, but it does not fully correspond to the samples in the audio file: for some values of i, a small portion of the previous sample is played before the sample that I want to play. I have also tried spacing out the samples a little bit more (0.2 seconds per sample, so the audio file's total length is 2 seconds) and it seems the same thing happens.

I don't know if I can attach .wav files here, but if someone is interested and want to try the code, I can upload the audio files that I am using to Google Drive and link it here.

I'm having the same issue. I wasn't sure what the "frames" were, but it seems they are the sample rate times the length of playback you want. It appears that setPlayRange doesn't change the beginning of the sample, but does change the end.
I made a simple test

import "CoreLibs/graphics"
import "CoreLibs/sprites"
import "CoreLibs/animation"
import "CoreLibs/easing"

local gfx <const> = playdate.graphics
local geom <const> = playdate.geometry
local snd <const> = playdate.sound


local sound_buffer
local player

local total_frames

function startup()
    sound_buffer = snd.sample.new("sounds/sting")
    player = snd.sampleplayer.new(sound_buffer)
    local fps = snd.getSampleRate()
    total_frames = player:getLength() * fps
end

startup()

local slot = 0
function playdate.update()
    if not player:isPlaying() then
        print("Playing slot: "..slot)
        player:setPlayRange(total_frames * (slot * 0.25), total_frames * ((slot + 1) * 0.25))
        slot += 1
        if slot == 4 then
            slot = 0
        end
        player:play()
    end
end

I'd expect this to play each quarter of the sample one after another, but in fact it plays the first quarter, then the first half, then the first 3/4, then the whole sample.

Thank you for the feedback on this issue and sorry to reply to you so late, this week I've been too busy.

Yes, the behaviour that I encounter with :setPlayRange is the same as yours, I think we are facing the same problem. I am changing the category of this thread from "Get Help" to "Bug Reports", as I now think that this is a problem of the :setPlayRange method, and not of my code (but maybe I'm wrong of course). I hope the method get fixed.

Oof. I can't believe this has been on my todo list since May. :sob:

So yeah, there's a bug where if you don't specify an offset for playback it starts at the beginning of the file instead of the beginning of the play range. There's an easy workaround, though, you can use player:setOffset() to tell it to start at the play range--but there's a hitch: instead of frame offsets like setPlayRange() uses, the argument to setOffset() is in seconds. :confused: I might add a separate setPlayOffset() that takes a frame offset instead..

I've filed an MR for this, but I didn't flag it for next release since we have a workaround and I don't want it to delay 1.13.

First of all, thank you for your answer and sorry for replying so late.

The solution you speak of is something like what @matt suggested. When I use player:setOffset() in conjunction with player:setPlayRange() the audio that is played is indeed shorter, but it does not start where I want it to (and as I write it in the code).

I have upload here link to google drive the code and the audio file that I am using. You can try and see if you get the same problem.

From my recent experience with fileplayer:setOffset(), I would say this might be down to the buffer of the player. Try calling setOffset((sampleLength*i)+bufferSize). I guess the bufferSize is probably small by default for samplePlayer but I guess noticable for you use case?