Saving microphone output

Hi! I'm making an animation program, and I want to add sound support with the microphone, but I'm not sure how to save samples recorded by the microphone. Seeing pd.datastore, the only way to save it would be to add it to a table and save that, but I'm not sure if that'll work out. I would greatly appreciate any help!

Looking at the docs, microphone input gives you a sample object. Use playdate.sound.sample:save(filename) to save the file. Use datastore only for things like settings IMO.

2 Likes

by any chance do you know where it saves it, because it's confirmed saving, but I swear I cannot find the file. Is it in data or in the files

Ha, yeah. It's in your playdate SDK location. In Disk/Data/(your game id)/

1 Like

Thanks! I looked there and I couldn't find it, I guess it was a error on my part

Yeah, so I ran it on device and it didn't show up there either.

function recording_done_callback()
    recording_recording = false
    recording_buffer:save(animation[1])
    recording_buffer:play()
end

this is my code. It works, as the recording buffer is played. Animation[1] contains a string. I really don't know why it doesn't work.

I was doing a similar thing in a game I've been working on recently, and dealing with the sample files can be a little confusing.

Here's a simplified example of what ended up working for me:

recordPlayer handles the listening, and playRecording creates a sample player to play the result, then performs a callback action when the sample is finished playing.

function self:init( scene )
    self.buffer = playdate.sound.sample.new( 2, playdate.sound.kFormat16bitMono )
    self.listening = false
    self.playerSamplePath = 'playerSample'
end

function self:recordPlayer()
    Sound.micinput.startListening()
    Sound.micinput.recordToSample( self.buffer, function ( sample )
        Sound.micinput.stopListening()
        self.listening = false
        sample:save( self.playerSamplePath )
    end)
end

function self:playRecording()
    if type( self.playerSamplePath ~= 'string' then
        self:progressDialogue()
        return
    end

    self.buffer:load( self.playerSamplePath )
    if self.buffer == nil then
        return
    end

    local player = Sound.sampleplayer.new( self.buffer )
    player:play()
    player:setFinishCallback( function()
        self:progressDialogue()
    end)
end

Hope this is helpful!

Based on your code it seemed like I already did everything well, I'm not sure why it doesn't work.I really tried my best with it, but it just refuses to save. I've given up trying to debug it myself, here is the code for the recording scene:

function recording_init()
    menu:removeAllMenuItems()
    recording_recording = false
    local _menu_button, _error_1 = menu:addMenuItem("menu", function()
        menu_init()
        scene = "menu"
    end)
end
function recording_update()
    image_rec_booth:draw(0, 0)
    if recording_recording == false then
        if pd.buttonJustPressed(pd.kButtonA) then
            recording_recording = true
            recording_buffer = playdate.sound.sample.new(#animation[2]/playing_fps, playdate.sound.kFormat16bitMono)
            pd.sound.micinput.startListening()
            pd.sound.micinput.recordToSample(recording_buffer, recording_done_callback)
            playing_frame = 1
        end
        recording_buffer:save(animation[1])
    end
    if recording_recording == true then
        if playing_frame == 1 then
            pd.display.setRefreshRate(playing_fps)
            animation[2][playing_frame]:draw(0, 0)
            playing_frame += 1
        elseif playing_frame == #animation[2] then
            animation[2][playing_frame]:draw(0, 0)
            pd.display.setRefreshRate(30)
        else
            animation[2][playing_frame]:draw(0, 0)
            playing_frame += 1
        end
        image_recording:draw(298, 8)
        if pd.buttonJustPressed(pd.kButtonB) then
            pd.sound.micinput.stopRecording()
            playing_frame = #animation[2]
        end
    end
end
function recording_done_callback()
    pd.sound.micinput.stopListening()
    recording_recording = false
    recording_buffer:save(animation[1])
    recording_buffer:play()
end

@sheep42 a quick question, where did it save the files for you? It would truly help me out.

Wait a second.... I just saved it as a wav to see if that worked, and it totally did! I'm not sure why though. I'll try using this, maybe it'll work.

For me the file is saved at Data/com.my.namespace/playerSample on the Playdate disk.

I'm not sure you'll be able to play the sound if you save it as a wav, but definitely give it a go.

Not able to test and debug your code at the moment, but just looking at it, I'd say:

  • Actually, just remembered that new takes the buffer time in seconds as the first arg, not the path, so don't mind this one. Make sure that the path on the line recording_buffer = playdate.sound.sample.new(#animation[2]/playing_fps, playdate.sound.kFormat16bitMono) is a string: recording_buffer = playdate.sound.sample.new( tostring( #animation[2] ) .. '/playing_fps', playdate.sound.kFormat16bitMono)

  • Maybe don't do the buffer save inside of your first if branch, just save it in recording_done_callback.

Thanks for the tips! I do already save the sound in the recording done callback, and the timing is correct as well, regarding the fact that the wav file it created was exactly what I recorded. I did find something in the middle of writing this though, and it seemed to fix my problem! The thing is, there's already a folder with the exact same name as the sound because it stores the animation, so what I did is I just saved the sample in the folder under the name sound and it completely worked. I also managed to load it. I'll close it with this message, and I'll try to confirm it's a bug, then write a bug report. Thanks guys!

1 Like