// // rhimportd // // The Radio Helsinki Rivendell Import Daemon // // // Copyright (C) 2015-2016 Christian Pointner // // This file is part of rhimportd. // // rhimportd is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // any later version. // // rhimportd 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 // along with rhimportd. If not, see . // package rhimport import ( "fmt" "io" "os" "os/exec" "path" "path/filepath" "strings" ) type ffmpegResult struct { output string err error } func runNormalizer(ctx *Context, src *os.File, size int64) (err error) { if ctx.DeleteSourceFile { defer os.Remove(src.Name()) } basepath, filename := filepath.Split(src.Name()) ext := filepath.Ext(filename) destName := strings.TrimSuffix(filename, ext) + "_normalized.flac" rhl.Printf("NormalizeFile: '%s' -> '%s', using gain = %.2f dB", filename, destName, ctx.LoudnessCorr) ctx.SourceFile = basepath + destName ffmpeg := exec.Command("ffmpeg", "-loglevel", "warning", "-i", "-", "-filter:a", fmt.Sprintf("volume=%fdB", ctx.LoudnessCorr), "-map_metadata", "0", "-f", "flac", ctx.SourceFile) var ffstdin io.WriteCloser if ffstdin, err = ffmpeg.StdinPipe(); err != nil { return } ffresult := make(chan ffmpegResult) go func() { output, err := ffmpeg.CombinedOutput() ffresult <- ffmpegResult{strings.TrimSpace(string(output)), err} }() var written int64 for { var w int64 w, err = io.CopyN(ffstdin, src, 512*1024) if err != nil { break } written += w if ctx.ProgressCallBack != nil { if keep := ctx.ProgressCallBack(2, "normalizing", float64(written), float64(size), ctx.ProgressCallBackData); !keep { ctx.ProgressCallBack = nil } } } ffstdin.Close() res := <-ffresult if res.err != nil { return fmt.Errorf("normalizer exited with error: %q, ffmpeg output: %s", res.err, res.output) } if err != nil && err != io.EOF { return } return nil } func NormalizeFile(ctx *Context) (err error) { if ctx.LoudnessCorr == 0.0 { rhl.Println("NormalizeFile: skipping normalization since the gain = 0.0dB") if ctx.ProgressCallBack != nil { if keep := ctx.ProgressCallBack(2, "normalizing", 1.0, 1.0, ctx.ProgressCallBackData); !keep { ctx.ProgressCallBack = nil } } return } ctx.NormalizationLevel = 0 // don't do double normalization var src *os.File if src, err = os.Open(ctx.SourceFile); err != nil { return } defer src.Close() size := int64(0) if info, err := src.Stat(); err != nil { return err } else { size = info.Size() } if ctx.ProgressCallBack != nil { if keep := ctx.ProgressCallBack(2, "normalizing", 0.0, float64(size), ctx.ProgressCallBackData); !keep { ctx.ProgressCallBack = nil } } if err = runNormalizer(ctx, src, size); err != nil { rhl.Println("NormalizeFile error:", err) if ctx.DeleteSourceFile { os.Remove(ctx.SourceFile) if ctx.DeleteSourceDir { os.Remove(path.Dir(ctx.SourceFile)) } } return } if ctx.ProgressCallBack != nil { if keep := ctx.ProgressCallBack(2, "normalizing", float64(size), float64(size), ctx.ProgressCallBackData); !keep { ctx.ProgressCallBack = nil } } return }