Skip to content

Commit

Permalink
Added -flac argument
Browse files Browse the repository at this point in the history
- Added `-flac` argument to allow extracting flac stream
  • Loading branch information
ScriptTiger committed Feb 11, 2024
1 parent 063b3d5 commit 9555eb0
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 30 deletions.
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -6,10 +6,11 @@ FLACSFX is a minimal FLAC-to-WAV transcoder to transcode an embedded FLAC file t
Usage: `flacsfx [options...]`
Argument | Description
--------------------------|-----------------------------------------------------------------------------------------------------
`-flac` | Output FLAC
`-o <file>` | Destination file
`-info` | Show stream info

`-` can be used in place of `<file>` to designate standard output as the destination, allowing you to quickly pipe the WAV data to a compatible application, such as VLC, without having to extract it to an actual file. For piping to FFmpeg, you must skip the first 44 bytes of the header and use input parameters to define the audio stream (i.e. to define a stereo audio stream of 24-bit depth and 48 kHz sample rate in order to convert it to AAC and save it as an M4A, you would use the following: `MyFlaxSFX -o - | ffmpeg -skip_initial_bytes 44 -f s24le -ar 48k -ac 2 -i - out.m4a`). Alternatively, with FFmpeg, you could also use the `-skip_initial_bytes` and `-f flac` input parameters with the FLACSFX executable as the input in order to access the FLAC audio stream directly, inclduing the FLAC header, so that no further input parameters are needed.
`-` can be used in place of `<file>` to designate standard output as the destination. When piping a FLAC stream, the complete header is included. However, when piping a WAV stream, the header is unfinished until the write operation is complete, meaning you may have to skip the first 44 bytes in order for the receiving application to process the stream in real time.

Without any arguments, the embedded FLAC data will be transcoded into the working directory to a WAV file of the same name as the executable, except with the `.wav` extension. So, command-line usage is only optional and the end user can just execute the application as they would any other application for this default behavior.

Expand Down
94 changes: 65 additions & 29 deletions flacsfx.go
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"io"
"os"
"path/filepath"
"strconv"
"strings"

Expand All @@ -16,6 +17,7 @@ import (
func help(err int) {
os.Stdout.WriteString(
"Usage: flacsfx [options...]\n"+
" -flac Output FLAC\n"+
" -o <file> Destination file\n"+
" -info Show stream info",
)
Expand Down Expand Up @@ -52,18 +54,35 @@ func main() {
help(-1)
}

// Initialize misc variables
var (
flacStream *flac.Stream
err error
)

// Initialize uninitialized flags
var info bool
var (
flacenc bool
orw bool
info bool
)

// Push arguments to flag pointers
for i := 1; i < len(os.Args); i++ {
if strings.HasPrefix(os.Args[i], "-") {
switch strings.TrimPrefix(os.Args[i], "-") {
case "flac":
if flacenc {help(-1)}
flacenc = true
continue
case "o":
if orw {help(-1)}
i++
wavName = os.Args[i]
orw = true
continue
case "info":
if info {help(-1)}
info = true
continue
default:
Expand All @@ -72,48 +91,65 @@ func main() {
} else {help(-1)}
}

// Parse FLAC byte stream
flacStream, err := flac.New(bytes.NewReader(flacRaw))
if err != nil {
os.Stdout.WriteString("There was a problem parsing the FLAC stream.")
os.Exit(1)
if !flacenc {
// Parse FLAC byte stream
flacStream, err = flac.New(bytes.NewReader(flacRaw))
if err != nil {
os.Stdout.WriteString("There was a problem parsing the FLAC stream.")
os.Exit(1)
}
defer flacStream.Close()

// Display stream info and exit
if info {
os.Stdout.WriteString(
"codec_name=flac\n"+
"codec_long_name=FLAC (Free Lossless Audio Codec)\n"+
"codec_type=audio\n"+
"sample_rate="+strconv.FormatUint(uint64(flacStream.Info.SampleRate), 10)+"\n"+
"channels="+strconv.Itoa(int(flacStream.Info.NChannels))+"\n"+
"channel_layout="+layoutLookup(flacStream.Info.NChannels)+"\n"+
"time_base=1/"+strconv.FormatUint(uint64(flacStream.Info.SampleRate), 10)+"\n"+
"duration_ts="+strconv.FormatUint(uint64(flacStream.Info.NSamples), 10)+"\n"+
"duration="+strconv.FormatFloat(float64(flacStream.Info.NSamples)/float64(flacStream.Info.SampleRate), 'f', -1, 64)+"\n"+
"bits_per_raw_sample="+strconv.Itoa(int(flacStream.Info.BitsPerSample)),
)
os.Exit(0)
}
}
defer flacStream.Close()

// Display stream info and exit
if info {
os.Stdout.WriteString(
"codec_name=flac\n"+
"codec_long_name=FLAC (Free Lossless Audio Codec)\n"+
"codec_type=audio\n"+
"sample_rate="+strconv.FormatUint(uint64(flacStream.Info.SampleRate), 10)+"\n"+
"channels="+strconv.Itoa(int(flacStream.Info.NChannels))+"\n"+
"channel_layout="+layoutLookup(flacStream.Info.NChannels)+"\n"+
"time_base=1/"+strconv.FormatUint(uint64(flacStream.Info.SampleRate), 10)+"\n"+
"duration_ts="+strconv.FormatUint(uint64(flacStream.Info.NSamples), 10)+"\n"+
"duration="+strconv.FormatFloat(float64(flacStream.Info.NSamples)/float64(flacStream.Info.SampleRate), 'f', -1, 64)+"\n"+
"bits_per_raw_sample="+strconv.Itoa(int(flacStream.Info.BitsPerSample)),
)
os.Exit(0)

// Rewrite file name if needed
if flacenc && !orw {
wavName = strings.TrimSuffix(wavName, filepath.Ext(wavName))+".flac"
}

// Initialize WAV writer
var wavWriter *os.File
// Initialize file writer
var fileWriter *os.File
if wavName == "-" {
wavWriter = os.Stdout
fileWriter = os.Stdout
} else {
wavWriter, err = os.Create(wavName)
fileWriter, err = os.Create(wavName)
if err != nil {
os.Stdout.WriteString("There was a problem creating the new WAV file.")
os.Exit(2)
}
os.Stdout.WriteString("Extracting \""+wavName+"\"...")
}
defer wavWriter.Close()
defer fileWriter.Close()

if flacenc {
// If flac requested, write to file and exit
_, err = fileWriter.Write(flacRaw)
if err != nil {
os.Stdout.WriteString("There was a problem writing the FLAC stream to the file.")
os.Exit(1)
}
os.Exit(0)
}

// Initialize WAV encoder
wavEncoder := wav.NewEncoder(
wavWriter,
fileWriter,
int(flacStream.Info.SampleRate),
int(flacStream.Info.BitsPerSample),
int(flacStream.Info.NChannels),
Expand Down

0 comments on commit 9555eb0

Please sign in to comment.