Giter Club home page Giter Club logo

beep's Introduction

Beep

Go Reference Go build status Coverage Status Go Report Card Discord Chat

A little package that brings sound to any Go application. Suitable for playback and audio-processing.

go get -u github.com/gopxl/beep/v2

Features

Beep is built on top of its Streamer interface, which is like io.Reader, but for audio. It was one of the best design decisions I've ever made and it enabled all the rest of the features to naturally come together with not much code.

  • Decode and play WAV, MP3, OGG, FLAC and MIDI.
  • Encode and save WAV.
  • Very simple API. Limiting the support to stereo (two channel) audio made it possible to simplify the architecture and the API.
  • Rich library of compositors and effects. Loop, pause/resume, change volume, mix, sequence, change playback speed, and more.
  • Easily create new effects. With the Streamer interface, creating new effects is very easy.
  • Generate completely own artificial sounds. Again, the Streamer interface enables easy sound generation.
  • Very small codebase. The core is just ~1K LOC.

Tutorial

The Wiki contains a handful of tutorials for you to get started. They teach the fundamentals and advanced topics alike. Read them especially if you call speaker.Init every time you play something.

Examples

Speedy Player Doppler Stereo Room
Speedy Player Doppler Stereo Room

Dependencies

For playback, Beep uses Oto under the hood. Check its requirements to see what you need to install for building your application.

Running an already built application should work with no extra dependencies.

License

MIT

Related projects

Projects using Beep

beep's People

Contributors

markkremer avatar faiface avatar dusk125 avatar duysqubix avatar mewmew avatar dependabot[bot] avatar nhalstead avatar cswank avatar avivklas avatar ilyapashuk avatar eiton avatar alextopher avatar rsp9u avatar lordwelch avatar jypelle avatar tslocum avatar totallygamerjet avatar hundemeier avatar deryrahman avatar cebarks avatar asday avatar christopher-dg avatar dxbednarczyk avatar jwangsadinata avatar jboverfelt avatar skitaoka avatar nkalait avatar xorbit01 avatar eula01 avatar hyperturtle avatar

Stargazers

Freja avatar UnboxTheCat avatar Paz avatar James Moreau avatar  avatar heesu avatar  avatar Davi Rodrigues avatar AlpineYahoo avatar Ricardo avatar Martin Beierling-Mutz avatar Daniel Ahn avatar Aditya Bimawan avatar Iliyan Yankov avatar dragsbruh avatar haifeiWu avatar Sin-Woo Bang avatar Christoffer Nissen avatar ourenyang avatar stephen-shopopop avatar Ezequiel Moreno avatar Tom avatar  avatar Eric Scott avatar Edward Stock avatar Ev Dolzhenko avatar Piero Lescano avatar runghall avatar  avatar  avatar Lukas Werner avatar Ajit avatar  avatar Raiya Araki avatar Xavier Godart avatar Spmart avatar ringsaturn avatar Aurélien Rainone avatar PJ Kumlue avatar Lukas Schwab avatar Philipp Gillé avatar  avatar Yakut Özcan avatar JamieJiang avatar sano avatar The Little Waltz avatar Alexey Agafonov avatar Akmal avatar  avatar oldkingcone avatar Karol Marcjan avatar Jeeva Kandasamy avatar Hariharan avatar Eduardo Henrique Freire Machado avatar Winter Snow avatar Mark Sanders avatar NB avatar Chatrapati Ākula avatar Cubicroot avatar Günther Eberl avatar  avatar Radu Topala avatar Charles Ozochukwu avatar  avatar Joana avatar Horse avatar Cauê Felchar avatar Mauro Coppola avatar  avatar Song Li avatar Victor Milhomem avatar Karol avatar Anastasios Papalyras avatar  avatar akapril avatar H avatar Luo Mingqiang avatar bokutake avatar Filip V. avatar Jesuszilla avatar shallot avatar Ki avatar Eren Aslan avatar Wildlife avatar Afif Alauddin avatar Teddy Sommavilla avatar Thananchai kongthaworn avatar Pavlin Georgiev avatar Marcos Sader avatar  avatar Gabe Cook avatar Brance Boren avatar  avatar Jun Nishimura avatar Miguel Eduardo Gil Biraud avatar Craig Donnelly avatar Ayodeji O. avatar Raden Mohamad Rishwan avatar Jakub avatar  avatar

Watchers

 avatar  avatar  avatar  avatar Geoff Clapp avatar

beep's Issues

Callbacks in loops

What's the best way to run a callback each time a streamer is played within a loop? Callback isn't a StreamSeeker, so you can't just use it plainly

Original issue: faiface/beep#98

USB audio device unplugged and replug, make program block at speaker.Init

I have an USB audio device which has poor contact, when replugged I have to call speaker.Init() again to make the speaker work again.

But it's stuck at here:
https://github.com/faiface/beep/blob/98afada94bffc8665c7fce42814174a3e8650108/speaker/speaker.go#L129
Since it's in a for loop, the whole loop is blocked:
https://github.com/faiface/beep/blob/98afada94bffc8665c7fce42814174a3e8650108/speaker/speaker.go#L48-L57
the done channel cannot be consumed, when I call speaker.Init again, it calls speaker.Close first, then it's stuck at here:
https://github.com/faiface/beep/blob/98afada94bffc8665c7fce42814174a3e8650108/speaker/speaker.go#L69

As a result when my USB audio device replugged I cannot call speaker.Init() again, it's always stuck.
So right now I change the done channel into a buffered chan, it looks good so far, but I'm not sure whether it will cause other problem.

Is it necessary to make the done channel an unbuffered chan? How to handle the replug situation properly?

Original issue: faiface/beep#115

Deps need upgrading

I needed to upgrade oto for MAC, and then noticed all needed to be.

The tests all pass with a go.mod of:

module github.com/faiface/beep

go 1.14

require (
	github.com/gdamore/tcell v1.3.0
	github.com/gopherjs/gopherwasm v1.0.0 // indirect
	github.com/hajimehoshi/go-mp3 v0.3.0
	github.com/hajimehoshi/oto v0.6.1
	github.com/jfreymuth/oggvorbis v1.0.1
	github.com/jfreymuth/vorbis v1.0.0 // indirect
	github.com/mewkiz/flac v1.0.6
	github.com/pkg/errors v0.9.1
	gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 // indirect
)

How about upgrading ??

Original issue: faiface/beep#94

undefined: newDriver compile linux from mac

Hey, I try compile my application to linux from mac but I got error like this:

env GOOS=linux GOARCH=amd64 go build -v
github.com/hajimehoshi/oto
# github.com/hajimehoshi/oto
../github.com/hajimehoshi/oto/context.go:69:12: undefined: newDriver

this is my go env

GO111MODULE="auto"
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/ramacatur/Library/Caches/go-build"
GOENV="/Users/ramacatur/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Volumes/Data/code/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Volumes/Data/code/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GOVCS=""
GOVERSION="go1.16.4"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/4n/dt47c6rn3654505gxvvrmjc80000gn/T/go-build3898342957=/tmp/go-build -gno-record-gcc-switches -fno-common"

thanks for the solution

Original issue: faiface/beep#125

Unable to reinitialize speakers on Linux

This small sample program panics on Linux:

package main

import (
	"fmt"
	"time"

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

func main() {
	format := beep.Format{
		SampleRate:  44100,
		NumChannels: 2,
		Precision:   2,
	}
	if err := speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10)); err != nil {
		panic(fmt.Errorf("initializing speakers: %w", err))
	}
	speaker.Close()
	if err := speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10)); err != nil {
		panic(fmt.Errorf("initializing speakers: %w", err))
	}
	speaker.Close()
}

I believe there must be a race or some kind of failed cleanup responsible, but I'm not certain.

The exact crash is:

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

runtime stack:
runtime.throw({0x4ada0a?, 0x4?})
        /home/chris/.local/lib/go-1.18/src/runtime/panic.go:992 +0x71
runtime.sigpanic()
        /home/chris/.local/lib/go-1.18/src/runtime/signal_unix.go:802 +0x3a9

goroutine 6 [syscall]:
runtime.cgocall(0x48d430, 0xc000048e90)
        /home/chris/.local/lib/go-1.18/src/runtime/cgocall.go:157 +0x5c fp=0xc000048e68 sp=0xc000048e30 pc=0x4050dc
github.com/hajimehoshi/oto._Cfunc_snd_pcm_drop(0x1a1a0c0)
        _cgo_gotypes.go:134 +0x4c fp=0xc000048e90 sp=0xc000048e68 pc=0x48a6cc
github.com/hajimehoshi/oto.(*driver).Close.func1(0xc000090000?)
        /home/chris/Code/go/pkg/mod/github.com/hajimehoshi/[email protected]/driver_linux.go:177 +0x46 fp=0xc000048ec8 sp=0xc000048e90 pc=0x48b946
github.com/hajimehoshi/oto.(*driver).Close(0x5f5e100?)
        /home/chris/Code/go/pkg/mod/github.com/hajimehoshi/[email protected]/driver_linux.go:177 +0x1e fp=0xc000048ee8 sp=0xc000048ec8 pc=0x48b81e
github.com/hajimehoshi/oto.(*driverWriter).Close(0xc000078150)
        /home/chris/Code/go/pkg/mod/github.com/hajimehoshi/[email protected]/context.go:170 +0xc9 fp=0xc000048f38 sp=0xc000048ee8 pc=0x489d49
github.com/hajimehoshi/oto.(*Context).Close(0xc00000e030)
        /home/chris/Code/go/pkg/mod/github.com/hajimehoshi/[email protected]/context.go:106 +0xa5 fp=0xc000048f80 sp=0xc000048f38 pc=0x4897c5
github.com/hajimehoshi/oto.NewContext.func1()
        /home/chris/Code/go/pkg/mod/github.com/hajimehoshi/[email protected]/context.go:89 +0x8f fp=0xc000048fe0 sp=0xc000048f80 pc=0x48968f
runtime.goexit()
        /home/chris/.local/lib/go-1.18/src/runtime/asm_amd64.s:1571 +0x1 fp=0xc000048fe8 sp=0xc000048fe0 pc=0x45fb21
created by github.com/hajimehoshi/oto.NewContext
        /home/chris/Code/go/pkg/mod/github.com/hajimehoshi/[email protected]/context.go:84 +0x2af

goroutine 1 [sleep]:
time.Sleep(0x5f5e100)
        /home/chris/.local/lib/go-1.18/src/runtime/time.go:194 +0x12e
github.com/hajimehoshi/oto.(*driverWriter).Close(0xc0001b4000)
        /home/chris/Code/go/pkg/mod/github.com/hajimehoshi/[email protected]/context.go:169 +0xb7
github.com/hajimehoshi/oto.(*Context).Close(0xc0001b6000)
        /home/chris/Code/go/pkg/mod/github.com/hajimehoshi/[email protected]/context.go:106 +0xa5
github.com/faiface/beep/speaker.Close()
        /home/chris/Code/go/pkg/mod/github.com/faiface/[email protected]/speaker/speaker.go:73 +0x85
main.main()
        /tmp/testsound/main.go:24 +0x54

goroutine 18 [semacquire]:
sync.runtime_SemacquireMutex(0x0?, 0xfe?, 0x4c6b10?)
        /home/chris/.local/lib/go-1.18/src/runtime/sema.go:71 +0x25
sync.(*Mutex).lockSlow(0xc0001b4020)
        /home/chris/.local/lib/go-1.18/src/sync/mutex.go:162 +0x165
sync.(*Mutex).Lock(...)
        /home/chris/.local/lib/go-1.18/src/sync/mutex.go:81
github.com/hajimehoshi/oto.(*driverWriter).Write(0xc0001b4000, {0xc00012e000?, 0x100?, 0x8000?})
        /home/chris/Code/go/pkg/mod/github.com/hajimehoshi/[email protected]/context.go:136 +0x97
io.copyBuffer({0x4c6b50, 0xc0001b4000}, {0x4c6b70, 0xc0001b0040}, {0x0, 0x0, 0x0})
        /home/chris/.local/lib/go-1.18/src/io/io.go:428 +0x204
io.Copy(...)
        /home/chris/.local/lib/go-1.18/src/io/io.go:385
github.com/hajimehoshi/oto.NewContext.func1()
        /home/chris/Code/go/pkg/mod/github.com/hajimehoshi/[email protected]/context.go:85 +0x49
created by github.com/hajimehoshi/oto.NewContext
        /home/chris/Code/go/pkg/mod/github.com/hajimehoshi/[email protected]/context.go:84 +0x2af
exit status 2

Am I doing something wrong in how I reinitialize this? My use-case is an application that only needs to play sound sometimes. Leaving the speaker running eats 5% of the system's CPU time to process silence, so I want to shut down the speakers when they're not needed. When I try to re-initialized the speakers (even after a delay), I get a crash like this.

System info (Arch Linux):

$ go version
go version go1.18 linux/amd64
$ uname -a
Linux vendetta 5.17.3-arch1-1 #1 SMP PREEMPT Thu, 14 Apr 2022 01:18:36 +0000 x86_64 GNU/Linux

Original issue: faiface/beep#146

WAV File with maximum filesize (FFFFFFFF) hangs Beep playback

Hi,

Supplying to the Beep library a WAV File with an unlimited Filesize (FFFFFFFF) at byte offset 0x4 and 0x134 hangs the playback. One needs to kill the whole process. While an unlimited filesize might be not a valid WAV-File (although Mac Finder plays the file fine) and should be therefore rejected it should not hang the library in any case. Drop me a line if you want to fix this and need a sample.

Cheers, Matthias

Original issue: faiface/beep#160

Sample format not supported

I'm getting this error when trying the example record.go:

$ ./record test.wav
Recording. Press Ctrl-C to stop.
2022/12/16 05:02:00 Sample format not supported

Original issue: faiface/beep#158

Implement Forward/Backward for Seq

Hi folks,
I have a list of streams wrapped by beep.Seq(). These are all played sequential one by one.
How do I get it managed to jump forward and backward in the streams.

Let's assume this:
Seq: Stream 1 -> Stream 2 -> Stream 3 -> Stream 4

When the player is started and is in stream 2 after a while, I want to jump to stream 3.
From there I want to jump back to stream 1.

How do I achieve that? I know, I could rebuild the sequence (beep.Seq()), but is there another way?
Or isn't it possible, because I missed something...

Thx

Original issue: faiface/beep#156

100% CPU usage on LINUX

On Windows BEEP is wonderful. On LINUX, it plays very well, but just linking V1.1.0 or earlier) into GO program on SBCs for RASPBIAN or ARMBIAN OS gives 100% CPU even without playing MP3.

Original issue: faiface/beep#131

Is there a smart way to play overlapping sounds?

I'm developing an application that plays overlapping .wav files, and I want to be able to mute/unmute or pause/start a specific one on a specific input. Should I load the files into the same beep.Streamer and load it into one beep.Speaker or into the same beep.Buffer and then into a beep.Speaker or approach it in a different way? What's the 'correct' way to do so?

Original issue: faiface/beep#161

Stuttering on 96 kHz / 24 bit FLAC

👋 Hello!

First off, thanks for this library! Got it mostly working, but struggling on a certain area.

Whenever I try playing a hi-res FLAC file (96 kHz / 24 bit) following the Beep Tutorial, I encounter stuttering and breaking.

Logging out format from flac.Decode on 2 different FLAC files:

  • File 1 (Precision: 2, Sample Rate: 44100) works without issues
  • File 2 (Precision: 3, Sample Rate: 96000) stutters. Tried fiddling with the resampling quality, without any luck.

Here's a sample file where I can reproduce the stuttering.

It also doesn't help that this is my first time delving into audio processing, so I'm not quite sure if I'm just missing some configuration on how I'm using the lib, or whether this problem exists within faiface/beep, or hajimehoshi/oto, or even just on OSX in general.

Any pointer to the right direction will be highly appreciated.

I am on: MacOS Big Sur 11.6 (let me know if you'd like more environment settings).

Original issue: faiface/beep#132

Support variable number of channels

Current state

I would say that the 2 most important types in Beep are the following:

// Streamer is able to stream a finite or infinite sequence of audio samples.
type Streamer interface {
	Stream(samples [][2]float64) (n int, ok bool)
	Err() error
}
// Format is the format of a Buffer or another audio source.
type Format struct {
	SampleRate SampleRate
	NumChannels int
	Precision int
}

Streamer allows us to define operations on samples. Using the composite pattern it is possible to combine operations to create more complex operations.

Format, besides storing the format information, is used to encode/decode samples into different representations.

These types are very powerful and can be used to do a lot of things with very little. However, there are some details about them that make me wonder if something better is possible:

  • Even though the Format specifies the number of channels, within the interface of the Streamer, the number of channels is hardcoded to 2. I suspect this is done to keep the library simple and this is an important consideration. Please try to keep this in mind when reading the rest of this proposal.
  • Withing Beep, Precision and SampleRate are mostly used at endpoints: when encoding/decoding a file and when using an in-memory buffer (which is similar to a WAV file). In addition, SampleRate can be used when resampling samples.

The number of channels seems like it's an inherent property of the samples while the Format is only used at specific parts of the application. It is metadata that is exposed when decoding a file format, or it can passed as configuration to encode audio. Format is however, never directly used by Streamers and is completely separate from the composite pattern that is core to Beep.

Proposal

Move NumChannels to Samples.

The samples are stored in an interleaved format in a 1D slice. We lose the syntactic sugar of 2D slices which I solved by using methods (BOOO!). I think the benefits could very well outweigh the drawbacks but I would like to invite you to think about the developer experience for the users of Beep when, say, they want to implement a custom Streamer.

For reference, this is what the types will look like (approximately):

// Samples contains a finite sequence of audio samples for one or more channels.
type Samples struct {
	Samples []float64 // interleaved
	NumChannels int
}

// Get a single sample.
func (s Samples) Get(index, channel int) float64 {
	return s.Samples[index*s.NumChannels + channel]
}

// Set the value of a sample.
func (s *Samples) Set(index, channel int, value float64) {
	s.Samples[index*s.NumChannels + channel] = value
}

// Streamer is able to stream a finite or infinite sequence of audio samples.
type Streamer interface {
	Stream(samples Samples) (n int, ok bool)
	Err() error
}
// Format describes the stored format of an audio stream, as a file or in-memory.
type Format struct {
	SampleRate SampleRate
	Precision int
}

In this scenario, Format can be used to format individual samples still. However, it doesn't deal with framing samples of channels together.

What do we gain?

One obvious benefit is that the number of channels isn't constant anymore:

  • It is possible to read Vorbis 5.1 surround sound files without Beep choosing for you which channels to keep. I don't know what they're doing with 5.1 audio files in Beep, but that sounds fun.
  • One of the use cases that has been on my mind lately is Beep within games. In games, a lot of source audio only requires a single channel. For example, enemy attack/grunt/movement sounds can be stored using a single channel. It is only until the sound is placed in the world that it gains a position. Then using the position information and the Doppler effect the audio is converted to 2 or more different channels for the speakers to play and your brain to interpret.

Furthermore: operations on channels.

Operations on channels

Because the channel count is stored in the Samples struct, Streamer operations that act on those channels become a possibility. This gives the user better control of what they want to do:

streamer, format, err := vorbis.Decode(myFileReader)
if err != nil {
	panic(err)
}

channels := SplitChannels(streamer)
desiredChannels := MergeChannels(channels[0], channels[2]) // keep only the front left and front right channel

err = speaker.Init(format.SampleRate, format.SampleRate.N(time.Second))
if err != nil {
	panic(err)
}
speaker.Play(disiredChannels)

I suspect the implementation of SplitChannels() and MergeChannels() will be a bit more complex than it may look at first. But I think it is doable.

Cons

  • Like I said, the Streamer becomes slightly more complex in some way.
  • Implementations of Streamer must support different values for NumChannels or return an error if the channel count is unsupported.
  • The speaker/Oto doesn't support more than 2 channels currently. It will be required to manually transform whatever Streamer you have to the required number of channels. However, the tools to do so will be available to you (see previous code snippet).
  • These changes are not backwards compatible.

Make testing with the speaker more convenient

I was just testing a PR and wanted to play something through a speaker. The speaker doesn't wait for playback to finish. The tutorial states that you have to do the following:

done := make(chan bool)
speaker.Play(beep.Seq(streamer, beep.Callback(func() {
	done <- true
})))

<-done

It would be nice to have a helper method to do this:

speaker.PlayAndWait(streamer)
speaker.PlaySync(streamer) //or

This method must work with multiple streamers, just like speaker.Play accepts multiple streamers.

Notes for myself:

  • In what other scenario's is beep.Callback used?
  • Make the tutorial explain beep.Callback's uses in other ways.

Fix crash in resampler

I noticed that when using small buffers and rapidly changing ratio, the resampler tries to access samples outside of the old buffer buf1, causing a crash.

My solution is to simply use the oldest sample we have. Not sure if this is good enough, but works for me.

Original issue: faiface/beep#152

Unsupported Format Type - 3 Error

Hello!

I'm using beep to load sounds for my ideaboard and project planning tool, MasterPlan, and it's been working pretty well; thank you very much for the excellent cross-platform audio library!

However, I've noticed that some files are unable to be decoded, and return an error of unsupported format type - 3. After re-exporting one WAV file that wasn't working into a couple of different formats, it looks like 32-bit PCM and 32-bit Float aren't supported, while 24-bit PCM works fine. For reference, the sound file I'm testing with is this one from FreeSound.org.

Is this a known issue or something that may be resolved in the future? If not because there's not enough use for streaming sound files that use such high bit-depth or sample rate, that would be fine, but could it then be possible to improve the error message to specifically mention the bit depth or sample rate as being unsupported in this case?

Thank you very much!

Original issue: faiface/beep#103

Improve error checking/handling

Collection issue for places where error checking or handling could be improved:

  • speaker.Init() check for valid samplerate #14
  • vorbis.Decode() doesn't check if the number of channels is supported. Check the decode methods of other formats as well.

What's the proper way to close underlying streamers from beep.Seq (or beep.Resample)

Hi, I'm confused about the proper method for closing underlying streamers in a beep.Seq if I can't defer the .Close where I instantiate the streamer ?
Most of the examples are withing a single scope so defer does the job, but for a more complex app, how do I access the .Close function ?
I've been using speaker.Clear() if I want to stop a sequence and play another, but doesn't that mean that the files in the sequence don't get closed properly ?

Original issue: faiface/beep#106

High CPU usage after `speaker.Init()` called

First of all, thanks for Beep and the excellent documentation.

I've written a doorbell in Go that listens to an MQTT channel and then plays a ding-dong noise when a button is pressed. I can deploy the code to multiple hosts on different platforms, which is the appeal of using Go for this.

My code runs as a systemd unit, starts up and initialises the sound streams using Beep's speaker package. However, I've noticed that the baseline CPU usages is pretty high - I have two wav files open and this is using ~15% CPU - even on a puny Celeron box this seems excessive. I've written some minimal code to reproduce this:

package main

import (
    "os"
    "github.com/faiface/beep"
    "github.com/faiface/beep/wav"
    "github.com/faiface/beep/speaker"    
    "time"
)

func main() {
    path := os.Args[1]

    f, _ := os.Open(path)

    streamer, format, _ := wav.Decode(f)    

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

}

Save the snippet above as play.go and then run with go run play.go <wavfile>. During the time.Sleep (after init, but before the sound is actually played) you can see the usage in top.

I can reduce the CPU usage by reducing the sample rate in the Init call - with time.Second it was down to about 1% - but that still seems high since it should be completely idle. I also tried using buffering as per the wiki, but that didn't make any difference. Did I miss something here? I'm using go1.18.1.

Original issue: faiface/beep#159

Fix mp3.decoder.Len() when file size is unknown

I'm trying to read an audio file through a reader from a []byte array, but when I do this, the length of the output stream is 0. However, using the traditional method of os.Open(), the length is 9589248. Both methods can play the audio, but I can't use .Seek() when using the byte array. I'm not sure what's wrong, any help would be appreciated.

package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/faiface/beep/mp3"
)

func main() {
	file, err := ioutil.ReadFile("audio.mp3")
	if err != nil {
		panic(err)
	}

	file2, err := os.Open("audio.mp3")
	if err != nil {
		panic(err)
	}

	fileString, _, _ := mp3.Decode(ioutil.NopCloser(bytes.NewReader(file))) // Outputs 0
	fileString2, _, _ := mp3.Decode(file2) // Outputs 9589248

	fmt.Println(fileString.Len())
	fmt.Println(fileString2.Len())
}

Original issue: faiface/beep#123

wav file header is fully inconsistent before seek fix

wav.Encode function tries to write all samples and then seek to the beginning of file and set correct duration in the header.

but it is not fully usefull in cases when it is used for streaming, for example wav.Encode(os.Stdout, streamer, format)

sox tool also do something like that, but if seeking is not possible, their header remains consistent, and if we make something like this:
rec -t wav - | play -t wav -
2 copies of sox will perfectly understand each other, "unknown" will be in the duration field.

with your function header is fully inconsistent.

please patch the main Encode function or add an other function that don't try to do anything with length in the header, may be simply keep it 0, and expect io.Writer instead WriteSeeker.

Original issue: faiface/beep#120

Support 5.1 Vorbis files

The code no longer assumes Vorbis files are stereo (they may have up to 255 channels), which caused e.g. mono files to play at twice their speed.

The current strategy for files with more than 2 channels is to only play the first and last channels, and drop the rest.

Original issue: faiface/beep#154

Segmentation Fault

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/[email protected]/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/[email protected]/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/[email protected]/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/[email protected]/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/[email protected]/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/[email protected]/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/[email protected]/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/[email protected]/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/[email protected]/lib/renderer/webview.go:235 +0x1ce
github.com/wailsapp/wails.(*App).start(0xc000530000, 0x0, 0x0)
/home/christopher/go/pkg/mod/github.com/wailsapp/[email protected]/app.go:151 +0x59f
github.com/wailsapp/wails.(*App).Run(0xc000530000, 0x86bd80, 0xa743b0)
/home/christopher/go/pkg/mod/github.com/wailsapp/[email protected]/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/[email protected]/xgb.go:253 +0x9e
created by github.com/BurntSushi/xgb.postNewConn
/home/christopher/go/pkg/mod/github.com/!burnt!sushi/[email protected]/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/[email protected]/xgb.go:278 +0x73
created by github.com/BurntSushi/xgb.postNewConn
/home/christopher/go/pkg/mod/github.com/!burnt!sushi/[email protected]/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/[email protected]/tebata.go:37 +0x31
created by github.com/syossan27/tebata.New
/home/christopher/go/pkg/mod/github.com/syossan27/[email protected]/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/[email protected]/xgb.go:253 +0x9e
created by github.com/BurntSushi/xgb.postNewConn
/home/christopher/go/pkg/mod/github.com/!burnt!sushi/[email protected]/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/[email protected]/xgb.go:278 +0x73
created by github.com/BurntSushi/xgb.postNewConn
/home/christopher/go/pkg/mod/github.com/!burnt!sushi/[email protected]/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/[email protected]/xgb.go:278 +0x73
created by github.com/BurntSushi/xgb.postNewConn
/home/christopher/go/pkg/mod/github.com/!burnt!sushi/[email protected]/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/[email protected]/xgb.go:253 +0x9e
created by github.com/BurntSushi/xgb.postNewConn
/home/christopher/go/pkg/mod/github.com/!burnt!sushi/[email protected]/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/[email protected]/lib/event/manager.go:103 +0x19c
created by github.com/wailsapp/wails/lib/event.(*Manager).Start
/home/christopher/go/pkg/mod/github.com/wailsapp/[email protected]/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/[email protected]/lib/ipc/manager.go:56 +0xdd
created by github.com/wailsapp/wails/lib/ipc.(*Manager).Start
/home/christopher/go/pkg/mod/github.com/wailsapp/[email protected]/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/[email protected]/context.go:116 +0xe5
github.com/faiface/beep/speaker.Close()
/home/christopher/go/pkg/mod/github.com/faiface/[email protected]/speaker/speaker.go:73 +0x55
github.com/faiface/beep/speaker.Init(0xac44, 0x113a, 0x0, 0x0)
/home/christopher/go/pkg/mod/github.com/faiface/[email protected]/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.

Original issue: faiface/beep#102

feature request: add beep/pcm package for reading raw PCM data

There's no simple way of converting an io.Reader into a beep.Streamer and vice-versa. The pcm package would provide the following api:

package pcm

func Decode(r io.Reader, format beep.Format) beep.Streamer
func Encode(w io.Writer, s beep.Stream, format beep.Format) error
func NewReader(s beep.Stream, format beep.Format) io.Reader

Original issue: faiface/beep#104

Confused about when to call speaker.Play()

First, thank you for the awesome project!

I have multiple, large music files I want to stream from disk, like this:

  • play song A
  • stop song A, start playing B
  • stop B, continue playing A
  • etc

When a song plays all the way through, I rewind it (via. streamer.Seek(0)).

Given this kind of usage, what I've come up with is to have ctrl := beep.Ctrl{...} where I keep changing ctrl.Streamer to the song I want to play.

However I'm confused about when I need to call speaker.Play(). Do I just call it once, after constructing ctrl? Or every time I change ctrl.Streamer? Or only after I rewind a streamer?

Thanks for any info!

Original issue: faiface/beep#143

Also send output to a new file vs just speaker

Hello, I have example from https://github.com/faiface/beep/wiki/Composing-and-controlling

	for {
		fmt.Print("Press [ENTER] to pause/resume. ")
		fmt.Scanln()

		speaker.Lock()
		ctrl.Paused = !ctrl.Paused
		volume.Volume += 0.5
		speedy.SetRatio(speedy.Ratio() + 0.1) // <-- right here
		speaker.Unlock()
	}

working nicely. My question is, can I also send the output to a NEW mp3 file on disk with all the pauses (added silence) and the speed increases as the user controls them? That is they play the original source.mp3 file (say it's 1 minute long) and hit pause as they like, and speed it up, slow it down etc. and the end result is 2 minutes of audio played through the speaker. Can I have that 2 minute of new audio as a new mp3 file?

Original issue: faiface/beep#150

Can't compile to wasm

Hi ! I've been toying a little with Beep under Mac OS with success. Great lib !

Then I tried to compile my little hello world for wasm with

GOOS=js GOARCH=wasm go build -o tstbeep

and I got these errors

/Users/chris/go/pkg/mod/github.com/gopherjs/[email protected]/js/js_wasm.go:38:17: undefined: js.Callback
/Users/chris/go/pkg/mod/github.com/gopherjs/[email protected]/js/js_wasm.go:40:26: undefined: js.EventCallbackFlag
/Users/chris/go/pkg/mod/github.com/gopherjs/[email protected]/js/js_wasm.go:43:29: undefined: js.PreventDefault
/Users/chris/go/pkg/mod/github.com/gopherjs/[email protected]/js/js_wasm.go:44:29: undefined: js.StopPropagation
/Users/chris/go/pkg/mod/github.com/gopherjs/[email protected]/js/js_wasm.go:45:29: undefined: js.StopImmediatePropagation
/Users/chris/go/pkg/mod/github.com/gopherjs/[email protected]/js/js_wasm.go:49:9: undefined: js.NewCallback
/Users/chris/go/pkg/mod/github.com/gopherjs/[email protected]/js/js_wasm.go:53:9: undefined: js.NewEventCallback
/Users/chris/go/pkg/mod/github.com/gopherjs/[email protected]/js/js_wasm.go:64:19: undefined: js.TypedArray
/Users/chris/go/pkg/mod/github.com/gopherjs/[email protected]/js/js_wasm.go:67:9: undefined: js.TypedArrayOf

Most, if not all, of these functions have been deprecated for a while.

At first I thought it could be a problem related with Oto, so I compiled https://github.com/hajimehoshi/oto/blob/master/example/main.go to wasm and go it working (at least for Firefox, Chrome is more restrictive but that's not the point)

My tstbeep go.mod is simple as

module tstbeep

go 1.16

require (
	github.com/faiface/beep v1.0.2 // indirect
	github.com/hajimehoshi/oto v0.3.1 // indirect
)

I really don't see where gopherjs comes into the dance. Oto doesn't seems to use it, neither Beep...

So, because I had installed (and never used) gopherjs :
rm -rf gopherjs ~/go/bin directory
and
rm -rf gopherjs ~/go/pkg/mod/github.com/gopherjs

And issuing a
GOOS=js GOARCH=wasm go build -o tstbeep

resurrected ~/go/pkg/mod/github.com/gopherjs

Any clue ?

Original issue: faiface/beep#113

How to fade out playing sound or start/stop without clicking/popping

I've read the docs + tutorial (and #49) but somehow this isn't quite sticking in my head right. I have a list of .wav files and when the user clicks on one I want to load and play it. So far so good and that's proved very easy with beep - thanks!

What I started with was when the selected file changed I load it up and then call speaker.Clear() and then speaker.Play() with the new data. It works nicely but sometimes there is a bit of a click or pop (fair enough). I've tried to add a quick fade out over a few milliseconds but I'm clearly not understanding how the volume scaling works.

I have an effects.Volume using Base 10 (but 2 also doesn't seem to do what I want). So I've done stuff like this:

	for i := 0; i < 80; i++ {
		volume.Volume = 0 - float64(i)/2
		time.Sleep(1 * time.Millisecond)
	}

Then I

	volume.Silent = true
	time.Sleep(5 * time.Millisecond)
	speaker.Clear()
	volume.Silent = false
	volume.Volume = 0

	// skipping some resampling but I update the Volume streamer here.

	speaker.Play(beep.Seq(&volume, beep.Callback(func() {
		done <- true
	})))
     

Theres is some latency (unsurprising given 85ms sleeping!) but this doesn't really seem to affect my clicks much at all. I think I've missed something really fundamental here. Would appreciate any help you can give.

Original issue: faiface/beep#134

go-mp3 in unmaintained

The go-mp3 library that Beep uses is no longer being maintained.

If you know a good alternatives, please share! :)

If that implementation happens to support encoding as well as decoding, even better! (I think a license is no longer needed question mark?)

Is there a smart way to play overlapping sounds?

I'm developing an application that plays overlapping .wav files, and I want to be able to mute/unmute or pause/start a specific one on a specific input. Should I load the files into the same beep.Streamer and load it into one beep.Speaker or into the same beep.Buffer and then into a beep.Speaker or approach it in a different way? What's the 'correct' way to do so?

Original issue: faiface/beep#161

go 1.18 module support

Add proper module support for go 1.18, otherwise things that require submodules (like generators, etc) will not be able to resolve properly in the build system.

Original issue: faiface/beep#149

Setting Streamer to nil not stopping sound

Hello, I'm from the IKEMEN-Go project. On our save-state branch, we are having an issue where setting the ctrl.Streamer instance to nil isn't stopping sound. Setting ctrl.Paused = true stops the sound, though. Any suggestions as to what could be wrong and how to pin down this issue? Here is the code in our library:

Our custom streamer for handling Panning and Volume

type SoundEffect struct {
	streamer beep.Streamer
	volume   float32
	ls, p    float32
	x        *float32
}

// sys is our global Game object 
func (s *SoundEffect) Stream(samples [][2]float64) (n int, ok bool) {
	lv, rv := s.volume, s.volume
	if sys.stereoEffects && (s.x != nil || s.p != 0) {
		var r float32
		if s.x != nil { // pan
			r = ((sys.xmax - s.ls**s.x) - s.p) / (sys.xmax - sys.xmin)
		} else { // abspan
			r = ((sys.xmax-sys.xmin)/2 - s.p) / (sys.xmax - sys.xmin)
		}
		sc := sys.panningRange / 100
		of := (100 - sys.panningRange) / 200
		lv = s.volume * 2 * (r*sc + of)
		rv = s.volume * 2 * ((1-r)*sc + of)
		if lv > 512 {
			lv = 512
		} else if lv < 0 {
			lv = 0
		}
		if rv > 512 {
			rv = 512
		} else if rv < 0 {
			rv = 0
		}
	}

	n, ok = s.streamer.Stream(samples)
	for i := range samples[:n] {
		samples[i][0] *= float64(lv / 256)
		samples[i][1] *= float64(rv / 256)
	}
	return n, ok
}

func (s *SoundEffect) Err() error {
	return s.streamer.Err()
}

How we play sound

func (s *SoundChannel) Play(sound *Sound, loop bool, freqmul float32) {
	if sound == nil {
		return
	}
	s.sound = sound
	s.streamer = s.sound.GetStreamer()
	loopCount := int(1)
	if loop {
		loopCount = -1
	}
	looper := beep.Loop(loopCount, s.streamer)
	s.sfx = &SoundEffect{streamer: looper, volume: 256}
	srcRate := s.sound.format.SampleRate
	dstRate := beep.SampleRate(audioFrequency / freqmul)
	resampler := beep.Resample(audioResampleQuality, srcRate, dstRate, s.sfx)
	s.ctrl = &beep.Ctrl{Streamer: resampler}
	sys.soundMixer.Add(s.ctrl)
}

How we are stopping sound

func (s *SoundChannel) Stop() {
	if s.ctrl != nil {
		speaker.Lock()
		s.ctrl.Streamer = nil
		speaker.Unlock()
	}
	s.sound = nil
}

Original issue: faiface/beep#153

speaker.Init function shoud add recover to deal panic?

go func() {
	for {
		select {
		default:
			update()
		case <-done:
			return
		}
	}
}()

panic: runtime error: index out of range [-111848]

goroutine 155 [running]:
github.com/faiface/beep.(*Resampler).Stream(0xc00084d880, {0xc00083e000?, 0x67?, 0xc0000c9ee0?})
C:/Users/Ares/go/pkg/mod/github.com/faiface/[email protected]/resample.go:96 +0x3d3
github.com/faiface/beep.(*Ctrl).Stream(0x1000000000067?, {0xc00083e000?, 0xc00047dcd8?, 0x1010000000000?})
C:/Users/Ares/go/pkg/mod/github.com/faiface/[email protected]/ctrl.go:43 +0x65
github.com/faiface/beep.Seq.func1({0xc00083e000?, 0x7ff76a618320?, 0x7ff769398701?})
C:/Users/Ares/go/pkg/mod/github.com/faiface/[email protected]/compositors.go:86 +0xb4
github.com/faiface/beep.StreamerFunc.Stream(0x44e8?, {0xc00083e000?, 0xc0000c9f80?, 0x7ff76972a996?})
C:/Users/Ares/go/pkg/mod/github.com/faiface/[email protected]/interface.go:100 +0x2a
github.com/faiface/beep.(*Mixer).Stream(0x7ff76aa36d80, {0xc002310000, 0x113a, 0x113a})
C:/Users/Ares/go/pkg/mod/github.com/faiface/[email protected]/mixer.go:42 +0x194
github.com/faiface/beep/speaker.update()
C:/Users/Ares/go/pkg/mod/github.com/faiface/[email protected]/speaker/speaker.go:109 +0x65
github.com/faiface/beep/speaker.Init.func1()
C:/Users/Ares/go/pkg/mod/github.com/faiface/[email protected]/speaker/speaker.go:52 +0x1b
created by github.com/faiface/beep/speaker.Init
C:/Users/Ares/go/pkg/mod/github.com/faiface/[email protected]/speaker/speaker.go:48 +0x1e9

Process finished with the exit code 2

Original issue: faiface/beep#155

Improve performance of vorbis.decoder.Stream()`

The underlying package github.com/jfreymuth/oggvorbis's Reader struct can Read multiple samples at once. Currently, Beep only asks it for a single sample per channel at a time. Then the package has to do a bunch of checks for every sample. I think this can be improved by requesting a small (but larger that 2, e.g. 512) buffer of samples at a time.

  • Benchmark test
  • Improved implementation of vorbis.decoder.Stream()

[tweak] expose `beep.Buffer.data` data to caller

I'm using beep to play audio that I have already decoded into PCM s16le as a []byte.

AFAIK the beep API forces me to then wrap that slice in a streamer and append it to a beep.Buffer. However, I would prefer to just shove my byte slice onto the data field on the beep.Buffer - since it's already correctly formatted.

Opening up this fields by making it public or by introducing a second constructor would be strictly backwards compatible, and trivial to do.

Thoughts on this? Perhaps I'm misunderstanding the existing API.

In the meantime I have copied the type in it's entirety to achieve this, which does work but isn't ideal.

Thanks for the excellent package!

Original issue: faiface/beep#135

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.