broken
This commit is contained in:
parent
962ce282f7
commit
358b7c8754
6 changed files with 381 additions and 3 deletions
59
cmd/calls/audio.go
Normal file
59
cmd/calls/audio.go
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gopxl/beep"
|
||||||
|
"github.com/gopxl/beep/mp3"
|
||||||
|
"github.com/gopxl/beep/speaker"
|
||||||
|
"github.com/gopxl/beep/wav"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Player struct {
|
||||||
|
rate beep.SampleRate
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlayer() *Player {
|
||||||
|
p := &Player{}
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) initSpeaker(rate beep.SampleRate) {
|
||||||
|
if p.rate != rate {
|
||||||
|
speaker.Init(rate, rate.N(time.Second/10))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) Play(audio []byte, mimeType string) error {
|
||||||
|
var streamer beep.StreamCloser
|
||||||
|
var err error
|
||||||
|
var format beep.Format
|
||||||
|
r := io.NopCloser(bytes.NewBuffer(audio))
|
||||||
|
switch mimeType {
|
||||||
|
case "audio/mpeg":
|
||||||
|
streamer, format, err = mp3.Decode(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer streamer.Close()
|
||||||
|
p.initSpeaker(format.SampleRate)
|
||||||
|
|
||||||
|
case "audio/wav":
|
||||||
|
streamer, format, err = wav.Decode(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer streamer.Close()
|
||||||
|
p.initSpeaker(format.SampleRate)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown format %s", mimeType)
|
||||||
|
}
|
||||||
|
|
||||||
|
speaker.Play(streamer)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -30,6 +30,7 @@ func main() {
|
||||||
|
|
||||||
interrupt := make(chan os.Signal, 1)
|
interrupt := make(chan os.Signal, 1)
|
||||||
signal.Notify(interrupt, os.Interrupt)
|
signal.Notify(interrupt, os.Interrupt)
|
||||||
|
play := NewPlayer()
|
||||||
|
|
||||||
loginForm := url.Values{}
|
loginForm := url.Values{}
|
||||||
loginForm.Add("username", *username)
|
loginForm.Add("username", *username)
|
||||||
|
@ -101,6 +102,11 @@ func main() {
|
||||||
switch v := m.ToClientMessage.(type) {
|
switch v := m.ToClientMessage.(type) {
|
||||||
case *pb.Message_Call:
|
case *pb.Message_Call:
|
||||||
log.Printf("call tg %d", v.Call.Talkgroup)
|
log.Printf("call tg %d", v.Call.Talkgroup)
|
||||||
|
err := play.Play(v.Call.Audio, v.Call.AudioType)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
os.WriteFile("failed.mp3", v.Call.Audio, 0644)
|
||||||
|
}
|
||||||
case *pb.Message_Notification:
|
case *pb.Message_Notification:
|
||||||
log.Println(v.Notification.Msg)
|
log.Println(v.Notification.Msg)
|
||||||
default:
|
default:
|
||||||
|
|
7
go.mod
7
go.mod
|
@ -10,6 +10,7 @@ require (
|
||||||
github.com/go-chi/render v1.0.3
|
github.com/go-chi/render v1.0.3
|
||||||
github.com/golang-migrate/migrate/v4 v4.17.1
|
github.com/golang-migrate/migrate/v4 v4.17.1
|
||||||
github.com/google/uuid v1.4.0
|
github.com/google/uuid v1.4.0
|
||||||
|
github.com/gopxl/beep v1.4.1
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
github.com/jackc/pgx/v5 v5.6.0
|
github.com/jackc/pgx/v5 v5.6.0
|
||||||
github.com/rs/zerolog v1.33.0
|
github.com/rs/zerolog v1.33.0
|
||||||
|
@ -24,7 +25,10 @@ require (
|
||||||
github.com/ajg/form v1.5.1 // indirect
|
github.com/ajg/form v1.5.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||||
|
github.com/ebitengine/oto/v3 v3.1.0 // indirect
|
||||||
|
github.com/ebitengine/purego v0.7.1 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/hajimehoshi/go-mp3 v0.3.4 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
@ -41,11 +45,12 @@ require (
|
||||||
github.com/lestrrat-go/option v1.0.1 // indirect
|
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||||
github.com/segmentio/asm v1.2.0 // indirect
|
github.com/segmentio/asm v1.2.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
go.uber.org/atomic v1.7.0 // indirect
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
golang.org/x/sync v0.5.0 // indirect
|
golang.org/x/sync v0.5.0 // indirect
|
||||||
golang.org/x/sys v0.18.0 // indirect
|
golang.org/x/sys v0.20.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
16
go.sum
16
go.sum
|
@ -24,6 +24,10 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh
|
||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
github.com/ebitengine/oto/v3 v3.1.0 h1:9tChG6rizyeR2w3vsygTTTVVJ9QMMyu00m2yBOCch6U=
|
||||||
|
github.com/ebitengine/oto/v3 v3.1.0/go.mod h1:IK1QTnlfZK2GIB6ziyECm433hAdTaPpOsGMLhEyEGTg=
|
||||||
|
github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA=
|
||||||
|
github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
||||||
github.com/go-chi/chi v1.5.5 h1:vOB/HbEMt9QqBqErz07QehcOKHaWFtuj87tTDVz2qXE=
|
github.com/go-chi/chi v1.5.5 h1:vOB/HbEMt9QqBqErz07QehcOKHaWFtuj87tTDVz2qXE=
|
||||||
github.com/go-chi/chi v1.5.5/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw=
|
github.com/go-chi/chi v1.5.5/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw=
|
||||||
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
|
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
|
||||||
|
@ -45,8 +49,13 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gopxl/beep v1.4.1 h1:WqNs9RsDAhG9M3khMyc1FaVY50dTdxG/6S6a3qsUHqE=
|
||||||
|
github.com/gopxl/beep v1.4.1/go.mod h1:A1dmiUkuY8kxsvcNJNUBIEcchmiP6eUyCHSxpXl0YO0=
|
||||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68=
|
||||||
|
github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo=
|
||||||
|
github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
@ -95,6 +104,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||||
|
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e h1:s2RNOM/IGdY0Y6qfTeUKhDawdHDpK9RGBdx80qN4Ttw=
|
||||||
|
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e/go.mod h1:nBdnFKj15wFbf94Rwfq4m30eAcyY9V/IyKAGQFtqkW0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
@ -128,11 +139,12 @@ golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
|
|
107
internal/mpg123/decode.go
Normal file
107
internal/mpg123/decode.go
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
// Package mp3 implements audio data decoding in MP3 format.
|
||||||
|
package mpg123
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/gopxl/beep"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
gomp3NumChannels = 2
|
||||||
|
gomp3Precision = 2
|
||||||
|
gomp3BytesPerFrame = gomp3NumChannels * gomp3Precision
|
||||||
|
)
|
||||||
|
|
||||||
|
// Decode takes a ReadCloser containing audio data in MP3 format and returns a StreamSeekCloser,
|
||||||
|
// which streams that audio. The Seek method will panic if rc is not io.Seeker.
|
||||||
|
//
|
||||||
|
// Do not close the supplied ReadSeekCloser, instead, use the Close method of the returned
|
||||||
|
// StreamSeekCloser when you want to release the resources.
|
||||||
|
func Decode(rc io.ReadCloser) (s beep.StreamCloser, format beep.Format, err error) {
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Wrap(err, "mp3")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
d, err := NewDecoder("")
|
||||||
|
if err != nil {
|
||||||
|
return nil, beep.Format{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Format(8000, 2, ENC_FLOAT_64)
|
||||||
|
err = d.OpenFeed()
|
||||||
|
if err != nil {
|
||||||
|
return nil, beep.Format{}, err
|
||||||
|
}
|
||||||
|
// get 4k of file
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
for {
|
||||||
|
n, err := rc.Read(buf)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return nil, beep.Format{}, err
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
err = d.Feed(buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, beep.Format{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rate, channels, enc := d.GetFormat()
|
||||||
|
fmt.Printf("rate %d chan %d enc %d\n", rate, channels, enc)
|
||||||
|
|
||||||
|
format = beep.Format{
|
||||||
|
SampleRate: beep.SampleRate(8000),
|
||||||
|
NumChannels: 2,
|
||||||
|
Precision: 4,
|
||||||
|
}
|
||||||
|
return &decoder{rc, d, format, 0, nil}, format, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type decoder struct {
|
||||||
|
closer io.Closer
|
||||||
|
d *Decoder
|
||||||
|
f beep.Format
|
||||||
|
pos int
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) Stream(samples [][2]float64) (n int, ok bool) {
|
||||||
|
if d.err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
var tmp [gomp3BytesPerFrame]byte
|
||||||
|
for i := range samples {
|
||||||
|
dn, err := d.d.Read(tmp[:])
|
||||||
|
if dn == len(tmp) {
|
||||||
|
samples[i], _ = d.f.DecodeSigned(tmp[:])
|
||||||
|
d.pos += dn
|
||||||
|
n++
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
d.err = errors.Wrap(err, "mp3")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) Err() error {
|
||||||
|
return d.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) Close() error {
|
||||||
|
err := d.closer.Close()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "mp3")
|
||||||
|
}
|
||||||
|
return d.d.Close()
|
||||||
|
}
|
189
internal/mpg123/mpg123.go
Normal file
189
internal/mpg123/mpg123.go
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
// mpg123.go contains all bindings to the C library
|
||||||
|
|
||||||
|
package mpg123
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <mpg123.h>
|
||||||
|
#cgo LDFLAGS: -lmpg123
|
||||||
|
|
||||||
|
int do_mpg123_read(mpg123_handle *mh, void *outmemory, size_t outmemsize, size_t *done) {
|
||||||
|
return mpg123_read(mh, outmemory, outmemsize, done);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var EOF = errors.New("EOF")
|
||||||
|
|
||||||
|
// All output encoding formats supported by mpg123
|
||||||
|
const (
|
||||||
|
ENC_8 = C.MPG123_ENC_8
|
||||||
|
ENC_16 = C.MPG123_ENC_16
|
||||||
|
ENC_24 = C.MPG123_ENC_24
|
||||||
|
ENC_32 = C.MPG123_ENC_32
|
||||||
|
ENC_SIGNED = C.MPG123_ENC_SIGNED
|
||||||
|
ENC_FLOAT = C.MPG123_ENC_FLOAT
|
||||||
|
ENC_SIGNED_8 = C.MPG123_ENC_SIGNED_8
|
||||||
|
ENC_UNSIGNED_8 = C.MPG123_ENC_UNSIGNED_8
|
||||||
|
ENC_ULAW_8 = C.MPG123_ENC_ULAW_8
|
||||||
|
ENC_ALAW_8 = C.MPG123_ENC_ALAW_8
|
||||||
|
ENC_SIGNED_16 = C.MPG123_ENC_SIGNED_16
|
||||||
|
ENC_UNSIGNED_16 = C.MPG123_ENC_UNSIGNED_16
|
||||||
|
ENC_SIGNED_24 = C.MPG123_ENC_SIGNED_24
|
||||||
|
ENC_UNSIGNED_24 = C.MPG123_ENC_UNSIGNED_24
|
||||||
|
ENC_SIGNED_32 = C.MPG123_ENC_SIGNED_32
|
||||||
|
ENC_UNSIGNED_32 = C.MPG123_ENC_UNSIGNED_32
|
||||||
|
ENC_FLOAT_32 = C.MPG123_ENC_FLOAT_32
|
||||||
|
ENC_FLOAT_64 = C.MPG123_ENC_FLOAT_64
|
||||||
|
ENC_ANY = C.MPG123_ENC_ANY
|
||||||
|
)
|
||||||
|
|
||||||
|
// Contains a handle for and mpg123 decoder instance
|
||||||
|
type Decoder struct {
|
||||||
|
handle *C.mpg123_handle
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes the mpg123 library when package is loaded
|
||||||
|
func init() {
|
||||||
|
err := C.mpg123_init()
|
||||||
|
if err != C.MPG123_OK {
|
||||||
|
//return fmt.Errorf("error initializing mpg123")
|
||||||
|
panic("failed to initialize mpg123")
|
||||||
|
}
|
||||||
|
//return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////
|
||||||
|
// DECODER INSTANCE CODE //
|
||||||
|
///////////////////////////
|
||||||
|
|
||||||
|
// NewDecoder creates a new mpg123 decoder instance
|
||||||
|
func NewDecoder(decoder string) (*Decoder, error) {
|
||||||
|
var err C.int
|
||||||
|
var mh *C.mpg123_handle
|
||||||
|
if decoder != "" {
|
||||||
|
mh = C.mpg123_new(nil, &err)
|
||||||
|
} else {
|
||||||
|
cdecoder := C.CString(decoder)
|
||||||
|
defer C.free(unsafe.Pointer(cdecoder))
|
||||||
|
mh = C.mpg123_new(cdecoder, &err)
|
||||||
|
}
|
||||||
|
if mh == nil {
|
||||||
|
errstring := C.mpg123_plain_strerror(err)
|
||||||
|
err := C.GoString(errstring)
|
||||||
|
C.free(unsafe.Pointer(errstring))
|
||||||
|
return nil, fmt.Errorf("error initializing mpg123 decoder: %s", err)
|
||||||
|
}
|
||||||
|
dec := new(Decoder)
|
||||||
|
dec.handle = mh
|
||||||
|
return dec, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete frees an mpg123 decoder instance
|
||||||
|
func (d *Decoder) Delete() {
|
||||||
|
C.mpg123_delete(d.handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a string containing the most recent error message corresponding to
|
||||||
|
// an mpg123 decoder instance
|
||||||
|
func (d *Decoder) strerror() string {
|
||||||
|
return C.GoString(C.mpg123_strerror(d.handle))
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////
|
||||||
|
// OUTPUT FORMAT CODE //
|
||||||
|
////////////////////////
|
||||||
|
|
||||||
|
// FormatNone disables all decoder output formats (used to specifying supported formats)
|
||||||
|
func (d *Decoder) FormatNone() {
|
||||||
|
C.mpg123_format_none(d.handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromatAll enables all decoder output formats (this is the default setting)
|
||||||
|
func (d *Decoder) FormatAll() {
|
||||||
|
C.mpg123_format_all(d.handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFormat returns current output format
|
||||||
|
func (d *Decoder) GetFormat() (rate int64, channels int, encoding int) {
|
||||||
|
var cRate C.long
|
||||||
|
var cChans, cEnc C.int
|
||||||
|
C.mpg123_getformat(d.handle, &cRate, &cChans, &cEnc)
|
||||||
|
return int64(cRate), int(cChans), int(cEnc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format sets the audio output format for decoder
|
||||||
|
func (d *Decoder) Format(rate int64, channels int, encodings int) {
|
||||||
|
C.mpg123_format(d.handle, C.long(rate), C.int(channels), C.int(encodings))
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// INPUT AND DECODING CODE //
|
||||||
|
/////////////////////////////
|
||||||
|
|
||||||
|
// Open initializes a decoder for an mp3 file using a filename
|
||||||
|
func (d *Decoder) Open(file string) error {
|
||||||
|
cfile := C.CString(file)
|
||||||
|
defer C.free(unsafe.Pointer(cfile))
|
||||||
|
err := C.mpg123_open(d.handle, cfile)
|
||||||
|
if err != C.MPG123_OK {
|
||||||
|
return fmt.Errorf("error opening %s: %s", file, d.strerror())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenFile binds to an fd from an open *os.File for decoding
|
||||||
|
func (d *Decoder) OpenFile(f *os.File) error {
|
||||||
|
err := C.mpg123_open_fd(d.handle, C.int(f.Fd()))
|
||||||
|
if err != C.MPG123_OK {
|
||||||
|
return fmt.Errorf("error attaching file: %s", d.strerror())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenFeed prepares a decoder for direct feeding via Feed(..)
|
||||||
|
func (d *Decoder) OpenFeed() error {
|
||||||
|
err := C.mpg123_open_feed(d.handle)
|
||||||
|
if err != C.MPG123_OK {
|
||||||
|
return fmt.Errorf("mpg123 error: %s", d.strerror())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes an input file if one was opened by mpg123
|
||||||
|
func (d *Decoder) Close() error {
|
||||||
|
err := C.mpg123_close(d.handle)
|
||||||
|
if err != C.MPG123_OK {
|
||||||
|
return fmt.Errorf("mpg123 error: %s", d.strerror())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read decodes data and into buf and returns number of bytes decoded.
|
||||||
|
func (d *Decoder) Read(buf []byte) (int, error) {
|
||||||
|
var done C.size_t
|
||||||
|
err := C.do_mpg123_read(d.handle, (unsafe.Pointer)(&buf[0]), C.size_t(len(buf)), &done)
|
||||||
|
if err == C.MPG123_DONE {
|
||||||
|
return int(done), EOF
|
||||||
|
}
|
||||||
|
if err != C.MPG123_OK {
|
||||||
|
return int(done), fmt.Errorf("mpg123 error: %s", d.strerror())
|
||||||
|
}
|
||||||
|
return int(done), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feed provides data bytes into the decoder
|
||||||
|
func (d *Decoder) Feed(buf []byte) error {
|
||||||
|
err := C.mpg123_feed(d.handle, (*C.uchar)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)))
|
||||||
|
if err != C.MPG123_OK {
|
||||||
|
return fmt.Errorf("mpg123 error: %s", d.strerror())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in a new issue