1 /***************************************************************************
2  *   Copyright (C) 2013-2021 by Ilya Kotov                                 *
3  *   forkotov02@ya.ru                                                      *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20 
21 #include <QStringList>
22 #include <math.h>
23 #include <qmmp/inputsourcefactory.h>
24 #include <qmmp/decoderfactory.h>
25 #include <qmmp/audioconverter.h>
26 #include "rgscanner.h"
27 
28 #define BUFFER_FRAMES 4096
29 
RGScanner()30 RGScanner::RGScanner()
31 {
32     m_gain = 0.;
33     m_peak = 0.;
34     m_user_stop = false;
35     m_is_running = false;
36     m_is_pending = false;
37     m_has_values = false;
38     m_handle = nullptr;
39     m_decoder = nullptr;
40     m_source = nullptr;
41 }
42 
~RGScanner()43 RGScanner::~RGScanner()
44 {
45     stop();
46     deinit();
47     if(m_handle)
48     {
49         DeinitGainAnalysis(m_handle);
50         m_handle = nullptr;
51     }
52 }
53 
prepare(const QString & url)54 bool RGScanner::prepare(const QString &url)
55 {
56     m_is_pending = false;
57     deinit();
58     m_url = url;
59     QString name = m_url.section("/", -1);
60     InputSource *source = InputSource::create(url, nullptr);
61     if(!source->initialize())
62     {
63         delete source;
64         qWarning("RGScanner: Invalid url");
65         return false;
66     }
67 
68     if(source->ioDevice() && !source->ioDevice()->open(QIODevice::ReadOnly))
69     {
70         qWarning("RGScanner: [%s] unable to open input stream, error: %s",
71                  qPrintable(name),
72                  qPrintable(source->ioDevice()->errorString()));
73         delete source;
74         return false;
75     }
76 
77     DecoderFactory *factory = Decoder::findByFilePath(source->path());
78 
79     if(!factory)
80     {
81         qWarning("RGScanner: [%s] unable to find factory", qPrintable(name));
82         delete source;
83         return false;
84     }
85     qDebug("RGScanner: [%s] selected decoder: %s",qPrintable(name),
86            qPrintable(factory->properties().shortName));
87 
88     if(factory->properties().noInput && source->ioDevice())
89         source->ioDevice()->close();
90 
91     Decoder *decoder = factory->create(source->path(), source->ioDevice());
92     if(!decoder->initialize())
93     {
94         qWarning("RGScanner: [%s] invalid file format", qPrintable(name));
95         delete source;
96         delete decoder;
97         return false;
98     }
99     if(decoder->audioParameters().channels() > 2)
100     {
101         qWarning("RGScanner: [%s] unsupported channel number: %d",
102                  qPrintable(name),
103                  decoder->audioParameters().channels());
104         delete source;
105         delete decoder;
106         return false;
107     }
108     m_decoder = decoder;
109     m_source = source;
110     m_user_stop = false;
111     m_has_values = false;
112     m_is_pending = true;
113     return true;
114 }
115 
stop()116 void RGScanner::stop()
117 {
118     m_mutex.lock();
119     m_user_stop = true;
120     m_mutex.unlock();
121 }
122 
isRunning() const123 bool RGScanner::isRunning() const
124 {
125     return m_is_running;
126 }
127 
isPending() const128 bool RGScanner::isPending() const
129 {
130     return m_is_pending;
131 }
132 
hasValues() const133 bool RGScanner::hasValues() const
134 {
135     return m_has_values;
136 }
137 
oldReplayGainInfo() const138 QMap<Qmmp::ReplayGainKey, double> RGScanner::oldReplayGainInfo() const
139 {
140     if(!m_decoder)
141         return QMap<Qmmp::ReplayGainKey, double>();
142 
143     return m_decoder->replayGainInfo();
144 }
145 
gain() const146 double RGScanner::gain() const
147 {
148     return m_gain;
149 }
150 
peak() const151 double RGScanner::peak() const
152 {
153     return m_peak;
154 }
155 
url() const156 QString RGScanner::url() const
157 {
158     return m_url;
159 }
160 
handle()161 GainHandle_t *RGScanner::handle()
162 {
163     return m_handle;
164 }
165 
run()166 void RGScanner::run()
167 {
168     if(m_user_stop)
169     {
170         m_is_pending = false;
171         return;
172     }
173     QString name = m_url.section("/", -1);
174     qDebug("RGScanner: [%s] staring thread", qPrintable(name));
175     m_is_running = true;
176     m_is_pending = false;
177     bool error = false;
178 
179     AudioParameters ap = m_decoder->audioParameters();
180     AudioConverter converter;
181     converter.configure(ap.format());
182     //buffers
183     double out_left[BUFFER_FRAMES] = {0}, out_right[BUFFER_FRAMES] = {0}; //replay gain buffers
184     float float_buf[BUFFER_FRAMES*ap.channels()]; //float buffer
185     qint64 buf_size = BUFFER_FRAMES*ap.frameSize();
186     unsigned char char_buf[buf_size]; //char buffer
187 
188 
189     //counters
190     qint64 totalSamples = m_decoder->totalTime() * ap.sampleRate() * ap.channels() / 1000, len = 0;
191     quint64 sample_counter = 0;
192     quint64 samples = 0;
193     double max = 0;
194 
195     if(m_handle)
196         DeinitGainAnalysis(m_handle);
197     InitGainAnalysis(&m_handle, ap.sampleRate());
198 
199     forever
200     {
201         len = m_decoder->read(char_buf, buf_size);
202 
203         if(len < 0)
204         {
205             error = true;
206             break;
207         }
208         else if(len == 0)
209             break;
210 
211         samples = len / ap.sampleSize();
212 
213         converter.toFloat(char_buf, float_buf, samples);
214 
215         if(ap.channels() == 2)
216         {
217             for(uint i = 0; i < (samples >> 1); ++i)
218             {
219                 out_left[i] = float_buf[i*2]*32768.0;
220                 out_right[i] = float_buf[i*2+1]*32768.0;
221                 max = qMax(fabs(out_left[i]), max);
222                 max = qMax(fabs(out_right[i]), max);
223             }
224         }
225         else if(ap.channels() == 1)
226         {
227             for(uint i = 0; i < samples; ++i)
228             {
229                 out_left[i] = float_buf[i]*32768.0;
230                 max = qMax(fabs(out_left[i]), max);
231             }
232         }
233 
234         size_t samples_per_channel = samples >> ((ap.channels() == 2) ? 1 : 0);
235 
236         AnalyzeSamples(m_handle, out_left, out_right, samples_per_channel, ap.channels());
237         sample_counter += samples;
238         emit progress(100 * sample_counter / totalSamples);
239 
240         m_mutex.lock();
241         if(m_user_stop)
242         {
243             m_mutex.unlock();
244             break;
245         }
246         m_mutex.unlock();
247     }
248 
249     if(error)
250     {
251         qWarning("RGScanner: [%s] finished with error", qPrintable(name));
252     }
253     else if(m_user_stop)
254     {
255         qDebug("RGScanner: [%s] stopped by user", qPrintable(name));
256     }
257     else
258     {
259         m_gain = GetTitleGain(m_handle);
260         m_peak = max/32768.0;
261         emit progress(100);
262         qDebug("RGScanner: [%s] peak=%f, gain=%f", qPrintable(name), m_peak, m_gain);
263         qDebug("RGScanner: [%s] finished with success ", qPrintable(name));
264         m_has_values = true;
265     }
266     deinit();
267     m_is_running = false;
268     emit finished(m_url);
269 }
270 
deinit()271 void RGScanner::deinit()
272 {
273     if(m_decoder)
274     {
275         delete m_decoder;
276         m_decoder = nullptr;
277     }
278     if(m_source)
279     {
280         delete m_source;
281         m_source = nullptr;
282     }
283 }
284