mirror of https://bitbucket.org/ausocean/av.git
132 lines
3.5 KiB
Go
132 lines
3.5 KiB
Go
|
/*
|
||
|
NAME
|
||
|
wav.go
|
||
|
|
||
|
DESCRIPTION
|
||
|
wav.go contains functions for processing wav.
|
||
|
|
||
|
AUTHOR
|
||
|
David Sutton <davidsutton@ausocean.org>
|
||
|
|
||
|
LICENSE
|
||
|
wav.go is Copyright (C) 2023 the Australian Ocean Lab (AusOcean)
|
||
|
|
||
|
It is free software: you can redistribute it and/or modify them
|
||
|
under the terms of the GNU General Public License as published by the
|
||
|
Free Software Foundation, either version 3 of the License, or (at your
|
||
|
option) any later version.
|
||
|
|
||
|
It is distributed in the hope that it will be useful, but WITHOUT
|
||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||
|
for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License in gpl.txt.
|
||
|
If not, see [GNU licenses](http://www.gnu.org/licenses).
|
||
|
*/
|
||
|
|
||
|
// Package wav provides functions for converting wav audio.
|
||
|
package wav
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
"fmt"
|
||
|
)
|
||
|
|
||
|
const PCMFormat = 1 // PCMFormat defines the value for pcm audio as defined by the wav std.
|
||
|
|
||
|
var (
|
||
|
errInvalidFormat = fmt.Errorf("invalid or no format defined")
|
||
|
errInvalidRate = fmt.Errorf("invalid or no sample rate defined")
|
||
|
errInvalidChannels = fmt.Errorf("invalid or no number of channels defined")
|
||
|
errInvalidBitDepth = fmt.Errorf("invalid or no bit depth defined")
|
||
|
)
|
||
|
|
||
|
// Metadata defines the format of the audio file for reading.
|
||
|
type Metadata struct {
|
||
|
AudioFormat int
|
||
|
Channels int
|
||
|
SampleRate int
|
||
|
BitDepth int
|
||
|
}
|
||
|
|
||
|
type WAV struct {
|
||
|
Metadata Metadata
|
||
|
Audio []byte
|
||
|
}
|
||
|
|
||
|
// Write writes the given audio byte slice to the WAV, encoding the appropriate headings.
|
||
|
func (w *WAV) Write(p []byte) (n int, err error) {
|
||
|
// Create header slice.
|
||
|
header := make([]byte, 44)
|
||
|
|
||
|
// Write RIFF type.
|
||
|
copy(header[0:4], []byte("RIFF"))
|
||
|
|
||
|
// Write the size of overall file.
|
||
|
buf := make([]byte, 4)
|
||
|
binary.LittleEndian.PutUint32(buf, uint32(len(p)+44))
|
||
|
copy(header[4:8], buf)
|
||
|
|
||
|
// Write WAVE type.
|
||
|
copy(header[8:12], []byte("WAVE"))
|
||
|
|
||
|
// Write fmt chunk marker.
|
||
|
copy(header[12:16], []byte("fmt "))
|
||
|
|
||
|
// Write the subchunk1 Size.
|
||
|
binary.LittleEndian.PutUint32(buf, 16)
|
||
|
copy(header[16:20], buf)
|
||
|
|
||
|
// Write the encoded audio format.
|
||
|
if w.Metadata.AudioFormat != PCMFormat { // TODO: allow for more encoding formats.
|
||
|
return 0, errInvalidFormat
|
||
|
}
|
||
|
binary.LittleEndian.PutUint16(buf[0:2], 1)
|
||
|
copy(header[20:22], buf[0:2])
|
||
|
|
||
|
// Write the number of channels.
|
||
|
if w.Metadata.Channels == 0 {
|
||
|
return 0, errInvalidChannels
|
||
|
}
|
||
|
binary.LittleEndian.PutUint16(buf[0:2], uint16(w.Metadata.Channels))
|
||
|
copy(header[22:24], buf[0:2])
|
||
|
|
||
|
// Write the sample rate.
|
||
|
if w.Metadata.SampleRate == 0 {
|
||
|
return 0, errInvalidRate
|
||
|
}
|
||
|
binary.LittleEndian.PutUint32(buf[0:4], uint32(w.Metadata.SampleRate))
|
||
|
copy(header[24:28], buf[0:4])
|
||
|
|
||
|
// Write bit depth values.
|
||
|
if w.Metadata.BitDepth == 0 {
|
||
|
return 0, errInvalidBitDepth
|
||
|
}
|
||
|
var val uint32 = uint32((w.Metadata.SampleRate * w.Metadata.BitDepth * w.Metadata.Channels) / 8)
|
||
|
binary.LittleEndian.PutUint32(buf[0:4], val)
|
||
|
copy(header[28:32], buf[0:4])
|
||
|
|
||
|
val = uint32((w.Metadata.BitDepth * w.Metadata.Channels) / 8)
|
||
|
binary.LittleEndian.PutUint32(buf[0:4], val)
|
||
|
copy(header[32:34], buf[0:4])
|
||
|
|
||
|
binary.LittleEndian.PutUint32(buf[0:4], uint32(w.Metadata.BitDepth))
|
||
|
copy(header[34:36], buf[0:4])
|
||
|
|
||
|
// Mark start of data.
|
||
|
copy(header[36:40], []byte("data"))
|
||
|
|
||
|
// Write size of data chunk.
|
||
|
binary.LittleEndian.PutUint32(buf[0:4], uint32(len(p)))
|
||
|
copy(header[40:44], buf[0:4])
|
||
|
|
||
|
// Append audio data.
|
||
|
w.Audio = header
|
||
|
w.Audio = append(w.Audio, p...)
|
||
|
|
||
|
// Return successful write.
|
||
|
return len(p) + 44, nil
|
||
|
|
||
|
}
|