flacgo/cmd/wav2flac/main.go
Robin Eklind 3e3f4b5fcf
flac: add Encoder API to encode audio samples and metadata blocks (#32)
* flac: encode frame header

* flac: calculate CRC-8 when encoding frame headers

* flac: fix encoding of frame header

* flac: add preliminary subframe encoder

* flac: fix UTF-8 encoding of frame number

* frame: add sanity check for sample count in decodeLPC

Updates #31.

* flac: update flac encoding API, depricate flac.Encode

Encode has been removed in favour of using NewEncoder.
The Encode function was temporarily added to support
re-encoding FLAC streams to update the metadata, but
it had no support for encoding audio samples.

The added flac.Encoder has support for encoding both
metadata and audio samples. It also does not require
that you first decode a FLAC file to later re-encode
it by calling Encode (as was the previous behaviour).

* flac: add MD5 running hash of unencoded audio samples to StreamInfo

* flac: remove unused encodePadding

Reported by golangci

* flac: fix golangci lint issues

	frame/utf8.go:57:6: `decodeUTF8Int` is unused (deadcode)
	func decodeUTF8Int(r io.Reader) (n uint64, err error) {
		  ^
	internal/utf8/encode.go:32:16: unnecessary conversion (unconvert)
			bits = uint64(t2 | (x>>6)&mask2)
							 ^
	internal/utf8/encode.go:37:16: unnecessary conversion (unconvert)
			bits = uint64(t3 | (x>>(6*2))&mask3)
							 ^
	internal/utf8/encode.go:42:16: unnecessary conversion (unconvert)
			bits = uint64(t4 | (x>>(6*3))&mask4)
							 ^

* flac: fix golangci lint issues

	encode_frame.go:89:1: cyclomatic complexity 52 of func `(*Encoder).encodeFrameHeader` is high (> 30) (gocyclo)
	func (enc *Encoder) encodeFrameHeader(w io.Writer, hdr frame.Header) error {
	^
	internal/utf8/encode.go:66:17: unnecessary conversion (unconvert)
			bits := uint64(tx | (x>>uint(6*i))&maskx)
							  ^
	encode_subframe.go:105:46: unnecessary conversion (unconvert)
			if err := bw.WriteBits(uint64(sample), byte(hdr.BitsPerSample)); err != nil {
																	 ^

* flac: clarify that frame.Header.Num is calculated by the encoder

* flac: minor re-phrasing
2018-08-19 03:18:12 +09:00

210 lines
6.3 KiB
Go

package main
import (
"flag"
"fmt"
"log"
"os"
"github.com/go-audio/audio"
"github.com/go-audio/wav"
"github.com/mewkiz/flac"
"github.com/mewkiz/flac/frame"
"github.com/mewkiz/flac/meta"
"github.com/mewkiz/pkg/osutil"
"github.com/mewkiz/pkg/pathutil"
"github.com/pkg/errors"
)
func main() {
// Parse command line arguments.
var (
// force overwrite FLAC file if already present.
force bool
)
flag.BoolVar(&force, "f", false, "force overwrite")
flag.Parse()
for _, wavPath := range flag.Args() {
if err := wav2flac(wavPath, force); err != nil {
log.Fatalf("%+v", err)
}
}
}
func wav2flac(wavPath string, force bool) error {
// Create WAV decoder.
r, err := os.Open(wavPath)
if err != nil {
return errors.WithStack(err)
}
defer r.Close()
dec := wav.NewDecoder(r)
if !dec.IsValidFile() {
return errors.Errorf("invalid WAV file %q", wavPath)
}
sampleRate, nchannels, bps := int(dec.SampleRate), int(dec.NumChans), int(dec.BitDepth)
// Create FLAC encoder.
flacPath := pathutil.TrimExt(wavPath) + ".flac"
if !force && osutil.Exists(flacPath) {
return errors.Errorf("FLAC file %q already present; use -f flag to force overwrite", flacPath)
}
w, err := os.Create(flacPath)
if err != nil {
return errors.WithStack(err)
}
info := &meta.StreamInfo{
// Minimum block size (in samples) used in the stream; between 16 and
// 65535 samples.
BlockSizeMin: 16, // adjusted by encoder.
// Maximum block size (in samples) used in the stream; between 16 and
// 65535 samples.
BlockSizeMax: 65535, // adjusted by encoder.
// Minimum frame size in bytes; a 0 value implies unknown.
//FrameSizeMin // set by encoder.
// Maximum frame size in bytes; a 0 value implies unknown.
//FrameSizeMax // set by encoder.
// Sample rate in Hz; between 1 and 655350 Hz.
SampleRate: uint32(sampleRate),
// Number of channels; between 1 and 8 channels.
NChannels: uint8(nchannels),
// Sample size in bits-per-sample; between 4 and 32 bits.
BitsPerSample: uint8(bps),
// Total number of inter-channel samples in the stream. One second of
// 44.1 KHz audio will have 44100 samples regardless of the number of
// channels. A 0 value implies unknown.
//NSamples // set by encoder.
// MD5 checksum of the unencoded audio data.
//MD5sum // set by encoder.
}
enc, err := flac.NewEncoder(w, info)
if err != nil {
return errors.WithStack(err)
}
defer enc.Close()
// Encode samples.
if err := dec.FwdToPCM(); err != nil {
return errors.WithStack(err)
}
// Number of samples per channel and block.
const nsamplesPerChannel = 16
nsamplesPerBlock := nchannels * nsamplesPerChannel
buf := &audio.IntBuffer{
Format: &audio.Format{
NumChannels: nchannels,
SampleRate: sampleRate,
},
Data: make([]int, nsamplesPerBlock),
SourceBitDepth: bps,
}
subframes := make([]*frame.Subframe, nchannels)
subHdr := frame.SubHeader{
// Specifies the prediction method used to encode the audio sample of the
// subframe.
Pred: frame.PredVerbatim,
// Prediction order used by fixed and FIR linear prediction decoding.
Order: 0,
// Wasted bits-per-sample.
Wasted: 0,
}
for i := range subframes {
subframe := &frame.Subframe{
SubHeader: subHdr,
Samples: make([]int32, nsamplesPerChannel),
}
subframes[i] = subframe
}
for j := 0; !dec.EOF(); j++ {
// Decode WAV samples.
n, err := dec.PCMBuffer(buf)
if err != nil {
return errors.WithStack(err)
}
if n == 0 {
break
}
for _, subframe := range subframes {
subframe.NSamples = n / nchannels
subframe.Samples = subframe.Samples[:subframe.NSamples]
}
for i, sample := range buf.Data {
subframe := subframes[i%nchannels]
subframe.Samples[i/nchannels] = int32(sample)
}
fmt.Println("j:", j)
// Encode FLAC frame.
channels, err := getChannels(nchannels)
if err != nil {
return errors.WithStack(err)
}
hdr := frame.Header{
// Specifies if the block size is fixed or variable.
HasFixedBlockSize: false,
// Block size in inter-channel samples, i.e. the number of audio samples
// in each subframe.
BlockSize: uint16(nsamplesPerChannel),
// Sample rate in Hz; a 0 value implies unknown, get sample rate from
// StreamInfo.
SampleRate: uint32(sampleRate),
// Specifies the number of channels (subframes) that exist in the frame,
// their order and possible inter-channel decorrelation.
Channels: channels,
// Sample size in bits-per-sample; a 0 value implies unknown, get sample
// size from StreamInfo.
BitsPerSample: uint8(bps),
// Specifies the frame number if the block size is fixed, and the first
// sample number in the frame otherwise. When using fixed block size, the
// first sample number in the frame can be derived by multiplying the
// frame number with the block size (in samples).
//Num // set by encoder.
}
f := &frame.Frame{
Header: hdr,
Subframes: subframes,
}
if err := enc.WriteFrame(f); err != nil {
return errors.WithStack(err)
}
}
return nil
}
// getChannels returns the channels assignment matching the given number of
// channels.
func getChannels(nchannels int) (frame.Channels, error) {
switch nchannels {
case 1:
// 1 channel: mono.
return frame.ChannelsMono, nil
case 2:
// 2 channels: left, right.
return frame.ChannelsLR, nil
//return frame.ChannelsLeftSide, nil // 2 channels: left, side; using inter-channel decorrelation.
//return frame.ChannelsSideRight, nil // 2 channels: side, right; using inter-channel decorrelation.
//return frame.ChannelsMidSide, nil // 2 channels: mid, side; using inter-channel decorrelation.
case 3:
// 3 channels: left, right, center.
return frame.ChannelsLRC, nil
case 4:
// 4 channels: left, right, left surround, right surround.
return frame.ChannelsLRLsRs, nil
case 5:
// 5 channels: left, right, center, left surround, right surround.
return frame.ChannelsLRCLsRs, nil
case 6:
// 6 channels: left, right, center, LFE, left surround, right surround.
return frame.ChannelsLRCLfeLsRs, nil
case 7:
// 7 channels: left, right, center, LFE, center surround, side left, side right.
return frame.ChannelsLRCLfeCsSlSr, nil
case 8:
// 8 channels: left, right, center, LFE, left surround, right surround, side left, side right.
return frame.ChannelsLRCLfeLsRsSlSr, nil
default:
return 0, errors.Errorf("support for %d number of channels not yet implemented", nchannels)
}
}