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