summaryrefslogtreecommitdiffstats
path: root/src/sources/soundsourcemp3.h
blob: bb83cd9b709e0510d0e26a83d7508cfb55497a99 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#pragma once

#include "sources/soundsourceprovider.h"

#ifdef _MSC_VER
// So mad.h doesn't try to use inline assembly which MSVC doesn't support.
// Notably, FPM_64BIT does not require a 64-bit machine. It merely requires a
// compiler that supports 64-bit types.
#define FPM_64BIT
#endif
#include <mad.h>

#include <QFile>

#include <vector>

namespace mixxx {

class SoundSourceMp3 final : public SoundSource {
  public:
    explicit SoundSourceMp3(const QUrl& url);
    ~SoundSourceMp3() override;

    void close() override;

  protected:
    ReadableSampleFrames readSampleFramesClamped(
            const WritableSampleFrames& sampleFrames) override;

  private:
    OpenResult tryOpen(
            OpenMode mode,
            const OpenParams& params) override;

    QFile m_file;
    quint64 m_fileSize;
    unsigned char* m_pFileData;

    /** Struct used to store mad frames for seeking */
    struct SeekFrameType {
        SINT frameIndex;
        const unsigned char* pInputData;
    };

    /** It is not possible to make a precise seek in an mp3 file without decoding the whole stream.
     * To have precise seek within a limited range from the current decode position, we keep track
     * of past decoded frame, and their exact position. If a seek occurs and it is within the
     * range of frames we keep track of a precise seek occurs, otherwise an imprecise seek is performed
     */
    typedef std::vector<SeekFrameType> SeekFrameList;
    SeekFrameList m_seekFrameList; // ordered-by frameIndex
    SINT m_avgSeekFrameCount;      // avg. sample frames per MP3 frame

    void addSeekFrame(SINT frameIndex, const unsigned char* pInputData);

    /** Returns the position in m_seekFrameList of the requested frame index. */
    SINT findSeekFrameIndex(SINT frameIndex) const;

    SINT m_curFrameIndex;

    // NOTE(uklotzde): Each invocation of initDecoding() must be
    // followed by an invocation of finishDecoding().
    void initDecoding();
    void restartDecoding(const SeekFrameType& seekFrame);
    void finishDecoding();

    // MAD decoder
    mad_stream m_madStream;
    mad_frame m_madFrame;
    mad_synth m_madSynth;

    SINT m_madSynthCount; // left overs from the previous read

    std::vector<unsigned char> m_leftoverBuffer;
};

class SoundSourceProviderMp3 : public SoundSourceProvider {
  public:
    static const QString kDisplayName;
    static const QStringList kSupportedFileExtensions;

    QString getDisplayName() const override {
        return kDisplayName;
    }

    QStringList getSupportedFileExtensions() const override {
        return kSupportedFileExtensions;
    }

    SoundSourcePointer newSoundSource(const QUrl& url) override {
        return newSoundSourceFromUrl<SoundSourceMp3>(url);
    }
};

} // namespace mixxx