gopxl / beep

A little package that brings sound to any Go application. Suitable for playback and audio-processing.
MIT License
284 stars 13 forks source link

No sound on macOS #145

Closed flowchartsman closed 6 months ago

flowchartsman commented 9 months ago

OS version: 14.1.1 (Sonoma)

Running the to buffer or not to buffer example does not play any sound when enter is struck. No errors are displayed, it's simply silent.

MarkKremer commented 9 months ago

Does the audio work in other examples? I suspect the fmt.Scanln() may not detect the enter press in the desired way and be blocking.

flowchartsman commented 9 months ago

Whatever it is, it's not the call to Scanln() the following also does not play anything:

package main

import (
        "log"
        "os"
        "time"

        "github.com/gopxl/beep"
        "github.com/gopxl/beep/mp3"
        "github.com/gopxl/beep/speaker"
)

func main() {
        f, err := os.Open("gunshot.mp3")
        if err != nil {
                log.Fatal(err)
        }

        streamer, format, err := mp3.Decode(f)
        if err != nil {
                log.Fatal(err)
        }

        speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/60))

        buffer := beep.NewBuffer(format)
        buffer.Append(streamer)
        streamer.Close()

        for {
                shot := buffer.Streamer(0, buffer.Len())
                speaker.Play(shot)
                time.Sleep(1 * time.Second)
        }
}

I can confirm, however, that at least the tone generator works.

MarkKremer commented 9 months ago

Hmm, that's going to be a bit hard to debug. Your example works for me.

If the tone generator works, that would suggest that the speaker works. If you have no errors, I would assume the mp3 can be read and parsed successfully. And I'm assuming you're using the same gunshot.mp3 file as me so I don't see why that would fail. The buffer doesn't do anything that would be different for us either.

I think this is going to be a game of test a lot of stuff to narrow it down. Below are some things to try. But if you can narrow it down further that would be great.

Just to confirm, this works?

package main

import (
    "log"
    "time"

    "github.com/gopxl/beep"
    "github.com/gopxl/beep/generators"
    "github.com/gopxl/beep/speaker"
)

func main() {
    format := beep.Format{
        SampleRate:  44100,
        NumChannels: 2,
        Precision:   2,
    }
    sine, err := generators.SineTone(format.SampleRate, 400)
    if err != nil {
        log.Fatal(err)
    }
    streamer := beep.Take(format.SampleRate.N(time.Second/2), sine)

    speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/60))

    buffer := beep.NewBuffer(format)
    buffer.Append(streamer)

    for {
        shot := buffer.Streamer(0, buffer.Len())
        speaker.Play(shot)
        time.Sleep(1 * time.Second)
    }
}

Could you also check the other functions for errors and check if the buffer is filled. I've also increased the speaker buffer size here.

package main

import (
    "fmt"
    "log"
    "os"
    "time"

    "github.com/gopxl/beep"
    "github.com/gopxl/beep/mp3"
    "github.com/gopxl/beep/speaker"
)

func main() {
    f, err := os.Open("gunshot.mp3")
    if err != nil {
        log.Fatal(err)
    }

    streamer, format, err := mp3.Decode(f)
    if err != nil {
        log.Fatal(err)
    }

    err = speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/30))
    if err != nil {
        log.Fatal(err)
    }

    buffer := beep.NewBuffer(format)
    buffer.Append(streamer)
    err = streamer.Close()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Buffer size: %d\n", buffer.Len())

    for {
        shot := buffer.Streamer(0, buffer.Len())
        speaker.Play(shot)
        time.Sleep(1 * time.Second)
    }
}
flowchartsman commented 9 months ago

Just to confirm, this works?

Kind of. This first example makes a brief popping sound with perhaps a few millis of the tone before going silent.

I've also increased the speaker buffer size here.

This works as intended!

MarkKremer commented 9 months ago

Can this issue be marked as solved?

flowchartsman commented 9 months ago

Up to you! I have tested and every other example works fine, but the core of the issue, really, is that I cannot run beep/examples/tutorial/3-to-buffer-or-not-to-buffer/main.go on my system as it is, and it was difficult to figure out that it was buffer size related, so maybe there's room for a doc issue in this.

The tutorial mentions increasing the buffer size if latency is observed, but I think it might be helpful to mention that this could also include no sound whatsoever. I think a comment in the tutorial and a comment in the example file itself might go a long way, but there's probably also room for some heuristics that could attempt to determine the ideal sample rate.

The documentation could probably also use a treatment on selecting speaker sample rate for use cases that involve playing more than one sample. I imagine many users will want to use this library as I am attempting to, which is to play one of a few different samples as unique notification sounds. The current documents and examples all involve initializing the speaker with the sample rate of the file being played, which makes complete sense if you're only playing one sound, but I think there's room for docs on selecting a speaker sample rate independent of any of the files as well as how you might select that and how to go about ensuring all of your files play as they're intended.

It might even be worth having a func ResampleAuto(quality int, audioSampleRate SampleRate, s Streamer) (or ResampleForSpeaker) that can reach into beep/speaker and get the current sample rate and scale the sample appropriately.

atljoseph commented 8 months ago

This has GOT to be a bug, but just not with beep, specifically.

I ran into this same issue. I cannot use ANY package to which I import github.com/gopxl/beep/speaker. Simply hangs and never does anything. Even happens when importing _, like this: _ "github.com/gopxl/beep/speaker".

This is useless code, as it doesn't produce any output or fatal error or panics:

package filesystem

import (
    "fmt"
    _ "github.com/gopxl/beep/speaker"
)

func Debug(filename string) error {
    fmt.Println("file", filename)
    return nil
}

Can't even play a tone from the tone-player example in this repo.

I'd expect a failure at the least.... Not just ... nothing.

After much time, it seems that the importation of github.com/ebitengine/oto/v3 is responsible for this bad behavior.

On an M1 Mac.

Link: https://github.com/ebitengine/oto/issues/233

MarkKremer commented 8 months ago

Hey, you are very disrespectful in the linked issue and don't seem to help us debug the problem in any meaningful way up untill now.

You said to have moved on to using a different project. So I won't be helping you with this further. Goodbye

atljoseph commented 8 months ago

Thanks. Yes, I got a bit annoyed and I regret that. As I stated early on there, I am thankful for the code y’all spent time and energy writing so people could have it for free. Please don’t mistake the annoyance for not being thankful. The main reason for my heat tonight was not being heard after reporting something. The code still hangs for me on loading speaker. Already moved on though. People just don’t like to be called out, and that happened on both sides, so nobody was happy. I regret that too. Thanks again for the note.

On Sun, Mar 3, 2024 at 3:26 AM Mark Kremer @.***> wrote:

Hey, you are very disrespectful in the linked issue and don't seem to help us debug the problem in any meaningful way up untill now.

You said to have moved on to using a different project. So I won't be helping you with this further. Goodbye

— Reply to this email directly, view it on GitHub https://github.com/gopxl/beep/issues/145#issuecomment-1975085006, or unsubscribe https://github.com/notifications/unsubscribe-auth/AF632FIB5XRHY6NXK3CQTLDYWLNFBAVCNFSM6AAAAABCLGCZIWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSNZVGA4DKMBQGY . You are receiving this because you commented.Message ID: @.***>

atljoseph commented 8 months ago

Came back to this for a few minutes today, to make sure there was actually an issue still there... Seems to be.

This file represents a program that produces logs and completes:

package main

import (
    "fmt"

    "github.com/gopxl/beep"
    // "github.com/gopxl/beep/speaker"
)

func main() {
    fmt.Println("before")
    _ = beep.SampleRate(48000)
    fmt.Println("during")
    // speaker.Init(sr, 4800)
    fmt.Println("after")
}

But this one runs, never completes, and doesn't log anything:

package main

import (
    "fmt"

    "github.com/gopxl/beep"
    "github.com/gopxl/beep/speaker"
)

func main() {
    fmt.Println("before")
    sr := beep.SampleRate(48000)
    fmt.Println("during")
    speaker.Init(sr, 4800)
    fmt.Println("after")
}

The importation of github.com/gopxl/beep/speaker introduces the behavior change.

Versions:

Again, I have a workaround, but If I'm seeing it, then other people are. Figured you would like to know. Sorry about being a douche last weekend.

MarkKremer commented 8 months ago

So the speaker package doesn't really do anything until Init is called. So I'm wondering if the compilation process is just being a little slow (I've had that before). Could you try:

Could you try this with the verbose build option to see what it hangs on if it does?

atljoseph commented 7 months ago

cd examples/tone-player

Run.

go run -v .

No output.

Build.

go build -v

Build is completed immediately.

./tone-player

No output.

Result.

Gave each 10 minutes. From main branch. Nothing ever happened.

MarkKremer commented 7 months ago

Could you run the following snippets with go run -v -a . and reply with the output of each including the build output?

1.

package main

import (
    "fmt"

    "github.com/gopxl/beep"
    "github.com/gopxl/beep/speaker"
)

func init() {
    fmt.Println("init()")
}

func main() {
    fmt.Println("main() start")

    fmt.Println("speaker.Init()")
    err := speaker.Init(beep.SampleRate(41000), 4100)
    if err != nil {
        panic(err)
    }

    fmt.Println("main() end")
}

2.

package main

import (
    "fmt"

    _ "github.com/gopxl/beep/speaker"
)

func init() {
    fmt.Println("init()")
}

func main() {
    fmt.Println("main()")
}

3.

package main

import (
    "fmt"

    _ "github.com/ebitengine/oto/v3"
)

func init() {
    fmt.Println("init()")
}

func main() {
    fmt.Println("main()")
}

4.

package main

import (
    "fmt"
    "runtime"
)

func main() {
    fmt.Printf(
        "Hello world from %s/%s\n",
        runtime.GOOS,
        runtime.GOARCH,
    )
}

5.

package main

import (
    "fmt"

    _ "github.com/ebitengine/purego"
    _ "github.com/ebitengine/purego/objc"
)

func init() {
    fmt.Println("init()")
}

func main() {
    fmt.Println("main()")
}

6.

package main

import (
    "fmt"

    _ "github.com/ebitengine/purego/objc"
)

func init() {
    fmt.Println("init()")
}

func main() {
    fmt.Println("main()")
}
domingguss commented 7 months ago

Since no-one answered and I have the same issue here are my results, running on mac OS Big Sur 11.7.10 on a Intel Macbook Pro Late 2014.

TL;DR:

Only scenario 4 exits normally, all other ones hang indefinitely and after 5 minutes I did a ctrl-c.

  1. $ go run -v -a .
    internal/coverage/rtcov
    internal/unsafeheader
    internal/goarch
    internal/goos
    internal/godebugs
    internal/goexperiment
    runtime/internal/atomic
    internal/cpu
    internal/itoa
    math/bits
    internal/chacha8rand
    runtime/internal/math
    internal/abi
    runtime/internal/sys
    unicode/utf8
    internal/race
    sync/atomic
    unicode
    cmp
    internal/bytealg
    slices
    github.com/ebitengine/purego/internal/strings
    math
    runtime
    internal/reflectlite
    sync
    internal/testlog
    errors
    sort
    runtime/cgo
    internal/oserror
    internal/safefilepath
    path
    io
    strconv
    syscall
    bytes
    strings
    reflect
    regexp/syntax
    internal/syscall/execenv
    internal/syscall/unix
    time
    regexp
    io/fs
    internal/poll
    internal/fmtsort
    os
    github.com/ebitengine/purego
    fmt
    github.com/ebitengine/oto/v3/internal/mux
    github.com/ebitengine/purego/objc
    github.com/pkg/errors
    github.com/gopxl/beep
    github.com/ebitengine/oto/v3
    github.com/gopxl/beep/speaker
    example/hello
    ^Csignal: interrupt

2 .

$  go run -v -a .
internal/goarch
internal/goos
internal/coverage/rtcov
internal/unsafeheader
internal/godebugs
internal/goexperiment
internal/cpu
runtime/internal/atomic
internal/chacha8rand
internal/abi
runtime/internal/math
internal/itoa
runtime/internal/sys
math/bits
unicode/utf8
internal/race
sync/atomic
cmp
unicode
github.com/ebitengine/purego/internal/strings
internal/bytealg
math
slices
runtime
internal/reflectlite
sync
internal/testlog
runtime/cgo
errors
sort
internal/oserror
path
io
internal/safefilepath
strconv
syscall
bytes
strings
reflect
regexp/syntax
internal/syscall/execenv
internal/syscall/unix
time
regexp
io/fs
internal/poll
internal/fmtsort
os
fmt
github.com/ebitengine/purego
github.com/ebitengine/oto/v3/internal/mux
github.com/ebitengine/purego/objc
github.com/pkg/errors
github.com/gopxl/beep
github.com/ebitengine/oto/v3
github.com/gopxl/beep/speaker
example/hello
^Csignal: interrupt

3 .

$  go run -v -a .
internal/coverage/rtcov
internal/goarch
internal/godebugs
internal/goexperiment
internal/unsafeheader
internal/cpu
internal/goos
runtime/internal/atomic
internal/itoa
internal/abi
internal/chacha8rand
runtime/internal/math
runtime/internal/sys
math/bits
unicode/utf8
internal/race
sync/atomic
unicode
cmp
internal/bytealg
github.com/ebitengine/purego/internal/strings
math
slices
runtime
internal/reflectlite
sync
internal/testlog
runtime/cgo
errors
sort
internal/oserror
internal/safefilepath
path
io
strconv
syscall
bytes
strings
reflect
regexp/syntax
internal/syscall/execenv
internal/syscall/unix
time
regexp
io/fs
internal/poll
internal/fmtsort
os
github.com/ebitengine/purego
fmt
github.com/ebitengine/oto/v3/internal/mux
github.com/ebitengine/purego/objc
github.com/ebitengine/oto/v3
example/hello
^Csignal: interrupt

4 .

$ go run -v -a .
internal/godebugs
internal/coverage/rtcov
internal/unsafeheader
internal/goos
internal/goexperiment
internal/goarch
internal/cpu
runtime/internal/atomic
internal/itoa
math/bits
unicode/utf8
internal/race
internal/abi
internal/chacha8rand
runtime/internal/math
runtime/internal/sys
sync/atomic
unicode
cmp
internal/bytealg
slices
math
runtime
internal/reflectlite
sync
internal/testlog
errors
sort
internal/oserror
internal/safefilepath
io
path
strconv
syscall
reflect
internal/syscall/execenv
internal/syscall/unix
time
io/fs
internal/poll
internal/fmtsort
os
fmt
example/hello
Hello world from darwin/amd64

5 .

$ go run -v -a .
internal/goarch
internal/unsafeheader
internal/coverage/rtcov
internal/godebugs
internal/goos
internal/goexperiment
internal/cpu
runtime/internal/atomic
internal/itoa
internal/abi
runtime/internal/math
internal/chacha8rand
math/bits
runtime/internal/sys
unicode/utf8
internal/race
sync/atomic
cmp
unicode
slices
github.com/ebitengine/purego/internal/strings
internal/bytealg
math
runtime
internal/reflectlite
sync
internal/testlog
errors
sort
runtime/cgo
internal/safefilepath
internal/oserror
path
io
strconv
syscall
bytes
strings
reflect
regexp/syntax
internal/syscall/unix
internal/syscall/execenv
time
regexp
io/fs
internal/poll
internal/fmtsort
os
github.com/ebitengine/purego
fmt
github.com/ebitengine/purego/objc
example/hello
^Csignal: interrupt

6 .

  go run -v -a .
internal/unsafeheader
internal/godebugs
internal/goarch
internal/goos
internal/goexperiment
internal/coverage/rtcov
internal/cpu
runtime/internal/atomic
internal/itoa
math/bits
internal/abi
runtime/internal/math
internal/chacha8rand
runtime/internal/sys
unicode/utf8
internal/race
sync/atomic
unicode
cmp
internal/bytealg
slices
math
github.com/ebitengine/purego/internal/strings
runtime
internal/reflectlite
sync
internal/testlog
runtime/cgo
errors
sort
internal/oserror
internal/safefilepath
path
io
strconv
syscall
bytes
strings
reflect
regexp/syntax
internal/syscall/execenv
internal/syscall/unix
time
regexp
io/fs
internal/poll
os
internal/fmtsort
fmt
github.com/ebitengine/purego
github.com/ebitengine/purego/objc
example/hello
^Csignal: interrupt
domingguss commented 7 months ago

Interesting: when I downgrade all the way to v1.1.0, only scenarios 3, 5 and 6 keep getting stuck after it logs example/hello as shown above.

Scenarios 1,2 and 4 do finish, and the first tutorial also works...

from v1.2.0 it keeps hanging again in all scenarios including the tutorial, except for scenario 4 (obviously).

MarkKremer commented 7 months ago

Thanks for your help!!

In Beep v1.2 we've upgraded from Oto <1 to 3. From the output above I think it's save to say that we've narrowed down to be somewhere in github.com/ebitengine/purego or github.com/ebitengine/purego/objc.

I have created an issue in the purego repo (https://github.com/ebitengine/purego/issues/224). It would be great if anyone experiencing this issue could help out there if they have questions. :)

domingguss commented 7 months ago

Found a workaround!

TL;DR - manually set purego version to v0.7.0 after go get github.com/gopxl/beep/speaker

  1. First, I cleared my go.mod dependencies file.

  2. run code below

    
    package main

import ( "fmt"

_ "github.com/gopxl/beep/speaker"

)

func init() { fmt.Println("init()") }

func main() { fmt.Println("main()") }


3. The following happenend in the terminal:

$ go run -v -a . main.go:6:2: no required module provides package github.com/gopxl/beep/speaker; to add it: go get github.com/gopxl/beep/speaker $ go get github.com/gopxl/beep/speaker go: added github.com/ebitengine/oto/v3 v3.1.0 go: added github.com/ebitengine/purego v0.5.0 go: added github.com/gopxl/beep v1.4.0 go: added github.com/pkg/errors v0.9.1 go: added golang.org/x/sys v0.12.0 dominggus@dombp15 .../projects/doWork/go-playground go run -v -a .
internal/unsafeheader internal/goos internal/godebugs internal/coverage/rtcov internal/goexperiment internal/goarch runtime/internal/atomic internal/cpu internal/itoa internal/chacha8rand runtime/internal/math internal/abi runtime/internal/sys math/bits unicode/utf8 internal/race sync/atomic cmp unicode github.com/ebitengine/purego/internal/strings internal/bytealg slices math runtime internal/reflectlite sync internal/testlog runtime/cgo errors sort path internal/oserror io internal/safefilepath strconv syscall bytes strings reflect regexp/syntax internal/syscall/execenv internal/syscall/unix time regexp io/fs internal/poll internal/fmtsort os github.com/ebitengine/purego fmt github.com/ebitengine/oto/v3/internal/mux github.com/ebitengine/purego/objc github.com/pkg/errors github.com/gopxl/beep github.com/ebitengine/oto/v3 github.com/gopxl/beep/speaker example.com/mod ^Csignal: interrupt


4. Then it hangs as expected. Then I open the `go.mod` file again:

module example.com/mod

go 1.22.1

require ( github.com/ebitengine/oto/v3 v3.1.0 // indirect github.com/ebitengine/purego v0.5.0 // indirect github.com/gopxl/beep v1.4.0 // indirect github.com/pkg/errors v0.9.1 // indirect golang.org/x/sys v0.12.0 // indirect )

5. Then, I change purego's version to `v0.7.0`

6. Voila, it works!

go run -v -a . internal/goarch internal/goexperiment internal/coverage/rtcov internal/unsafeheader internal/godebugs internal/goos internal/cpu internal/abi runtime/internal/atomic runtime/internal/math internal/itoa internal/chacha8rand runtime/internal/sys math/bits unicode/utf8 internal/race sync/atomic unicode cmp github.com/ebitengine/purego/internal/cgo github.com/ebitengine/purego/internal/strings internal/bytealg slices math runtime internal/reflectlite sync internal/testlog runtime/cgo errors sort internal/oserror internal/safefilepath path io strconv syscall bytes strings reflect regexp/syntax internal/syscall/execenv time internal/syscall/unix regexp io/fs internal/poll internal/fmtsort os fmt github.com/ebitengine/oto/v3/internal/mux github.com/pkg/errors github.com/gopxl/beep github.com/ebitengine/purego github.com/ebitengine/purego/objc github.com/ebitengine/oto/v3 github.com/gopxl/beep/speaker example.com/mod init() main()

domingguss commented 7 months ago

Would upping purego's version here fix it?

This is actually my first time trying to create an application with Golang, pls forgive me my noobness :)

TotallyGamerJet commented 7 months ago

You can update a dependency like so go get github.com/ebitengine/purego@latest

EDIT: I was actually able to reproduce the issue with the current beep version #151 does fix it for me.

MarkKremer commented 6 months ago

Merged :+1: I assume this is fixed now but if anyone still has troubles after updating, don't hesitate to re-open or comment.

MarkKremer commented 6 months ago

@domingguss maybe I'm a bit too hasty here. Can you confirm that it works now?

domingguss commented 6 months ago

@domingguss maybe I'm a bit too hasty here. Can you confirm that it works now?

Yes it works with 1.4.1, thank you all!