~ubuntu-branches/ubuntu/lucid/lastfm/lucid

« back to all changes in this revision

Viewing changes to src/libFingerprint/Fingerprinter2.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Pedro Fragoso
  • Date: 2007-12-31 09:49:54 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20071231094954-ix1amvcsj9pk61ya
Tags: 1:1.4.1.57486.dfsg-1ubuntu1
* Merge from Debian unstable (LP: #180254), remaining changes:
  - debian/rules;
    - Added dh_icons
  - Modify Maintainer value to match Debian-Maintainer-Field Spec

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   Copyright (C) 2005 - 2007 by                                          *
 
3
 *      Last.fm Ltd <client@last.fm>                                       *
 
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 Steet, Fifth Floor, Boston, MA  02110-1301, USA.          *
 
19
 ***************************************************************************/
 
20
 
 
21
#include "Fingerprinter2.h"
 
22
 
 
23
#include "interfaces/TranscodeInterface.h"
 
24
 
 
25
#include "Sha256File.h"
 
26
#include "Sha256.h"
 
27
#include "MP3_Source_Qt.h"
 
28
 
 
29
#include <QDebug>
 
30
#include <QFile>
 
31
#include <QDateTime>
 
32
 
 
33
 
 
34
using namespace fingerprint;
 
35
 
 
36
static const uint k_bufferSize = 1024 * 8;
 
37
 
 
38
Fingerprinter2::Fingerprinter2( QObject* parent ) :
 
39
    QThread( parent ),
 
40
    m_abort( false ),
 
41
    m_mode( Query )
 
42
{
 
43
    connect( this, SIGNAL( finished() ), SLOT( onThreadFinished() ) );
 
44
    reset();
 
45
}
 
46
 
 
47
// private slot:
 
48
void Fingerprinter2::onThreadFinished()
 
49
{
 
50
    emit threadFinished( this );
 
51
}
 
52
 
 
53
void Fingerprinter2::reset()
 
54
{
 
55
    m_fingerprint = QByteArray();
 
56
    m_track = TrackInfo();
 
57
    
 
58
    m_abort = false;
 
59
    m_reset = true;
 
60
}
 
61
 
 
62
bool Fingerprinter2::isFree()
 
63
{
 
64
    return !isRunning() && m_reset;
 
65
}
 
66
 
 
67
 
 
68
void Fingerprinter2::startFullFingerprint()
 
69
{
 
70
    m_mode = Full;
 
71
    start();
 
72
}
 
73
 
 
74
 
 
75
void Fingerprinter2::startQueryFingerprint()
 
76
{
 
77
    m_mode = Query;
 
78
    start();
 
79
}
 
80
 
 
81
 
 
82
void Fingerprinter2::stop()
 
83
{
 
84
    m_abort = true;
 
85
}
 
86
 
 
87
 
 
88
void Fingerprinter2::start()
 
89
{
 
90
    if (isFree())
 
91
    {
 
92
        m_reset = false;
 
93
        QThread::start();
 
94
    }
 
95
    else
 
96
    {
 
97
        Q_ASSERT( !"Error: Fingerprinter2-thread cannot start since it is not free. Always check isFree() first!" );
 
98
    }
 
99
}
 
100
 
 
101
void Fingerprinter2::run()
 
102
{
 
103
    //qDebug() << "Fingerprinting thread started.";
 
104
    fingerprint(m_track.path());
 
105
}
 
106
 
 
107
QString Fingerprinter2::sha256()
 
108
{
 
109
    QMutexLocker locker( &m_trackMutex );
 
110
    unsigned char hash[SHA256_HASH_SIZE];
 
111
    QString sha;
 
112
    Sha256File::getHash( m_track.path().toStdString(), hash );
 
113
    
 
114
    for (int i = 0; i < SHA256_HASH_SIZE; ++i) {
 
115
        QString hex = QString("%1").arg(uchar(hash[i]), 2, 16,
 
116
                                        QChar('0'));
 
117
        sha.append(hex);
 
118
    }
 
119
    
 
120
    return sha;
 
121
}
 
122
 
 
123
 
 
124
/*
 
125
void Fingerprinter2::fingerprintOld( QString filename )
 
126
{
 
127
    //int time = QDateTime::currentDateTime().toTime_t();
 
128
    
 
129
    QString fileExt = QFileInfo( filename ).suffix();
 
130
    TranscodeInterface* transcoder = TranscodeInterface::getDecoder( fileExt );
 
131
 
 
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 )
 
135
    {
 
136
        qDebug() << "Failed to find transcoder for extension:" << fileExt;
 
137
        return;
 
138
    }
 
139
    
 
140
    TranscodeInterface* transcoder2 = transcoder->newInstance();
 
141
    if ( transcoder2 == 0 )
 
142
    {
 
143
        qDebug() << "Failed to create transcoder2";
 
144
        return;
 
145
    }
 
146
    
 
147
    QFile inFile( filename );
 
148
    bool fine = inFile.open( QIODevice::ReadOnly );
 
149
    if ( !fine )
 
150
    {
 
151
        qDebug() << "Failed to open file:" << filename;
 
152
        transcoder->deleteInstance( transcoder2 );
 
153
        return;
 
154
    }
 
155
    
 
156
    //QFile outFile( MooseUtils::savePath( "out.wav" ) );
 
157
    //outFile.open( QIODevice::WriteOnly | QIODevice::Unbuffered );
 
158
 
 
159
    m_sampleRate = -1;
 
160
    m_numChannels = -1;
 
161
 
 
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 );
 
166
 
 
167
    qDebug() << "--- Starting FP for: " << filename;
 
168
 
 
169
    bool fpDone = false;
 
170
 
 
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() )
 
174
    {
 
175
        fine = decode( inFile, transcoder2, false );
 
176
 
 
177
        qDebug() << "fp decode loop " << inFile.pos();
 
178
    }
 
179
 
 
180
    if ( inFile.atEnd() )
 
181
    {
 
182
        fine = false;
 
183
        qDebug() << "Caught file that never returned samplerate: " << filename;
 
184
    }
 
185
 
 
186
    if ( fine )
 
187
    {
 
188
        QByteArray data;
 
189
        
 
190
        uint bytesToSkip;
 
191
        uint bytesRead = 0;
 
192
        if ( mode() == Full )
 
193
        {
 
194
            m_extractor.initForFullSubmit( m_sampleRate, m_numChannels );
 
195
            bytesToSkip = 0;    
 
196
        }
 
197
        else
 
198
        {
 
199
            m_extractor.initForQuery( m_sampleRate, m_numChannels );
 
200
 
 
201
            // Skippety skip for as long as the skipper sez (optimisation)
 
202
            //inFile.reset();
 
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
 
206
 
 
207
            qDebug() << "Will skip " << bytesToSkip << " bytes";
 
208
        }
 
209
 
 
210
        bool skipping = bytesRead < bytesToSkip;
 
211
        while ( !fpDone && !inFile.atEnd() )
 
212
        {
 
213
            if ( m_abort ) break;
 
214
 
 
215
            fine = decode( inFile, transcoder2, skipping );
 
216
            if ( !fine )
 
217
                break;
 
218
 
 
219
            try
 
220
            {
 
221
                while ( transcoder2->hasData() && !fpDone )
 
222
                {
 
223
                    transcoder2->data( data, k_bufferSize );
 
224
 
 
225
                    bytesRead += data.size();
 
226
                    skipping = bytesRead < bytesToSkip;
 
227
                    
 
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());
 
231
 
 
232
                    fpDone = m_extractor.process(
 
233
                        reinterpret_cast<const short*>( data.data() ),
 
234
                        data.size() / sizeof( short ),
 
235
                        inFile.atEnd() && !transcoder2->hasData() );
 
236
 
 
237
                    //outFile.write( data );
 
238
                }
 
239
            }
 
240
            catch ( const std::exception& e )
 
241
            {
 
242
                qDebug() << "Fingerprinter failed: " << e.what();
 
243
                break;
 
244
            }
 
245
 
 
246
        } // end outer while
 
247
 
 
248
    } // end if fine
 
249
 
 
250
    if ( fpDone )
 
251
    {
 
252
        // We succeeded
 
253
        std::pair<const char*, size_t> fpData = m_extractor.getFingerprint();
 
254
        m_fingerprint = QByteArray::fromRawData( fpData.first, fpData.second );
 
255
    }
 
256
    else
 
257
    {
 
258
        qDebug() << "FingerprintExtractor::process never returned true, fingerprint not calculated";
 
259
        m_fingerprint.clear();
 
260
    }
 
261
 
 
262
    inFile.close();
 
263
    transcoder->deleteInstance( transcoder2 );
 
264
}
 
265
*/
 
266
 
 
267
void Fingerprinter2::fingerprint( QString filename )
 
268
{
 
269
    //int time = QDateTime::currentDateTime().toTime_t();
 
270
    
 
271
    int duration, samplerate, bitrate, nchannels;
 
272
    MP3_Source ms;
 
273
 
 
274
    try
 
275
    {
 
276
        MP3_Source::getInfo( filename, duration, samplerate, bitrate, nchannels );
 
277
 
 
278
        m_sampleRate = samplerate;
 
279
        m_numChannels = nchannels;
 
280
 
 
281
        ms.init( filename );
 
282
    }
 
283
    catch ( std::exception& e )
 
284
    {
 
285
        qDebug() << "Failed to read file: " << filename;
 
286
        qDebug() << "MP3_Source error: " << e.what();
 
287
        return;
 
288
    }
 
289
    
 
290
    ms.skipSilence();
 
291
 
 
292
    QByteArray data;
 
293
    bool fpDone = false;
 
294
    if ( mode() == Full )
 
295
    {
 
296
        qDebug() << "*** Starting full FP for: " << filename;
 
297
        m_extractor.initForFullSubmit( m_sampleRate, m_numChannels );
 
298
    }
 
299
    else
 
300
    {
 
301
        qDebug() << "--- Starting query FP for: " << filename;
 
302
        m_extractor.initForQuery( m_sampleRate, m_numChannels, duration );
 
303
 
 
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(
 
308
            0,
 
309
            static_cast<size_t>( m_sampleRate * m_numChannels * secsToSkip ),
 
310
            false );
 
311
    }
 
312
 
 
313
    const size_t PCMBufSize = 131072; 
 
314
    short* pPCMBuffer = new short[PCMBufSize];
 
315
 
 
316
    while ( !fpDone )
 
317
    {
 
318
        if ( m_abort )
 
319
            break;
 
320
 
 
321
        size_t readData = ms.updateBuffer( pPCMBuffer, PCMBufSize );
 
322
        if ( readData == 0 )
 
323
            break;
 
324
 
 
325
        try
 
326
        {
 
327
            fpDone = m_extractor.process( pPCMBuffer, readData, ms.eof() );
 
328
        }
 
329
        catch ( const std::exception& e )
 
330
        {
 
331
            qDebug() << "Fingerprinter failed: " << e.what();
 
332
            break;
 
333
        }
 
334
 
 
335
    } // end outer while
 
336
 
 
337
    if ( fpDone )
 
338
    {
 
339
        // We succeeded
 
340
        std::pair<const char*, size_t> fpData = m_extractor.getFingerprint();
 
341
        m_fingerprint = QByteArray( fpData.first, fpData.second );
 
342
    }
 
343
    else
 
344
    {
 
345
        qDebug() << "FingerprintExtractor::process never returned true, fingerprint not calculated";
 
346
        m_fingerprint.clear();
 
347
    }
 
348
 
 
349
    delete[] pPCMBuffer;
 
350
}
 
351
 
 
352
 
 
353
void Fingerprinter2::onStreamInitialized( long sampleRate, int channels )
 
354
{
 
355
    m_sampleRate = sampleRate;
 
356
    m_numChannels = channels;
 
357
}
 
358
 
 
359
 
 
360
bool Fingerprinter2::decode( QFile& inFile, TranscodeInterface* transcoder2, bool skip )
 
361
{
 
362
    char buffer[k_bufferSize];
 
363
 
 
364
    int numBytes = inFile.read( buffer, k_bufferSize );
 
365
    if ( numBytes == -1 )
 
366
    {
 
367
        qDebug() << "Failed to read data from file.";
 
368
        return false;
 
369
    }
 
370
 
 
371
    // This avoids making a deep copy into the QByteArray
 
372
    QByteArray ba = QByteArray::fromRawData( buffer, numBytes );
 
373
    bool fine = transcoder2->processData( ba, skip );
 
374
    if ( !fine )
 
375
    {
 
376
        qDebug() << "The encoder choked on the data in file.";
 
377
        return false;
 
378
    }
 
379
    
 
380
    return true;
 
381
}