diff options
author | Christian Pointner <equinox@helsinki.at> | 2016-07-13 16:31:41 (GMT) |
---|---|---|
committer | Christian Pointner <equinox@helsinki.at> | 2016-07-13 16:31:41 (GMT) |
commit | 54ce457a8e5d7cd41e2944348250c4399bc5fdf0 (patch) | |
tree | ea1cc4d5078ff132ddbe1ffd27af65b7c6479eb3 /rhimport | |
parent | f3da623a4c6cade4d9a16aa66db02da092a6c429 (diff) |
using bs1770gain to analyze loudness works now
Diffstat (limited to 'rhimport')
-rw-r--r-- | rhimport/bs1770_responses.go | 89 | ||||
-rw-r--r-- | rhimport/converter.go | 69 |
2 files changed, 144 insertions, 14 deletions
diff --git a/rhimport/bs1770_responses.go b/rhimport/bs1770_responses.go new file mode 100644 index 0000000..b254b9c --- /dev/null +++ b/rhimport/bs1770_responses.go @@ -0,0 +1,89 @@ +// +// rhimportd +// +// The Radio Helsinki Rivendell Import Daemon +// +// +// Copyright (C) 2015-2016 Christian Pointner <equinox@helsinki.at> +// +// 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 <http://www.gnu.org/licenses/>. +// + +package rhimport + +import ( + "encoding/xml" + "fmt" + "io" +) + +type BS1770Result struct { + Album BS1770ResultAlbum `xml:"album"` +} + +type BS1770ResultAlbum struct { + Tracks []BS1770ResultTrack `xml:"track"` + Summary BS1770ResultSummary `xml:"summary"` +} + +type BS1770ResultTrack struct { + Total uint `xml:"total,attr"` + Number uint `xml:"number,attr"` + File string `xml:"file,attr"` + Integrated BS1770ResultValueLUFS `xml:"integrated"` + Momentary BS1770ResultValueLUFS `xml:"momentary"` + ShorttermMaximum BS1770ResultValueLUFS `xml:"shortterm-maximum"` + SamplePeak BS1770ResultValueSPFS `xml:"sample-peak"` + TruePeak BS1770ResultValueTPFS `xml:"true-peak"` +} + +type BS1770ResultSummary struct { + Total uint `xml:"total,attr"` + Integrated BS1770ResultValueLUFS `xml:"integrated"` + Momentary BS1770ResultValueLUFS `xml:"momentary"` + ShorttermMaximum BS1770ResultValueLUFS `xml:"shortterm-maximum"` + SamplePeak BS1770ResultValueSPFS `xml:"sample-peak"` + TruePeak BS1770ResultValueTPFS `xml:"true-peak"` +} + +type BS1770ResultValueLUFS struct { + LUFS float64 `xml:"lufs,attr"` + LU float64 `xml:"lu,attr"` +} + +type BS1770ResultValueRange struct { + LUFS float64 `xml:"lufs,attr"` +} + +type BS1770ResultValueSPFS struct { + SPFS float64 `xml:"spfs,attr"` + Factor float64 `xml:"factor,attr"` +} + +type BS1770ResultValueTPFS struct { + TPFS float64 `xml:"tpfs,attr"` + Factor float64 `xml:"factor,attr"` +} + +func NewBS1770ResultFromXML(data io.Reader) (res *BS1770Result, err error) { + decoder := xml.NewDecoder(data) + res = &BS1770Result{} + if xmlerr := decoder.Decode(res); xmlerr != nil { + err = fmt.Errorf("Error parsing XML response: %s", xmlerr) + return + } + return +} diff --git a/rhimport/converter.go b/rhimport/converter.go index 82e7f8b..c809c3c 100644 --- a/rhimport/converter.go +++ b/rhimport/converter.go @@ -25,6 +25,7 @@ package rhimport import ( + "bytes" "errors" "fmt" "io" @@ -141,32 +142,62 @@ func (ff *FFMpegFetchConverter) GetResult() (result string, err error, loudnessC // type FFMpegBS1770FetchConverter struct { - ffmpeg *exec.Cmd - bs1770 *exec.Cmd - pipe io.WriteCloser - result chan ConverterResult + ffmpeg *exec.Cmd + bs1770 *exec.Cmd + pipe io.WriteCloser + resultFF chan ConverterResult + resultBS chan ConverterResult } func NewFFMpegBS1770FetchConverter(filename string, metadata map[string]string) (ff *FFMpegBS1770FetchConverter, filenameFlac string, err error) { ff = &FFMpegBS1770FetchConverter{} ext := filepath.Ext(filename) filenameFlac = strings.TrimSuffix(filename, ext) + ".flac" - rhl.Printf("ffmpeg-converter: starting ffmpeg for file '%s' (had extension: '%s')", filenameFlac, ext) - ff.ffmpeg = exec.Command("ffmpeg", "-loglevel", "warning", "-i", "-", "-map_metadata", "0") + rhl.Printf("ffmpeg-bs1770-converter: starting ffmpeg for file '%s' (had extension: '%s')", filenameFlac, ext) + ff.ffmpeg = exec.Command("ffmpeg", "-loglevel", "warning", "-i", "pipe:0", "-map_metadata", "0") if metadata != nil { for key, value := range metadata { ff.ffmpeg.Args = append(ff.ffmpeg.Args, "-metadata", fmt.Sprintf("%s=%s", key, value)) } } - ff.ffmpeg.Args = append(ff.ffmpeg.Args, "-f", "flac", filenameFlac) + ff.ffmpeg.Args = append(ff.ffmpeg.Args, "-f", "flac", filenameFlac, "-f", "flac", "pipe:1") if ff.pipe, err = ff.ffmpeg.StdinPipe(); err != nil { return nil, "", err } + var ffStderr bytes.Buffer + ff.ffmpeg.Stderr = &ffStderr - ff.result = make(chan ConverterResult, 1) + ff.bs1770 = exec.Command("bs1770gain", "--ebu", "-i", "--xml", "-") + var ffstdout io.WriteCloser + if ffstdout, err = ff.bs1770.StdinPipe(); err != nil { + return nil, "", err + } + ff.ffmpeg.Stdout = ffstdout + + var bsStdout, bsStderr bytes.Buffer + ff.bs1770.Stdout = &bsStdout + ff.bs1770.Stderr = &bsStderr + + ff.resultFF = make(chan ConverterResult, 1) + ff.resultBS = make(chan ConverterResult, 1) go func() { - output, err := ff.ffmpeg.CombinedOutput() - ff.result <- ConverterResult{strings.TrimSpace(string(output)), err, 0.0} + err := ff.ffmpeg.Run() + ffstdout.Close() + ff.resultFF <- ConverterResult{strings.TrimSpace(string(ffStderr.String())), err, 0.0} + }() + go func() { + if err := ff.bs1770.Run(); err != nil { + ff.resultBS <- ConverterResult{strings.TrimSpace(string(bsStderr.String())), err, 0.0} + } + + res, err := NewBS1770ResultFromXML(&bsStdout) + if err != nil { + ff.resultBS <- ConverterResult{bsStdout.String(), err, 0.0} + } + if len(res.Album.Tracks) == 0 { + ff.resultBS <- ConverterResult{bsStdout.String(), fmt.Errorf("bs1770gain returned no/invalid result"), 0.0} + } + ff.resultBS <- ConverterResult{"", nil, res.Album.Tracks[0].Integrated.LU} }() return } @@ -180,9 +211,19 @@ func (ff *FFMpegBS1770FetchConverter) Close() (err error) { } func (ff *FFMpegBS1770FetchConverter) GetResult() (result string, err error, loudnessCorr float64) { - if ff.result != nil { - r := <-ff.result - return r.output, r.err, r.loudnessCorr + var rff, rbs ConverterResult + if ff.resultFF != nil { + rff = <-ff.resultFF } - return "", nil, 0.0 + if ff.resultBS != nil { + rbs = <-ff.resultBS + } + if rff.err != nil { + return rff.output, fmt.Errorf("ffmpeg: %v", rff.err), 0.0 + } + if rbs.err != nil { + return rbs.output, fmt.Errorf("bs1770gain: %v", rbs.err), 0.0 + } + + return "", nil, rbs.loudnessCorr } |