1
/***************************************************************************
2
* Copyright (C) 2005 - 2007 by *
3
* Last.fm Ltd <client@last.fm> *
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. *
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. *
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 Steet, Fifth Floor, Boston, MA 02110-1301, USA. *
19
***************************************************************************/
21
#include "Fingerprinter2.h"
23
#include "interfaces/TranscodeInterface.h"
25
#include "Sha256File.h"
27
#include "MP3_Source_Qt.h"
34
using namespace fingerprint;
36
static const uint k_bufferSize = 1024 * 8;
38
Fingerprinter2::Fingerprinter2( QObject* parent ) :
43
connect( this, SIGNAL( finished() ), SLOT( onThreadFinished() ) );
48
void Fingerprinter2::onThreadFinished()
50
emit threadFinished( this );
53
void Fingerprinter2::reset()
55
m_fingerprint = QByteArray();
56
m_track = TrackInfo();
62
bool Fingerprinter2::isFree()
64
return !isRunning() && m_reset;
68
void Fingerprinter2::startFullFingerprint()
75
void Fingerprinter2::startQueryFingerprint()
82
void Fingerprinter2::stop()
88
void Fingerprinter2::start()
97
Q_ASSERT( !"Error: Fingerprinter2-thread cannot start since it is not free. Always check isFree() first!" );
101
void Fingerprinter2::run()
103
//qDebug() << "Fingerprinting thread started.";
104
fingerprint(m_track.path());
107
QString Fingerprinter2::sha256()
109
QMutexLocker locker( &m_trackMutex );
110
unsigned char hash[SHA256_HASH_SIZE];
112
Sha256File::getHash( m_track.path().toStdString(), hash );
114
for (int i = 0; i < SHA256_HASH_SIZE; ++i) {
115
QString hex = QString("%1").arg(uchar(hash[i]), 2, 16,
125
void Fingerprinter2::fingerprintOld( QString filename )
127
//int time = QDateTime::currentDateTime().toTime_t();
129
QString fileExt = QFileInfo( filename ).suffix();
130
TranscodeInterface* transcoder = TranscodeInterface::getDecoder( fileExt );
132
// For all failures in this class, the caller will have to check whether no
133
// fingerprint data was produced and deduce failure.
134
if ( transcoder == 0 )
136
qDebug() << "Failed to find transcoder for extension:" << fileExt;
140
TranscodeInterface* transcoder2 = transcoder->newInstance();
141
if ( transcoder2 == 0 )
143
qDebug() << "Failed to create transcoder2";
147
QFile inFile( filename );
148
bool fine = inFile.open( QIODevice::ReadOnly );
151
qDebug() << "Failed to open file:" << filename;
152
transcoder->deleteInstance( transcoder2 );
156
//QFile outFile( MooseUtils::savePath( "out.wav" ) );
157
//outFile.open( QIODevice::WriteOnly | QIODevice::Unbuffered );
162
// We can't start fingerprinting until the transcoder has called back
163
// with the correct samplerate and channel count
164
connect( transcoder2, SIGNAL( streamInitialized( long, int ) ),
165
this, SLOT( onStreamInitialized( long, int ) ), Qt::DirectConnection );
167
qDebug() << "--- Starting FP for: " << filename;
171
// Decode until the decoder sends us the streamInitialized signal letting
172
// us know what the sample rate is
173
while ( m_sampleRate == -1 && fine && !inFile.atEnd() )
175
fine = decode( inFile, transcoder2, false );
177
qDebug() << "fp decode loop " << inFile.pos();
180
if ( inFile.atEnd() )
183
qDebug() << "Caught file that never returned samplerate: " << filename;
192
if ( mode() == Full )
194
m_extractor.initForFullSubmit( m_sampleRate, m_numChannels );
199
m_extractor.initForQuery( m_sampleRate, m_numChannels );
201
// Skippety skip for as long as the skipper sez (optimisation)
203
float secsToSkip = m_extractor.getToSkipMs() / 1000.0f;
204
bytesToSkip = ( secsToSkip * m_sampleRate * m_numChannels * 2 ) - // 1 sample = 2 bytes
205
( 10 * k_bufferSize ); // safety
207
qDebug() << "Will skip " << bytesToSkip << " bytes";
210
bool skipping = bytesRead < bytesToSkip;
211
while ( !fpDone && !inFile.atEnd() )
213
if ( m_abort ) break;
215
fine = decode( inFile, transcoder2, skipping );
221
while ( transcoder2->hasData() && !fpDone )
223
transcoder2->data( data, k_bufferSize );
225
bytesRead += data.size();
226
skipping = bytesRead < bytesToSkip;
228
//qDebug() << "bytes read: " << bytesRead << ", skip: " << skipping;
229
//qDebug() << "data start address: " << (void*)data.data();
230
//qDebug() << "data end address: " << (void*)(data.data() + data.size());
232
fpDone = m_extractor.process(
233
reinterpret_cast<const short*>( data.data() ),
234
data.size() / sizeof( short ),
235
inFile.atEnd() && !transcoder2->hasData() );
237
//outFile.write( data );
240
catch ( const std::exception& e )
242
qDebug() << "Fingerprinter failed: " << e.what();
253
std::pair<const char*, size_t> fpData = m_extractor.getFingerprint();
254
m_fingerprint = QByteArray::fromRawData( fpData.first, fpData.second );
258
qDebug() << "FingerprintExtractor::process never returned true, fingerprint not calculated";
259
m_fingerprint.clear();
263
transcoder->deleteInstance( transcoder2 );
267
void Fingerprinter2::fingerprint( QString filename )
269
//int time = QDateTime::currentDateTime().toTime_t();
271
int duration, samplerate, bitrate, nchannels;
276
MP3_Source::getInfo( filename, duration, samplerate, bitrate, nchannels );
278
m_sampleRate = samplerate;
279
m_numChannels = nchannels;
283
catch ( std::exception& e )
285
qDebug() << "Failed to read file: " << filename;
286
qDebug() << "MP3_Source error: " << e.what();
294
if ( mode() == Full )
296
qDebug() << "*** Starting full FP for: " << filename;
297
m_extractor.initForFullSubmit( m_sampleRate, m_numChannels );
301
qDebug() << "--- Starting query FP for: " << filename;
302
m_extractor.initForQuery( m_sampleRate, m_numChannels, duration );
304
// Skippety skip for as long as the skipper sez (optimisation)
305
ms.skip( m_extractor.getToSkipMs() );
306
float secsToSkip = m_extractor.getToSkipMs() / 1000.0f;
307
fpDone = m_extractor.process(
309
static_cast<size_t>( m_sampleRate * m_numChannels * secsToSkip ),
313
const size_t PCMBufSize = 131072;
314
short* pPCMBuffer = new short[PCMBufSize];
321
size_t readData = ms.updateBuffer( pPCMBuffer, PCMBufSize );
327
fpDone = m_extractor.process( pPCMBuffer, readData, ms.eof() );
329
catch ( const std::exception& e )
331
qDebug() << "Fingerprinter failed: " << e.what();
340
std::pair<const char*, size_t> fpData = m_extractor.getFingerprint();
341
m_fingerprint = QByteArray( fpData.first, fpData.second );
345
qDebug() << "FingerprintExtractor::process never returned true, fingerprint not calculated";
346
m_fingerprint.clear();
353
void Fingerprinter2::onStreamInitialized( long sampleRate, int channels )
355
m_sampleRate = sampleRate;
356
m_numChannels = channels;
360
bool Fingerprinter2::decode( QFile& inFile, TranscodeInterface* transcoder2, bool skip )
362
char buffer[k_bufferSize];
364
int numBytes = inFile.read( buffer, k_bufferSize );
365
if ( numBytes == -1 )
367
qDebug() << "Failed to read data from file.";
371
// This avoids making a deep copy into the QByteArray
372
QByteArray ba = QByteArray::fromRawData( buffer, numBytes );
373
bool fine = transcoder2->processData( ba, skip );
376
qDebug() << "The encoder choked on the data in file.";