// // 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" "net/http" "os" "os/exec" "path/filepath" "strings" ) type ffmpegResult struct { output string err error } func runNormalizer(ctx *Context, res *Result, src *os.File, size int64) (err error) { ctx.reportProgress(2, "normalizing", 0.0, float64(size)) basepath, filename := filepath.Split(src.Name()) ext := filepath.Ext(filename) destName := strings.TrimSuffix(filename, ext) + "_normalized.flac" ctx.stdlog.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 { if ctx.isCanceled() { ffmpeg.Process.Kill() res.ResponseCode = http.StatusNoContent res.ErrorString = "canceled" return nil } var w int64 w, err = io.CopyN(ffstdin, src, 512*1024) if err != nil { break } written += w ctx.reportProgress(2, "normalizing", float64(written), float64(size)) } ffstdin.Close() r := <-ffresult if r.err != nil { return fmt.Errorf("normalizer exited with error: %q, ffmpeg output: %s", r.err, r.output) } if err != nil && err != io.EOF { return } ctx.reportProgress(2, "normalizing", float64(size), float64(size)) return nil } func NormalizeFile(ctx *Context) (res *Result, err error) { res = &Result{ResponseCode: http.StatusOK} if ctx.LoudnessCorr == 0.0 { ctx.stdlog.Println("NormalizeFile: skipping normalization since the gain = 0.0dB") ctx.reportProgress(2, "normalizing", 1.0, 1.0) return } 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 res, err } else { size = info.Size() } if err = runNormalizer(ctx, res, src, size); err != nil { ctx.stdlog.Println("NormalizeFile error:", err) return } return }