config
This commit is contained in:
parent
358b7c8754
commit
019c946c55
6 changed files with 66 additions and 344 deletions
|
@ -1,19 +1,15 @@
|
|||
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"
|
||||
"github.com/hajimehoshi/oto"
|
||||
"github.com/tosone/minimp3"
|
||||
)
|
||||
|
||||
type Player struct {
|
||||
rate beep.SampleRate
|
||||
ctx *oto.Context
|
||||
}
|
||||
|
||||
func NewPlayer() *Player {
|
||||
|
@ -22,38 +18,53 @@ func NewPlayer() *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) initOto(samp, channels int) error {
|
||||
if p.ctx != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
if p.ctx, err = oto.NewContext(samp, channels, 2, 1024); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Player) playMP3(audio []byte) error {
|
||||
var dec *minimp3.Decoder
|
||||
var data []byte
|
||||
var err error
|
||||
if dec, data, err = minimp3.DecodeFull(audio); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = p.initOto(dec.SampleRate, dec.Channels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var player = p.ctx.NewPlayer()
|
||||
player.Write(data)
|
||||
|
||||
<-time.After(time.Second)
|
||||
|
||||
dec.Close()
|
||||
if err = player.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
return p.playMP3(audio)
|
||||
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
|
||||
}
|
||||
|
|
|
@ -105,7 +105,6 @@ func main() {
|
|||
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:
|
||||
log.Println(v.Notification.Msg)
|
||||
|
|
10
go.mod
10
go.mod
|
@ -12,9 +12,12 @@ require (
|
|||
github.com/google/uuid v1.4.0
|
||||
github.com/gopxl/beep v1.4.1
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/hajimehoshi/oto v1.0.1
|
||||
github.com/jackc/pgx/v5 v5.6.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rs/zerolog v1.33.0
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/tosone/minimp3 v1.0.2
|
||||
golang.org/x/crypto v0.21.0
|
||||
golang.org/x/term v0.18.0
|
||||
google.golang.org/protobuf v1.33.0
|
||||
|
@ -25,10 +28,7 @@ require (
|
|||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.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/hajimehoshi/go-mp3 v0.3.4 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
|
@ -45,11 +45,13 @@ require (
|
|||
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // 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/segmentio/asm v1.2.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
golang.org/x/exp/shiny v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||
golang.org/x/image v0.14.0 // indirect
|
||||
golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a // indirect
|
||||
golang.org/x/sync v0.5.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
|
|
34
go.sum
34
go.sum
|
@ -24,10 +24,6 @@ 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-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
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/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw=
|
||||
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
|
||||
|
@ -53,9 +49,8 @@ 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/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/hajimehoshi/oto v1.0.1 h1:8AMnq0Yr2YmzaiqTg/k1Yzd6IygUGk2we9nmjgbgPn4=
|
||||
github.com/hajimehoshi/oto v1.0.1/go.mod h1:wovJ8WWMfFKvP587mhHgot/MBr4DnNy9m6EepeVGnos=
|
||||
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/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
|
@ -104,8 +99,6 @@ 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/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||
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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -129,17 +122,29 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tosone/minimp3 v1.0.2 h1:htFE2EbP7Y4CJ8KGW9c55tKWRpzv9kXkEmCYGxFzVjA=
|
||||
github.com/tosone/minimp3 v1.0.2/go.mod h1:N6vjknGR7PboMTyJVhe/7RHNQiIc0jWZxFKlSWdfzwc=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
|
||||
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp/shiny v0.0.0-20240719175910-8a7402abbf56 h1:8jM66xzUJjNInw31Y8bic4AYSLVChztDRT93+kmofUY=
|
||||
golang.org/x/exp/shiny v0.0.0-20240719175910-8a7402abbf56/go.mod h1:3F+MieQB7dRYLTmnncoFbb1crS5lfQoTfDgQy6K4N0o=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
|
||||
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a h1:sYbmY3FwUWCBTodZL1S3JUuOvaW6kM2o+clDzzDNBWg=
|
||||
golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a/go.mod h1:Ede7gF0KGoHlj822RtphAHK1jLdrcuRBZg0sF1Q+SPc=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
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/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -147,10 +152,11 @@ golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
|||
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/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
|
||||
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
|
||||
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
|
||||
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
// 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()
|
||||
}
|
|
@ -1,189 +0,0 @@
|
|||
// 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