Remove flac2wav / wav2flac examples on cmd
This commit is contained in:
parent
28f458bc53
commit
2ac188033a
|
@ -1,112 +0,0 @@
|
|||
// The flac2wav tool converts FLAC files to WAV files.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"git.gammaspectra.live/S.O.N.G/flacgo"
|
||||
"github.com/go-audio/audio"
|
||||
"github.com/go-audio/wav"
|
||||
"github.com/mewkiz/pkg/osutil"
|
||||
"github.com/mewkiz/pkg/pathutil"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func usage() {
|
||||
const use = `
|
||||
Usage: flac2wav [OPTION]... FILE.flac...`
|
||||
fmt.Fprintln(os.Stderr, use[1:])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Parse command line arguments.
|
||||
var (
|
||||
// force overwrite WAV file if present already.
|
||||
force bool
|
||||
)
|
||||
flag.BoolVar(&force, "f", false, "force overwrite")
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
for _, path := range flag.Args() {
|
||||
err := flac2wav(path, force)
|
||||
if err != nil {
|
||||
log.Fatalf("%+v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flac2wav converts the provided FLAC file to a WAV file.
|
||||
func flac2wav(path string, force bool) error {
|
||||
// Open FLAC file.
|
||||
stream, err := flac.Open(path)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
defer stream.Close()
|
||||
|
||||
// Create WAV file.
|
||||
wavPath := pathutil.TrimExt(path) + ".wav"
|
||||
if !force {
|
||||
if osutil.Exists(wavPath) {
|
||||
return errors.Errorf("WAV file %q already present; use the -f flag to force overwrite", wavPath)
|
||||
}
|
||||
}
|
||||
fw, err := os.Create(wavPath)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
defer fw.Close()
|
||||
|
||||
// Create WAV encoder.
|
||||
wavAudioFormat := 1 // PCM
|
||||
enc := wav.NewEncoder(fw, int(stream.Info.SampleRate), int(stream.Info.BitsPerSample), int(stream.Info.NChannels), wavAudioFormat)
|
||||
defer enc.Close()
|
||||
var data []int
|
||||
for {
|
||||
// Decode FLAC audio samples.
|
||||
frame, err := stream.ParseNext()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
// Encode WAV audio samples.
|
||||
data = data[:0]
|
||||
for i := 0; i < frame.Subframes[0].NSamples; i++ {
|
||||
for _, subframe := range frame.Subframes {
|
||||
sample := int(subframe.Samples[i])
|
||||
if frame.BitsPerSample == 8 {
|
||||
// WAV files with 8 bit-per-sample are stored with unsigned
|
||||
// values, WAV files with more than 8 bit-per-sample are stored
|
||||
// as signed values (ref page 59-60 of [1]).
|
||||
//
|
||||
// [1]: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/riffmci.pdf
|
||||
// ref: https://git.gammaspectra.live/S.O.N.G/flacgo/issues/51#issuecomment-1046183409
|
||||
const midpointValue = 0x80
|
||||
sample += midpointValue
|
||||
}
|
||||
data = append(data, sample)
|
||||
}
|
||||
}
|
||||
buf := &audio.IntBuffer{
|
||||
Format: &audio.Format{
|
||||
NumChannels: int(stream.Info.NChannels),
|
||||
SampleRate: int(stream.Info.SampleRate),
|
||||
},
|
||||
Data: data,
|
||||
SourceBitDepth: int(stream.Info.BitsPerSample),
|
||||
}
|
||||
if err := enc.Write(buf); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"git.gammaspectra.live/S.O.N.G/flacgo"
|
||||
"git.gammaspectra.live/S.O.N.G/flacgo/frame"
|
||||
"git.gammaspectra.live/S.O.N.G/flacgo/meta"
|
||||
"github.com/go-audio/audio"
|
||||
"github.com/go-audio/wav"
|
||||
"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 = 4096
|
||||
nsamplesPerBlock := nchannels * nsamplesPerChannel
|
||||
buf := &audio.IntBuffer{
|
||||
Format: &audio.Format{
|
||||
NumChannels: nchannels,
|
||||
SampleRate: sampleRate,
|
||||
},
|
||||
Data: make([]int, nsamplesPerBlock),
|
||||
SourceBitDepth: bps,
|
||||
}
|
||||
|
||||
subframes := make([]*frame.Subframe, nchannels)
|
||||
for i := range subframes {
|
||||
subframe := &frame.Subframe{
|
||||
Samples: make([]int32, nsamplesPerChannel),
|
||||
}
|
||||
subframes[i] = subframe
|
||||
}
|
||||
for frameNum := 0; !dec.EOF(); frameNum++ {
|
||||
fmt.Println("frame number:", frameNum)
|
||||
// Decode WAV samples.
|
||||
n, err := dec.PCMBuffer(buf)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
if n < 16*nchannels {
|
||||
n = 16
|
||||
//break
|
||||
}
|
||||
for _, subframe := range subframes {
|
||||
subframe.NSamples = n / nchannels
|
||||
subframe.Samples = subframe.Samples[:subframe.NSamples]
|
||||
}
|
||||
for i, sample := range buf.Data[:n] {
|
||||
subframe := subframes[i%nchannels]
|
||||
subframe.Samples[i/nchannels] = int32(sample)
|
||||
}
|
||||
|
||||
// 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(subframes[0].NSamples),
|
||||
// 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)
|
||||
}
|
||||
}
|
7
go.mod
7
go.mod
|
@ -3,11 +3,6 @@ module git.gammaspectra.live/S.O.N.G/flacgo
|
|||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/go-audio/audio v1.0.0
|
||||
github.com/go-audio/wav v1.1.0
|
||||
github.com/icza/bitio v1.1.0
|
||||
github.com/mewkiz/pkg v0.0.0-20211102230744-16a6ce8f1b77
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/mewkiz/pkg v0.0.0-20220820102221-bbbca16e2a6c
|
||||
)
|
||||
|
||||
require github.com/go-audio/riff v1.0.0 // indirect
|
||||
|
|
12
go.sum
12
go.sum
|
@ -1,20 +1,12 @@
|
|||
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
|
||||
github.com/go-audio/audio v1.0.0 h1:zS9vebldgbQqktK4H0lUqWrG8P0NxCJVqcj7ZpNnwd4=
|
||||
github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs=
|
||||
github.com/go-audio/riff v1.0.0 h1:d8iCGbDvox9BfLagY94fBynxSPHO80LmZCaOsmKxokA=
|
||||
github.com/go-audio/riff v1.0.0/go.mod h1:l3cQwc85y79NQFCRB7TiPoNiaijp6q8Z0Uv38rVG498=
|
||||
github.com/go-audio/wav v1.1.0 h1:jQgLtbqBzY7G+BM8fXF7AHUk1uHUviWS4X39d5rsL2g=
|
||||
github.com/go-audio/wav v1.1.0/go.mod h1:mpe9qfwbScEbkd8uybLuIpTgHyrISw/OTuvjUW2iGtE=
|
||||
github.com/icza/bitio v1.1.0 h1:ysX4vtldjdi3Ygai5m1cWy4oLkhWTAi+SyO6HC8L9T0=
|
||||
github.com/icza/bitio v1.1.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A=
|
||||
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 h1:8UsGZ2rr2ksmEru6lToqnXgA8Mz1DP11X4zSJ159C3k=
|
||||
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA=
|
||||
github.com/jszwec/csvutil v1.5.1/go.mod h1:Rpu7Uu9giO9subDyMCIQfHVDuLrcaC36UA4YcJjGBkg=
|
||||
github.com/mewkiz/pkg v0.0.0-20211102230744-16a6ce8f1b77 h1:DDyKVkTkrFmd9lR84QW3EIfkkoHlurlpgW+DYuAUJn8=
|
||||
github.com/mewkiz/pkg v0.0.0-20211102230744-16a6ce8f1b77/go.mod h1:J/rDzvIiwiVpv72OEP8aJFxLXjGpUdviIIeqJPLIctA=
|
||||
github.com/mewkiz/pkg v0.0.0-20220820102221-bbbca16e2a6c h1:6AzCfQNCql3Of8ee1JY6dufssFnBWJYuCVrGcES84AA=
|
||||
github.com/mewkiz/pkg v0.0.0-20220820102221-bbbca16e2a6c/go.mod h1:J/rDzvIiwiVpv72OEP8aJFxLXjGpUdviIIeqJPLIctA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
|
Loading…
Reference in a new issue