Wav audio file encoder and decoder. See GoDoc for more details.
go-audio / wav Goto Github PK
View Code? Open in Web Editor NEWBattle tested Wav decoder/encoder
License: Apache License 2.0
Battle tested Wav decoder/encoder
License: Apache License 2.0
Wav audio file encoder and decoder. See GoDoc for more details.
I am trying gomobile with the OpenAL module (https://godoc.org/golang.org/x/mobile/exp/audio/al). I need to pass raw byte data, but it seems this library only provides ints and floats. Am I missing something?
Is it possible to generate Oscillogram of a .wav audio from this library?
Hi Matt,
I've been experimenting with go-audio/audio
and go-audio/wav
, trying to parse PCM data (signed, 8 kHz, 16 bit, 1 channel) coming from Amazon Polly into a .wav file. Is this possible? The PCM data I have from Amazon is in a io.ReadCloser
. I've tried writing it to a file and decoding that with FullPCMBuffer
but then I encounter a "PCM chunk not found" error.
Any help/pointers are welcome. Thanks! :)
https://sites.google.com/site/musicgapi/technical-documents/wav-file-format#smpl
we should support the cue chunk
I noticed this: https://github.com/go-audio/wav/blob/master/decoder.go#L343
While developing my quick-hack audio decoder for WAV I ended up, IMHO, a neat design for decoding the buffer:
Maybe you can get new ideas from it -- or find better approaches based on it.
I tried the converting from pcm to wav file and succeeded.
However in my go server I want to reduce disk IO becasue all my data are []bytes
in memory(my server can generate pcm bytes by some TTS server and will serve as wav bytes for user to download).
NewEncoder
function can only accept io.WriteSeeker
, and I found a walkaround in stackoverflow to use a in memory one. However I think this walkaround is not elegant nor stable.
So could you consider add such in memory only convertion?
I sorta understand how to write WAV files from looking at the tests, but it's not obvious at all how to read samples from a file.
Please provide an example.
How can I get the duration of the audio of my current encoder, while still adding data to it? Use-case: I want to write the audio to disk in 5 minutes chunks.
Decoder
has a duration function, but not Encoder
and I don't see a simple way to create a decoder from an encoder that is still in use.
Having only a truncated IntBuffer feels extremely limiting, writing wav should accept a Float buffer - otherwise why are you using a lossless format.
In telephony server, I got PCM raw stream which is appended when user talking on their phone. Is it possible to create wav stream from the pcm with your library on fly?
Currently the decoding of the sampler information is working, but we we don't have the encoder part.
I try encode g729 codec wav file, and get panic: could not get sample decode func unhandled byte depth:0
Can I encode file from this codec with go-audio/wav?
Hi, could we get an example of how to use this library to adjust the sample rate to a new one?
Reading and writing wav files is great. Thank you very much.
I would also need to directly read and write from a small sound card connected on usb interface. The sound card has a mic input and headphone output. I need to record sounds and to write sounds with a go program.
Do you have a package that can do that ? Do you have a solution to suggest ?
func recordWav() string {
audioFileName := "temp.wav" //os.Args[1]
fmt.Println("Recording. Press ESC to quit.")
waveFile, err := os.Create(audioFileName)
chk(err)
// www.people.csail.mit.edu/hubert/pyaudio/ - under the Record tab
inputChannels := 1
outputChannels := 0
sampleRate := 44100
// init PortAudio
portaudio.Initialize()
//defer portaudio.Terminate()
encoder := wav.NewEncoder(waveFile, sampleRate, 16, 1, 1)
format := &audio.Format{
NumChannels: 1,
SampleRate: 44100,
}
data := make([]int, 4096)
buf := &audio.IntBuffer{Data: data, Format: format, SourceBitDepth: 16}
buf32 := buf.AsFloat32Buffer()
stream, err := portaudio.OpenDefaultStream(inputChannels, outputChannels, float64(sampleRate), 64, buf32.Data)
chk(err)
chk(stream.Start())
for start := time.Now(); time.Since(start) < time.Second*7; {
chk(stream.Read())
log.Println("Recording...")
// write to wave file
err := encoder.Write(buf)
chk(err)
}
stream.Close()
portaudio.Terminate()
return audioFileName
}
The only array type that portaudio and audio.IntBuffer both support is float32[]. When I run this code, nothing gets recorded. The file is silent. I don't know if this is the right place for this, but what the heck am I doing wrong? Is it something to do with converting to float32?
It's risky for applications to handle errors based on string matching (e.g. error reading chunk header - EOF
). Instead, it would be better to return io.EOF
when a WAV read stream is finished.
I have a 8Khz 8-bit PCM ulaw byte slice that I would like to convert to a WAV file. When I try to convert it using this library, I get audio that is loud and choppy, sounding a bit like the following issue.
I'm using an Encoder
as follows:
wav := NewEncoder(f, 8000, 8, 1, 1)
I'm not quite sure how to convert the raw byte slice into an audio.IntBuffer
so I'm doing the following, which results in the poor quality audio.
func BytesToInts(bytes []byte) []int {
ints := []int{}
for _, b := range bytes {
ints = append(ints, int(b))
}
return ints
}
func UlawByteSliceToIntBuffer(bytes []byte) *audio.IntBuffer {
return &audio.IntBuffer{
Format: &audio.Format{
NumChannels: 1,
SampleRate: 8000},
SourceBitDepth: 8,
Data: BytesToInts(bytes)}
}
Any tips on how to get this library to work for this?
As an alternative, the following raw approach results in good sounding audio where I can write out the bytes without transformation.
var wavHeaderThin = []byte{0x52, 0x49, 0x46, 0x46, 0x62, 0xb8, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20,
0x12, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, 0x40, 0x1f, 0x00, 0x00, 0x80, 0x3e, 0x00, 0x00,
0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x66, 0x61, 0x63, 0x74, 0x04, 0x00, 0x00, 0x00, 0xc5, 0x5b,
0x00, 0x00, 0x64, 0x61, 0x74, 0x61}
func WriteFileWavFromUlaw(filename string, ulawBytes []byte) error {
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
_, err = WriteWavFromUlaw(f, ulawBytes)
return err
}
func WriteWavFromUlaw(w io.Writer, ulawBytes []byte) (n int, err error) {
n1, err := w.Write(wavHeaderThin)
if err != nil {
return n, err
}
n += n1
count := len(ulawBytes)
n2, err := w.Write([]byte{
byte(count % 256),
byte((count >> 8) % 256),
byte((count >> 16) % 256),
byte((count >> 24) % 256)})
if err != nil {
return n, err
}
n += n2
n3, err := w.Write(ulawBytes)
if err != nil {
return n, err
}
return n + n3, nil
}
I've added this here for easy reuse:
https://github.com/grokify/simplego/blob/master/audio/ulaw/ulaw.go
$ ffmpeg -f mulaw -ar 8000 -i input.ulaw output.wav
When using this library to transcode a wav file from pcm_s24le
(as ffprobe
reports), into a pcm_s16le
wav file, the resulting file is just static noise. I based my attempts off of the wave to aiff example found here: https://github.com/go-audio/wav/blob/master/cmd/wavtoaiff/main.go . However, I got the same result using my own hackish, handwritten transcoder, prior to attempting to use this one. Yet, ffmpeg has no problem transcoding the same file from s24le to s16le, and produces a coherent wav file without noise. Do you have any idea why might this be?
Here's some output, regarding the wav file which I'm attempting to convert, from mplayer:
Clip info:
encoder: FL Studio 21
[pcm] Uncompressed PCM audio decoder
AUDIO: 48000 Hz, 2 ch, s24le, 2304.0 kbit/100.00% (ratio: 288000->288000)
AO: [alsa] 48000Hz 2ch s24le (3 bytes per sample)
and from ffmpeg:
Stream #0:0: Audio: pcm_s24le ([1][0][0][0] / 0x0001), 48000 Hz, 2 channels, s32 (24 bit), 2304 kb/s
I've even tried using the audio.PCMBuffer
type, with the SwitchPrimaryType
function, to no avail. The only thing my program does with the decoder before calling PCMBuffer()
with an audio.IntBuffer
(as per the example), is to check IsValidFile()
. I've noticed that if I read the metadata, the reader isn't reset, so I've avoided any calls which might prevent it from reading the PCM data.
I keep my PCM audio samples as []int16
, and while learning to use this library, it seemed natural to use it like this:
data := []int16{...}
enc := wav.NewEncoder(f, 44100, 16, 1, 1)
if err := enc.WriteFrame(data); err != nil {
panic(err)
}
enc.Close()
The reason why this seemed like a good idea is that WriteFrame()
calls AddLE()
and this ultimately calls binary.Write()
which in addition to integers accepts slices of integers. The resulting file has the correct size.
BUT... WriteFrame()
(at line https://github.com/go-audio/wav/blob/master/encoder.go#L217) only increments the frame count by one, which makes the WAV headers only recognize a single sample. If this were handled so that WriteFrame()
checks for a slice argument, and/or a separate function WriteFrames()
is added to write the whole slice at once, it would simplify the library's usage.
Edit:
The alternative approach:
for _, sample := range data {
enc.WriteFrame(sample)
}
...is horribly slow. It takes several seconds to write a 2 MB buffer! This is because binary.Write()
issues a write syscall for every single call, i.e. for every single sample!
Created a pull request.
https://sites.google.com/site/musicgapi/technical-documents/wav-file-format#list
An associated data list chunk is used to define text labels and names which are associated with the cue points to provide each text label or name a position.
From the documentation, this package seam to support only predefined sampling rates (see audio.Format values).
My wave files have a 10kHz sampling rate. Will this package be able to handle these wav files ?
Hey,
this is not about any wrong behaviour I would have noticed, but something that puzzled me when reading the code.
At https://github.com/go-audio/wav/blob/v1.1.0/decoder.go#L318 , since we're not returning nil, and we are returning m, and not explicitly 0, I assume that the call to Read above, could have partially succeeded until we hit the EOF, right?
If not, and if m is always 0 here, then you can forget about this issue (except I would explicitly return 0 to make it clear).
But if yes, then it means there possibly indeed is some data in tmpBuf at this point, right? And if yes, then I'm wondering why we are not copying the data from tmpBuf into buf.Data ? Shouldn't buf.Data always be populated as soon as m >= 0 is returned?
What is the reasoning behind not returning the error here?
func (d *Decoder) ReadInfo() {
d.err = d.readHeaders()
}
It looks like this would be quite easy to do following the same approach already used for smpl etc.
https://sites.google.com/site/musicgapi/technical-documents/wav-file-format#inst
There appears to be an error on that link as the chunk id is 'inst' not 'ltxt' (which is a text label). I could potentially start work o a PR if there's itnerest.
.
This package should be standalone and doesn't need to rely on a different package.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.