Playing audio file whilst running other code

Started by Arti, Feb 16, 2023, 03:12 AM

Previous topic - Next topic

Arti

Hiya im very new to computercraft and am making a simple system that lets me play sounds on a computer wirelessly. Ive been running into the issue that when playing audio the whole process hangs so I can no longer tell the program to stop playing audio remotely. I have tried to use multishell and corutines to no avail, likely me doing something very obviously wrong. The audio section of my code is below

function audioPlayer(audioReq)
    if fs.exists(audioReq .. ".dfpwm") == true then
        print("sound file found")
        playSound(audioReq)
    else
        shell.run("wget REDACTED" .. audioReq .. ".dfpwm")
        print("sound file downloaded")
        playSound(audioReq)
    end
end

function playSound(soundFile)
    print("sound now playing")
    local dfpwm = require "cc.audio.dfpwm"
    local decoder = dfpwm.make_decoder()

    for chunk in io.lines(soundFile .. ".dfpwm", 16 * 1024) do
        local buffer = decoder(chunk)
        while not speaker.playAudio(buffer) do
            os.pullEvent("speaker_audio_empty")
        end
    end
end



while true do
    local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
    print(message)

    if(channel == 1) then
        if message == "stop" then
            print("stopping Audio")
            stopSound()
        else
            audioPlayer(message)
        end
    end
    if(channel == 2) then
        imageLoader(message)
    end
    if(channel == 3) then
        --soon
    end
end

The idea is that whenever i request a sound to be played from my pocket computer it downloads it from my personal website and plays it, i can then stop the sound by sending a stop command. The downloading and playing sections work fine but I can not stop due to the while loop in playSound (i think)

What can be changed to make my code work?

Lupus590

Have you tried the parallel API? It does the coroutine management

Arti

I tried to use parallell wait for any to stop the play sound function from yielding when running it as a corutine but couldnt get it to work well. I managed to get it to continue playing which was fine, but I was unable to work out how to stop it or allow the other functions to run alongside it.

If possible could you show me how I would integrate this?

Lupus590

#3
I would have a function that manages the playing and stopping/pausing and another that manages user/rednet input. Each of the functions should have its own event loop.

In psudo lua code I think it would look something like this.


isPlaying = false
songFile = nil

function rednetLoop()
  while true do
    command = rednet.pull()
    if command == stop
      isPlaying = false
    elseif command == play
      songFile = fetchFile(command.song )
      isPlaying = true
      os.queueEvent(play)
    end
  end
end

function playerLoop()
  while true do
    while isPlaying
      for chunk in songFile
        buffer = decode(chunk)
        speaker.play(buffer)
        os.pullEvent(speaker_empty)
      end
      isPlaying = false -- you can remove this if you want it to loop
    end
    os.pullEvent(play)
  end
end

parallel.waitForAll(playerLoop, rednetLoop)   


Why do I queue and pull custom events? It's slightly more efficient than sleeping for a few seconds at a time until the isPlaying var is true.

Arti

Thanks for the help! I found a way of doing it with my own corutines thanks to this great document i found on an old post from 2014 https://docs.google.com/document/d/1UU-bSCgLqwAQixldXmDzvEFPACaieph3qs08WreQlZs/edit#

explains corutines really well.

This is the code which I ended up with

function playSound(soundFile)
    print("sound now playing")
    local dfpwm = require "cc.audio.dfpwm"
    local decoder = dfpwm.make_decoder()

    for chunk in io.lines(soundFile .. ".dfpwm", 16 * 1024) do
        local buffer = decoder(chunk)
        while not speaker.playAudio(buffer) do
            --yields here
            os.pullEvent("speaker_audio_empty")
        end
    end
end


local musicCorutine = coroutine.create ( playSound )


while true do
    local event = { os.pullEvent() }

    if event[1] == "modem_message" then
        local channel = event[3]
        local message = event[5]

        if(channel == 1) then
            if message == "stop" then
                print("stopping Audio")
                playing = false
            else

                audioLoader(message)
                playing = true
                musicCorutine = coroutine.create ( playSound )
                coroutine.resume(musicCorutine, message)
            end
        end
        if(channel == 2) then
            imageLoader(message)
        end
        if(channel == 3) then
            --soon
        end
    end

    if not playing then
        if coroutine.status(musicCorutine) == "running" then
            coroutine.yield(musicCorutine)
        end
    else
        print("resuming")
        if coroutine.status(musicCorutine) == "suspended" then
            coroutine.resume(musicCorutine)
        end
    end
end