summaryrefslogtreecommitdiffstats
path: root/src/controllers/midi/midicontroller.h
blob: f3e5a1f971a654a23ec8b013e44d1af4597588ad (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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#pragma once

#include "controllers/controller.h"
#include "controllers/midi/midicontrollerpreset.h"
#include "controllers/midi/midicontrollerpresetfilehandler.h"
#include "controllers/midi/midimessage.h"
#include "controllers/midi/midioutputhandler.h"
#include "controllers/softtakeover.h"

class DlgControllerLearning;

/// MIDI Controller base class
///
/// This is a base class representing a MIDI controller.
/// It must be inherited by a class that implements it on some API.
///
/// Note that the subclass' destructor should call close() at a minimum.
class MidiController : public Controller {
    Q_OBJECT
  public:
    explicit MidiController();
    ~MidiController() override;

    ControllerJSProxy* jsProxy() override;

    QString presetExtension() override;

    ControllerPresetPointer getPreset() const override {
        MidiControllerPreset* pClone = new MidiControllerPreset();
        *pClone = m_preset;
        return ControllerPresetPointer(pClone);
    }

    void visit(const MidiControllerPreset* preset) override;
    void visit(const HidControllerPreset* preset) override;

    void accept(ControllerVisitor* visitor) override {
        if (visitor) {
            visitor->visit(this);
        }
    }

    bool isMappable() const override {
        return m_preset.isMappable();
    }

    bool matchPreset(const PresetInfo& preset) override;

  signals:
    void messageReceived(unsigned char status, unsigned char control, unsigned char value);

  protected:
    virtual void sendShortMsg(unsigned char status,
            unsigned char byte1,
            unsigned char byte2) = 0;

    /// Alias for send()
    /// The length parameter is here for backwards compatibility for when scripts
    /// were required to specify it.
    inline void sendSysexMsg(const QList<int>& data, unsigned int length = 0) {
        Q_UNUSED(length);
        send(data);
    }

  protected slots:
    virtual void receive(unsigned char status,
            unsigned char control,
            unsigned char value,
            mixxx::Duration timestamp);
    // For receiving System Exclusive messages
    void receive(const QByteArray& data, mixxx::Duration timestamp) override;
    int close() override;

  private slots:
    /// Apply the preset to the controller.
    /// Initializes both controller engine and static output mappings.
    ///
    /// @param initializeScripts Can be set to false to skip script
    /// initialization for unit tests.
    /// @return Returns whether it was successful.
    bool applyPreset(bool initializeScripts = false) override;

    void learnTemporaryInputMappings(const MidiInputMappings& mappings);
    void clearTemporaryInputMappings();
    void commitTemporaryInputMappings();

  private:
    void processInputMapping(const MidiInputMapping& mapping,
                             unsigned char status,
                             unsigned char control,
                             unsigned char value,
                             mixxx::Duration timestamp);
    void processInputMapping(const MidiInputMapping& mapping,
                             const QByteArray& data,
                             mixxx::Duration timestamp);

    double computeValue(MidiOptions options, double _prevmidivalue, double _newmidivalue);
    void createOutputHandlers();
    void updateAllOutputs();
    void destroyOutputHandlers();

    /// Returns a pointer to the currently loaded controller preset. For internal
    /// use only.
    ControllerPreset* preset() override {
        return &m_preset;
    }

    QHash<uint16_t, MidiInputMapping> m_temporaryInputMappings;
    QList<MidiOutputHandler*> m_outputs;
    MidiControllerPreset m_preset;
    SoftTakeoverCtrl m_st;
    QList<QPair<MidiInputMapping, unsigned char> > m_fourteen_bit_queued_mappings;

    // So it can access sendShortMsg()
    friend class MidiOutputHandler;
    friend class MidiControllerTest;
    friend class MidiControllerJSProxy;

    // MIDI learning assistant
    friend class DlgControllerLearning;
};

class MidiControllerJSProxy : public ControllerJSProxy {
    Q_OBJECT
  public:
    MidiControllerJSProxy(MidiController* m_pController)
            : ControllerJSProxy(m_pController),
              m_pMidiController(m_pController) {
    }

    Q_INVOKABLE void sendShortMsg(unsigned char status,
            unsigned char byte1,
            unsigned char byte2) {
        m_pMidiController->sendShortMsg(status, byte1, byte2);
    }

    Q_INVOKABLE void sendSysexMsg(const QList<int>& data, unsigned int length = 0) {
        m_pMidiController->sendSysexMsg(data, length);
    }

  private:
    MidiController* m_pMidiController;
};