1 /*
2  * Cantata
3  *
4  * Copyright (c) 2011-2020 Craig Drummond <craig.p.drummond@gmail.com>
5  *
6  * ----
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; see the file COPYING.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 #include "replaygain.h"
25 #include "jobcontroller.h"
26 #include <QCoreApplication>
27 #include <stdio.h>
28 
29 // Work-around possible locale issues by forcing usage of '.' as separator
formatDouble(double d)30 static QString formatDouble(double d)
31 {
32     return QString::number(d, 'f', 10).replace(",", ".");
33 }
34 
ReplayGain(const QStringList & fileNames)35 ReplayGain::ReplayGain(const QStringList &fileNames)
36     : QObject(0)
37     , files(fileNames)
38     , lastProgress(-1)
39     , totalScanned(0)
40 {
41     TrackScanner::init();
42     JobController::self()->setMaxActive(8);
43 }
44 
~ReplayGain()45 ReplayGain::~ReplayGain()
46 {
47     clearScanners();
48 }
49 
scan()50 void ReplayGain::scan()
51 {
52     for (int i=0; i<files.count(); ++i) {
53         if (scanners.count()<100) {
54             createScanner(i);
55         } else {
56             toScan.append(i);
57         }
58     }
59 }
60 
createScanner(int index)61 void ReplayGain::createScanner(int index)
62 {
63     TrackScanner *s=new TrackScanner(index);
64     s->setFile(files.at(index));
65     connect(s, SIGNAL(progress(int)), this, SLOT(scannerProgress(int)));
66     connect(s, SIGNAL(done()), this, SLOT(scannerDone()));
67     scanners.insert(index, s);
68     JobController::self()->add(s);
69 }
70 
clearScanners()71 void ReplayGain::clearScanners()
72 {
73     JobController::self()->cancel();
74     QMap<int, TrackScanner *>::ConstIterator it(scanners.constBegin());
75     QMap<int, TrackScanner *>::ConstIterator end(scanners.constEnd());
76 
77     for (; it!=end; ++it) {
78         it.value()->stop();
79     }
80     scanners.clear();
81     toScan.clear();
82     tracks.clear();
83 }
84 
showProgress()85 void ReplayGain::showProgress()
86 {
87     int finished=0;
88     quint64 totalProgress=0;
89     QMap<int, Track>::iterator it=tracks.begin();
90     QMap<int, Track>::iterator end=tracks.end();
91 
92     for (; it!=end; ++it) {
93         if ((*it).finished) {
94             finished++;
95         }
96         totalProgress+=(*it).finished ? 100 : (*it).progress;
97     }
98     int progress=(totalProgress/files.count())+0.5;
99     progress=(progress/5)*5;
100     if (progress!=lastProgress) {
101         lastProgress=progress;
102         printf("PROGRESS: %02d\n", lastProgress);
103         fflush(stdout);
104     }
105 }
106 
showResults()107 void ReplayGain::showResults()
108 {
109     QList<TrackScanner *> okScanners;
110     for (int i=0; i<files.count(); ++i) {
111         TrackScanner *s=scanners[i];
112         const Track &t=tracks[i];
113         if (t.success && s->ok()) {
114             printf("TRACK: %d %s %s\n", i, formatDouble(TrackScanner::reference(s->results().loudness)).toLatin1().constData(),
115                                            formatDouble(s->results().peakValue()).toLatin1().constData());
116             okScanners.append(s);
117         } else {
118             printf("TRACK: %d FAILED\n", i);
119         }
120     }
121 
122     if (okScanners.isEmpty()) {
123         printf("ALBUM: FAILED\n");
124     } else {
125         TrackScanner::Data album=TrackScanner::global(okScanners);
126         printf("ALBUM: %s %s\n", formatDouble(TrackScanner::reference(album.loudness)).toLatin1().constData(),
127                                  formatDouble(album.peak).toLatin1().constData());
128     }
129     fflush(stdout);
130 
131     QCoreApplication::exit(0);
132 }
133 
scannerProgress(int p)134 void ReplayGain::scannerProgress(int p)
135 {
136     TrackScanner *s=qobject_cast<TrackScanner *>(sender());
137     if (!s) {
138         return;
139     }
140 
141     tracks[s->index()].progress=p;
142     showProgress();
143 }
144 
scannerDone()145 void ReplayGain::scannerDone()
146 {
147     TrackScanner *s=qobject_cast<TrackScanner *>(sender());
148     if (!s) {
149         return;
150     }
151     Track &track=tracks[s->index()];
152     if (!track.finished) {
153         track.finished=true;
154         track.success=s->success();
155         track.progress=100;
156         showProgress();
157         totalScanned++;
158     }
159 
160     if (toScan.isEmpty()) {
161         if (totalScanned==files.count()) {
162             showResults();
163         }
164     } else {
165         int index=toScan.takeAt(0);
166         createScanner(index);
167     }
168 }
169 
170 #include "moc_replaygain.cpp"
171