faiface / beep

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

Segmentation Fault #102

Open cfair-beckman opened 3 years ago

cfair-beckman commented 3 years ago

I have a program where I am trying to execute beep and sometimes I want to proceed to the next one before the first one is finished....when I do that (by calling the code to play the sound from a go routine) I end up getting a segmentation fault in the library. It does not happen EVERY time but it happens fairly often and is really reproducible....

fatal error: unexpected signal during runtime execution [signal SIGSEGV: segmentation violation code=0x1 addr=0x7ff1e8044 pc=0x7ff22632cc7a]

runtime stack: runtime.throw(0x924fd0, 0x2a) /usr/lib/go/src/runtime/panic.go:1116 +0x72 runtime.sigpanic() /usr/lib/go/src/runtime/signal_unix.go:704 +0x4ac

goroutine 291 [syscall]: runtime.cgocall(0x8323d0, 0xc000571d88, 0xc000358000) /usr/lib/go/src/runtime/cgocall.go:133 +0x5b fp=0xc000571d58 sp=0xc000571d20 pc=0x40831b github.com/hajimehoshi/oto._Cfunc_snd_pcm_writei(0x7ff1e80085c0, 0xc000358000, 0x5be, 0x0) _cgo_gotypes.go:162 +0x4e fp=0xc000571d88 sp=0xc000571d58 pc=0x81eb2e github.com/hajimehoshi/oto.(driver).TryWrite.func1(0xc0008a40c0, 0xc0002ea000) /home/christopher/go/pkg/mod/github.com/hajimehoshi/oto@v0.3.1/driver_linux.go:140 +0xd6 fp=0xc000571dd0 sp=0xc000571d88 pc=0x81f856 github.com/hajimehoshi/oto.(driver).TryWrite(0xc0008a40c0, 0xc0002ea0f0, 0x10, 0x8000, 0x100, 0x0, 0x0) /home/christopher/go/pkg/mod/github.com/hajimehoshi/oto@v0.3.1/driver_linux.go:140 +0x1d8 fp=0xc000571e60 sp=0xc000571dd0 pc=0x81f258 github.com/hajimehoshi/oto.(*driverWriter).Write(0xc0008d2200, 0xc0002ea000, 0x100, 0x8000, 0x0, 0x0, 0x0) /home/christopher/go/pkg/mod/github.com/hajimehoshi/oto@v0.3.1/context.go:136 +0x10c fp=0xc000571ef0 sp=0xc000571e60 pc=0x81deec io.copyBuffer(0xadbbe0, 0xc0008d2200, 0xadbc00, 0xc0008a4100, 0xc0002ea000, 0x8000, 0x8000, 0x0, 0x0, 0x46f701) /usr/lib/go/src/io/io.go:411 +0x206 fp=0xc000571f68 sp=0xc000571ef0 pc=0x47afa6 io.Copy(...) /usr/lib/go/src/io/io.go:368 github.com/hajimehoshi/oto.NewContext.func1(0xc0008d2220) /home/christopher/go/pkg/mod/github.com/hajimehoshi/oto@v0.3.1/context.go:79 +0x69 fp=0xc000571fd8 sp=0xc000571f68 pc=0x81f569 runtime.goexit() /usr/lib/go/src/runtime/asm_amd64.s:1374 +0x1 fp=0xc000571fe0 sp=0xc000571fd8 pc=0x46f701 created by github.com/hajimehoshi/oto.NewContext /home/christopher/go/pkg/mod/github.com/hajimehoshi/oto@v0.3.1/context.go:78 +0x250

goroutine 1 [syscall, locked to thread]: github.com/wailsapp/wails/lib/renderer/webview._Cfunc_CgoWebViewLoop(0x1af5830, 0x1, 0x0) _cgo_gotypes.go:187 +0x4d github.com/wailsapp/wails/lib/renderer/webview.(webview).Loop.func1(0xc000532028, 0x1, 0x0) /home/christopher/go/pkg/mod/github.com/wailsapp/wails@v1.8.0/lib/renderer/webview/webview.go:279 +0x5d github.com/wailsapp/wails/lib/renderer/webview.(webview).Loop(0xc000532028, 0x1, 0x1) /home/christopher/go/pkg/mod/github.com/wailsapp/wails@v1.8.0/lib/renderer/webview/webview.go:279 +0x34 github.com/wailsapp/wails/lib/renderer/webview.(webview).Run(0xc000532028) /home/christopher/go/pkg/mod/github.com/wailsapp/wails@v1.8.0/lib/renderer/webview/webview.go:283 +0x30 github.com/wailsapp/wails/lib/renderer.(WebView).Run(0xc00023a070, 0xaea4a0, 0xc00023a070) /home/christopher/go/pkg/mod/github.com/wailsapp/wails@v1.8.0/lib/renderer/webview.go:235 +0x1ce github.com/wailsapp/wails.(App).start(0xc000530000, 0x0, 0x0) /home/christopher/go/pkg/mod/github.com/wailsapp/wails@v1.8.0/app.go:151 +0x59f github.com/wailsapp/wails.(App).Run(0xc000530000, 0x86bd80, 0xa743b0) /home/christopher/go/pkg/mod/github.com/wailsapp/wails@v1.8.0/app.go:94 +0xb0 main.wailsBinding() /home/christopher/go/src/gitlab.com/chrisfair/latin-catholic-prayers/frontend_interface.go:121 +0x1f9 main.main() /home/christopher/go/src/gitlab.com/chrisfair/latin-catholic-prayers/main.go:14 +0x138

goroutine 38 [chan send, 1 minutes]: github.com/BurntSushi/xgb.(*Conn).generateXIds(0xc0000c4370) /home/christopher/go/pkg/mod/github.com/!burnt!sushi/xgb@v0.0.0-20200324125942-20f126ea2843/xgb.go:253 +0x9e created by github.com/BurntSushi/xgb.postNewConn /home/christopher/go/pkg/mod/github.com/!burnt!sushi/xgb@v0.0.0-20200324125942-20f126ea2843/xgb.go:130 +0x1a5

goroutine 39 [chan send, 1 minutes]: github.com/BurntSushi/xgb.(*Conn).generateSeqIds(0xc0000c4370) /home/christopher/go/pkg/mod/github.com/!burnt!sushi/xgb@v0.0.0-20200324125942-20f126ea2843/xgb.go:278 +0x73 created by github.com/BurntSushi/xgb.postNewConn /home/christopher/go/pkg/mod/github.com/!burnt!sushi/xgb@v0.0.0-20200324125942-20f126ea2843/xgb.go:131 +0x1c7

goroutine 22 [chan receive, 1 minutes]: github.com/syossan27/tebata.(*Tebata).listen(0xc0000a1050) /home/christopher/go/pkg/mod/github.com/syossan27/tebata@v0.0.0-20180602121909-b283fe4bc5ba/tebata.go:37 +0x31 created by github.com/syossan27/tebata.New /home/christopher/go/pkg/mod/github.com/syossan27/tebata@v0.0.0-20180602121909-b283fe4bc5ba/tebata.go:30 +0xdc

goroutine 66 [chan send, 1 minutes]: github.com/BurntSushi/xgb.(*Conn).generateXIds(0xc0000c42c0) /home/christopher/go/pkg/mod/github.com/!burnt!sushi/xgb@v0.0.0-20200324125942-20f126ea2843/xgb.go:253 +0x9e created by github.com/BurntSushi/xgb.postNewConn /home/christopher/go/pkg/mod/github.com/!burnt!sushi/xgb@v0.0.0-20200324125942-20f126ea2843/xgb.go:130 +0x1a5

goroutine 67 [chan send, 1 minutes]: github.com/BurntSushi/xgb.(*Conn).generateSeqIds(0xc0000c42c0) /home/christopher/go/pkg/mod/github.com/!burnt!sushi/xgb@v0.0.0-20200324125942-20f126ea2843/xgb.go:278 +0x73 created by github.com/BurntSushi/xgb.postNewConn /home/christopher/go/pkg/mod/github.com/!burnt!sushi/xgb@v0.0.0-20200324125942-20f126ea2843/xgb.go:131 +0x1c7

goroutine 14 [chan send, 1 minutes]: github.com/BurntSushi/xgb.(*Conn).generateSeqIds(0xc0000c4420) /home/christopher/go/pkg/mod/github.com/!burnt!sushi/xgb@v0.0.0-20200324125942-20f126ea2843/xgb.go:278 +0x73 created by github.com/BurntSushi/xgb.postNewConn /home/christopher/go/pkg/mod/github.com/!burnt!sushi/xgb@v0.0.0-20200324125942-20f126ea2843/xgb.go:131 +0x1c7

goroutine 13 [chan send, 1 minutes]: github.com/BurntSushi/xgb.(*Conn).generateXIds(0xc0000c4420) /home/christopher/go/pkg/mod/github.com/!burnt!sushi/xgb@v0.0.0-20200324125942-20f126ea2843/xgb.go:253 +0x9e created by github.com/BurntSushi/xgb.postNewConn /home/christopher/go/pkg/mod/github.com/!burnt!sushi/xgb@v0.0.0-20200324125942-20f126ea2843/xgb.go:130 +0x1a5

goroutine 42 [syscall, 1 minutes]: os/signal.signal_recv(0xa748c0) /usr/lib/go/src/runtime/sigqueue.go:147 +0x9d os/signal.loop() /usr/lib/go/src/os/signal/signal_unix.go:23 +0x25 created by os/signal.Notify.func1.1 /usr/lib/go/src/os/signal/signal.go:150 +0x45

goroutine 23 [select, 1 minutes]: github.com/wailsapp/wails/lib/event.(Manager).Start.func1(0xc000094140) /home/christopher/go/pkg/mod/github.com/wailsapp/wails@v1.8.0/lib/event/manager.go:103 +0x19c created by github.com/wailsapp/wails/lib/event.(Manager).Start /home/christopher/go/pkg/mod/github.com/wailsapp/wails@v1.8.0/lib/event/manager.go:99 +0x131

goroutine 24 [select]: github.com/wailsapp/wails/lib/ipc.(Manager).Start.func1(0xc000524300, 0xae5880, 0xc0000b8960) /home/christopher/go/pkg/mod/github.com/wailsapp/wails@v1.8.0/lib/ipc/manager.go:56 +0xdd created by github.com/wailsapp/wails/lib/ipc.(Manager).Start /home/christopher/go/pkg/mod/github.com/wailsapp/wails@v1.8.0/lib/ipc/manager.go:54 +0x16d

goroutine 294 [chan receive]: github.com/hajimehoshi/oto.(*Context).Close(0xc0008d2220, 0x0, 0x0) /home/christopher/go/pkg/mod/github.com/hajimehoshi/oto@v0.3.1/context.go:116 +0xe5 github.com/faiface/beep/speaker.Close() /home/christopher/go/pkg/mod/github.com/faiface/beep@v1.0.2/speaker/speaker.go:73 +0x55 github.com/faiface/beep/speaker.Init(0xac44, 0x113a, 0x0, 0x0) /home/christopher/go/pkg/mod/github.com/faiface/beep@v1.0.2/speaker/speaker.go:31 +0x75 main.playAudioFile(0xc00022c7e0, 0x69, 0x0, 0x0) /home/christopher/go/src/gitlab.com/chrisfair/latin-catholic-prayers/prayers.go:343 +0x145 main.PlayWordFromPrayer(0xc0006ce7e8, 0x3, 0xc000506050, 0x9, 0x8000, 0x8000) /home/christopher/go/src/gitlab.com/chrisfair/latin-catholic-prayers/prayers.go:310 +0xa6 main.playWord(0xc000506050, 0x9, 0xc0006ce7e8, 0x3) /home/christopher/go/src/gitlab.com/chrisfair/latin-catholic-prayers/frontend_interface.go:60 +0x4d created by main.checkAnswer /home/christopher/go/src/gitlab.com/chrisfair/latin-catholic-prayers/frontend_interface.go:27 +0x139

I am essentially just following your example for playing the ogg file....if you want to take a look at my source code I will give you viewing access to my private gitlab repo.

faiface commented 3 years ago

Hi! My first instinct here is that (since you're running a lot of goroutines) you are using some parts of Beep concurrently without proper synchronization, causing race conditions, sometimes leading to a segfault. Would that be correct? Not all parts of Beep are safe to use concurrently without synchronization, in fact, most aren't.

Also, are you locking the speaker when mutating your streamers? (In case you do mutate them.)

cfair-beckman commented 3 years ago

Yes that is correct. What I am trying to do is to use beep to play ogg files and those ogg files are being played when a user presses a button in a Vue app (I am using wails to connect the stuff up) Wails runs everything as a separate goroutine I believe when it connects. I used the example in your code to wrapper a function to play a sound.

func playAudioFile(name string) (err error) {
    f, err := os.Open(name)
    if err != nil {
        return err
    }
    streamer, format, err := vorbis.Decode(f)
    if err != nil {
        return err
    }
    defer streamer.Close()

    speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
    done := make(chan bool)
    speaker.Play(beep.Seq(streamer, beep.Callback(func() {
        done <- true
    })))
    <-done
    return
}

What I really want is if the user presses a button playing a sound for it to either play the sound immediately upon pressing the button or queing on a list to do it when the speaker is ready again.

cfair-beckman commented 3 years ago

I can get around it without too much difficulty I just put a guard variable to indicate whether or not the audio file is currently playing....if it is refuse to execute any of the code I the function. That seems to work for me but the long and short is if the stream is changed while the speaker is already playing possible boomers....got it...thanks :)

cfair-beckman commented 3 years ago

BTW I looked at some more of your example code and I implemented a queue and only initialize the speaker and and play on startup. Under that condition it works great. I have completely gotten around the issue with the segfaults. It just caught me offgaurd initially. Great library thanks a bunch :) I still think the segfaults are a potential issue but they are not too hard to prevent if I simply initialize once and play once (using the queue for pushing additional sounds instead of feeding it straight to the player).

pmoscode commented 2 years ago

BTW I looked at some more of your example code and I implemented a queue and only initialize the speaker and and play on startup. Under that condition it works great. I have completely gotten around the issue with the segfaults. It just caught me offgaurd initially. Great library thanks a bunch :) I still think the segfaults are a potential issue but they are not too hard to prevent if I simply initialize once and play once (using the queue for pushing additional sounds instead of feeding it straight to the player).

Hi, I have exactly the same issue. You say, that you implemented a queue as part of the solution. Can you give me a hint on that, or share this particular code?

Many thanks.