summaryrefslogtreecommitdiffstats
path: root/src/analyzer/analyzer.h
blob: 815514c56f8360609b7a97fe3ff268c6cdb2f195 (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
96
97
98
99
100
101
102
103
104
105
106
#pragma once

#include "audio/types.h"
#include "util/assert.h"
#include "util/types.h"

/*
 * An Analyzer is an object which wants to process an entire song to
 * calculate some kind of metadata about it. This could be bpm, the
 * summary, key or something else crazy. This is to help consolidate the
 * many different threads currently processing the whole track in Mixxx on load.
 *   -- Adam
 */

#include "track/track_decl.h"

class Analyzer {
  public:
    virtual ~Analyzer() = default;

    // This method is supposed to:
    //  1. Check if the track needs to be analyzed, otherwise return false.
    //  2. Perform the initialization and return true on success.
    //  3. If the initialization failed log the internal error and return false.
    virtual bool initialize(TrackPointer tio,
            mixxx::audio::SampleRate sampleRate,
            int totalSamples) = 0;

    /////////////////////////////////////////////////////////////////////////
    // All following methods will only be invoked after initialize()
    // returned true!
    /////////////////////////////////////////////////////////////////////////

    // Analyze the next chunk of audio samples and return true if successful.
    // If processing fails the analysis can be aborted early by returning
    // false. After aborting the analysis only cleanup() will be invoked,
    // but not finalize()!
    virtual bool processSamples(const CSAMPLE* pIn, const int iLen) = 0;

    // Update the track object with the analysis results after
    // processing finished successfully, i.e. all available audio
    // samples have been processed.
    virtual void storeResults(TrackPointer tio) = 0;

    // Discard any temporary results or free allocated memory.
    // This function will be invoked after the results have been
    // stored or if processing aborted preliminary.
    virtual void cleanup() = 0;
};

typedef std::unique_ptr<Analyzer> AnalyzerPtr;

class AnalyzerWithState final {
  public:
    explicit AnalyzerWithState(AnalyzerPtr analyzer)
            : m_analyzer(std::move(analyzer)),
              m_active(false) {
        DEBUG_ASSERT(m_analyzer);
    }
    AnalyzerWithState(const AnalyzerWithState&) = delete;
    AnalyzerWithState(AnalyzerWithState&&) = default;
    ~AnalyzerWithState() {
        VERIFY_OR_DEBUG_ASSERT(!m_active) {
            m_analyzer->cleanup();
        }
    }

    bool isActive() const {
        return m_active;
    }

    bool initialize(TrackPointer tio, mixxx::audio::SampleRate sampleRate, int totalSamples) {
        DEBUG_ASSERT(!m_active);
        return m_active = m_analyzer->initialize(tio, sampleRate, totalSamples);
    }

    void processSamples(const CSAMPLE* pIn, const int iLen) {
        if (m_active) {
            m_active = m_analyzer->processSamples(pIn, iLen);
            if (!m_active) {
                // Ensure that cleanup() is invoked after processing
                // failed and the analyzer became inactive!
                m_analyzer->cleanup();
            }
        }
    }

    void finish(TrackPointer tio) {
        if (m_active) {
            m_analyzer->storeResults(tio);
            m_analyzer->cleanup();
            m_active = false;
        }
    }

    void cancel() {
        if (m_active) {
            m_analyzer->cleanup();
            m_active = false;
        }
    }

  private:
    AnalyzerPtr m_analyzer;
    bool m_active;
};