gordonklaus / portaudio

Go bindings for the PortAudio audio I/O library
MIT License
704 stars 95 forks source link

Playback cutoff in blocking stream mode #38

Closed nguillaumin closed 2 years ago

nguillaumin commented 4 years ago

Hi,

I'm trying to decode and play an OGG file, but the playback is always cut off before the end of the file. I can get it to play fully if I add a few more stream.Write() calls at the end of my program, as if the output buffer was not completely exhausted somehow.

I wanted to try the play.go example to compare, but I was not able to find a WAV file that worked with the example code. I also tried with decoding a FLAC file but ran into the same issue.

Here's the code, and I'm attaching the sample OGG file as well. It's probably something wrong in my code and how I interact with the buffers, but I can't figure it out, any help would be appreciated!

Thanks!

package main

import (
    "fmt"
    "io"
    "os"

    "github.com/gordonklaus/portaudio"
    "github.com/jfreymuth/oggvorbis"
)

func main() {

    // PortAudio output buffer
    paBuf := make([]float32, 1024*8)

    file, err := os.Open("03 - OGG Sample.ogg")
    chk(err)
    defer file.Close()

    oggReader, err := oggvorbis.NewReader(file)
    chk(err)

    portaudio.Initialize()
    defer portaudio.Terminate()
    h, err := portaudio.DefaultHostApi()
    chk(err)

    fmt.Printf("Channels: %v, SampleRate: %v\n", oggReader.Channels(), oggReader.SampleRate())
    streamParameters := portaudio.StreamParameters{
        Output: portaudio.StreamDeviceParameters{
            Device:   h.DefaultOutputDevice,
            Channels: oggReader.Channels(),
            Latency:  h.DefaultOutputDevice.DefaultLowOutputLatency,
        },
        Input: portaudio.StreamDeviceParameters{
            Device: nil,
        },
        SampleRate:      float64(oggReader.SampleRate()),
        FramesPerBuffer: len(paBuf),
    }

    stream, err := portaudio.OpenStream(streamParameters, &paBuf)
    chk(err)

    defer stream.Close()
    chk(stream.Start())
    defer stream.Stop()

    var buffer []float32
    var n int
    for err != io.EOF {
        // Read enough data to fill paBuf
        for len(buffer) < len(paBuf) && err == nil {
            b := make([]float32, len(paBuf))
            n, err = oggReader.Read(b)
            buffer = append(buffer, b[:n]...)
        }

        fmt.Printf("buffer length: %v\n", len(buffer))

        if len(buffer) > 0 {
            n = copy(paBuf, buffer)
            buffer = buffer[n:]
            writeErr := stream.Write()
            chk(writeErr)
        }
    }

    fmt.Printf("finished. remaining in buffer: %v\n", len(buffer))
    for len(buffer) > 0 {
        n = Min(len(buffer), len(paBuf))
        copy(paBuf, buffer[:n])
        buffer = buffer[n:]
        writeErr := stream.Write()
        chk(writeErr)
    }

    // Uncomment these to get the complete playback
    // stream.Write()
    // stream.Write()
    // stream.Write()
    // stream.Write()

}

func chk(err error) {
    if err != nil {
        panic(err)
    }
}

func Min(x, y int) int {
    if x < y {
        return x
    }
    return y
}

03 - OGG Sample.zip

gordonklaus commented 2 years ago

Sorry for never replying to this issue. If it's still a problem and you think it's an issue with portaudio-go, please re-open.