~keithsalisbury/mixxx/mixxx

« back to all changes in this revision

Viewing changes to mixxx/src/midi/midimapping.cpp

  • Committer: Keith Salisbury
  • Date: 2012-05-06 13:44:20 UTC
  • mfrom: (2994.1.100 mixxx-trunk)
  • Revision ID: keithsalisbury@gmail.com-20120506134420-8k1dqq10aqmx0ecq
merge with trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/***************************************************************************
2
 
                             midimapping.cpp - "Wow, I wrote more new code!"
3
 
                           MIDI Mapping Class
4
 
                           -------------------
5
 
    begin                : Sat Jan 17 2009
6
 
    copyright            : (C) 2009 Sean M. Pappalardo
7
 
                           (C) 2009 Albert Santoni
8
 
    email                : pegasus@c64.org
9
 
 
10
 
***************************************************************************/
11
 
 
12
 
/***************************************************************************
13
 
*                                                                         *
14
 
*   This program is free software; you can redistribute it and/or modify  *
15
 
*   it under the terms of the GNU General Public License as published by  *
16
 
*   the Free Software Foundation; either version 2 of the License, or     *
17
 
*   (at your option) any later version.                                   *
18
 
*                                                                         *
19
 
***************************************************************************/
20
 
 
21
 
#include <qapplication.h>
22
 
#include "defs_version.h"
23
 
#include "widget/wwidget.h"    // FIXME: This should be xmlparse.h
24
 
#include "mixxxcontrol.h"
25
 
#include "midimessage.h"
26
 
#include "defs.h"
27
 
#include "midiinputmappingtablemodel.h"
28
 
#include "midioutputmappingtablemodel.h"
29
 
#include "midimapping.h"
30
 
#include "mididevicedummy.h"
31
 
#include "midiledhandler.h"
32
 
#include "configobject.h"
33
 
#include "errordialoghandler.h"
34
 
 
35
 
#define REQUIRED_SCRIPT_FILE "midi-mappings-scripts.js"
36
 
#define XML_SCHEMA_VERSION "1"
37
 
#define DEFAULT_DEVICE_PRESET BINDINGS_PATH.append(m_deviceName.right(m_deviceName.size()-m_deviceName.indexOf(" ")-1).replace(" ", "_") + MIDI_MAPPING_EXTENSION)
38
 
 
39
 
// static QString toHex(QString numberStr) {
40
 
//     return "0x" + QString("0" + QString::number(numberStr.toUShort(), 16).toUpper()).right(2);
41
 
// }
42
 
 
43
 
MidiMapping::MidiMapping(MidiDevice* outputMidiDevice)
44
 
        : QObject(),
45
 
          m_mappingLock(QMutex::Recursive) {
46
 
    // If BINDINGS_PATH doesn't exist, create it
47
 
    if (!QDir(BINDINGS_PATH).exists()) {
48
 
        qDebug() << "Creating new MIDI mapping directory" << BINDINGS_PATH;
49
 
        QDir().mkpath(BINDINGS_PATH);
50
 
    }
51
 
    // Likewise, if LPRESETS_PATH doesn't exist, create that too
52
 
    if (!QDir(LPRESETS_PATH).exists()) {
53
 
        qDebug() << "Creating new MIDI presets directory" <<LPRESETS_PATH;
54
 
        QDir().mkpath(LPRESETS_PATH);
55
 
    }
56
 
    // So we can signal the MidiScriptEngine and pass a QList
57
 
    qRegisterMetaType<QList<QString> >("QList<QString>");
58
 
 
59
 
    //Q_ASSERT(outputMidiDevice);
60
 
 
61
 
#ifdef __MIDISCRIPT__
62
 
    //Start the scripting engine.
63
 
    m_pScriptEngine = NULL;
64
 
    m_pOutputMidiDevice = outputMidiDevice;
65
 
    if (m_pOutputMidiDevice)
66
 
        m_deviceName = m_pOutputMidiDevice->getName(); //Name of the device to look for the <controller> block for in the XML.
67
 
 
68
 
    startupScriptEngine();
69
 
#endif
70
 
    m_pMidiInputMappingTableModel = new MidiInputMappingTableModel(this);
71
 
    m_pMidiOutputMappingTableModel = new MidiOutputMappingTableModel(this);
72
 
}
73
 
 
74
 
MidiMapping::~MidiMapping() {
75
 
    delete m_pMidiInputMappingTableModel;
76
 
    delete m_pMidiOutputMappingTableModel;
77
 
}
78
 
 
79
 
#ifdef __MIDISCRIPT__
80
 
void MidiMapping::startupScriptEngine() {
81
 
    QMutexLocker Locker(&m_mappingLock);
82
 
 
83
 
    if(m_pScriptEngine) return;
84
 
 
85
 
    //XXX FIXME: Deadly hack attack:
86
 
    if (m_pOutputMidiDevice == NULL) {
87
 
        m_pOutputMidiDevice = new MidiDeviceDummy(this); //Just make some dummy device :(
88
 
        /* Why can't this be the same as the input MIDI device? Most of the time,
89
 
            the script engine thinks of it as the input device. Scripting is useful
90
 
            even if the MIDI device has no outputs - Sean 4/19/10   */
91
 
    }
92
 
    //XXX Memory leak :(
93
 
 
94
 
    qDebug () << "Starting script engine with output device" << m_pOutputMidiDevice->getName();
95
 
 
96
 
    m_pScriptEngine = new MidiScriptEngine(m_pOutputMidiDevice);
97
 
 
98
 
    m_pScriptEngine->moveToThread(m_pScriptEngine);
99
 
 
100
 
    // Let the script engine tell us when it's done with each task
101
 
    connect(m_pScriptEngine, SIGNAL(initialized()),
102
 
            this, SLOT(slotScriptEngineReady()),
103
 
            Qt::DirectConnection);
104
 
    m_scriptEngineInitializedMutex.lock();
105
 
    m_pScriptEngine->start();
106
 
    // Wait until the script engine is initialized
107
 
    m_scriptEngineInitializedCondition.wait(&m_scriptEngineInitializedMutex);
108
 
    m_scriptEngineInitializedMutex.unlock();
109
 
 
110
 
    // Allow this object to signal the MidiScriptEngine to load the list of script files
111
 
    connect(this, SIGNAL(loadMidiScriptFiles(QList<QString>)), m_pScriptEngine, SLOT(loadScriptFiles(QList<QString>)));
112
 
    // Allow this object to signal the MidiScriptEngine to run the initialization
113
 
    //  functions in the loaded scripts
114
 
    connect(this, SIGNAL(initMidiScripts(QList<QString>)), m_pScriptEngine, SLOT(initializeScripts(QList<QString>)));
115
 
    // Allow this object to signal the MidiScriptEngine to run its shutdown routines
116
 
    connect(this, SIGNAL(shutdownMidiScriptEngine(QList<QString>)), m_pScriptEngine, SLOT(gracefulShutdown(QList<QString>)));
117
 
 
118
 
    // Allow this object to signal the MidiScriptEngine to call functions
119
 
    connect(this, SIGNAL(callMidiScriptFunction(QString)),
120
 
            m_pScriptEngine, SLOT(execute(QString)));
121
 
    connect(this, SIGNAL(callMidiScriptFunction(QString, QString)),
122
 
            m_pScriptEngine, SLOT(execute(QString, QString)));
123
 
 
124
 
    // Allow the MidiScriptEngine to tell us if it needs to reset the controller (on errors)
125
 
    connect(m_pScriptEngine, SIGNAL(resetController()), this, SLOT(reset()));
126
 
}
127
 
 
128
 
void MidiMapping::loadScriptCode() {
129
 
    QMutexLocker Locker(&m_mappingLock);
130
 
    if(m_pScriptEngine) {
131
 
        m_scriptEngineInitializedMutex.lock();
132
 
        // Tell the script engine to run the init function in all loaded scripts
133
 
        emit(loadMidiScriptFiles(m_scriptFileNames));
134
 
        // Wait until it's done
135
 
        m_scriptEngineInitializedCondition.wait(&m_scriptEngineInitializedMutex);
136
 
        m_scriptEngineInitializedMutex.unlock();
137
 
    }
138
 
}
139
 
 
140
 
void MidiMapping::initializeScripts() {
141
 
    QMutexLocker Locker(&m_mappingLock);
142
 
    if(m_pScriptEngine) {
143
 
        m_scriptEngineInitializedMutex.lock();
144
 
        // Tell the script engine to run the init function in all loaded scripts
145
 
        emit(initMidiScripts(m_scriptFunctionPrefixes));
146
 
        // Wait until it's done
147
 
        m_scriptEngineInitializedCondition.wait(&m_scriptEngineInitializedMutex);
148
 
        m_scriptEngineInitializedMutex.unlock();
149
 
    }
150
 
}
151
 
 
152
 
void MidiMapping::shutdownScriptEngine() {
153
 
    QMutexLocker Locker(&m_mappingLock);
154
 
    if(m_pScriptEngine) {
155
 
        // Tell the script engine to do its shutdown sequence
156
 
        emit(shutdownMidiScriptEngine(m_scriptFunctionPrefixes));
157
 
        // ...and wait for it to finish
158
 
        m_pScriptEngine->wait();
159
 
 
160
 
        MidiScriptEngine *engine = m_pScriptEngine;
161
 
        m_pScriptEngine = NULL;
162
 
        delete engine;
163
 
    }
164
 
}
165
 
#endif
166
 
 
167
 
void MidiMapping::setOutputMidiDevice(MidiDevice* outputMidiDevice)
168
 
{
169
 
    m_mappingLock.lock();
170
 
    m_pOutputMidiDevice = outputMidiDevice;
171
 
    m_mappingLock.unlock();
172
 
#ifdef __MIDISCRIPT__
173
 
    //Restart the script engine so it gets its pointer to the output MIDI device updated.
174
 
    restartScriptEngine();
175
 
#endif
176
 
}
177
 
 
178
 
/* ============================== MIDI Input Mapping Modifiers
179
 
                                    (Part of QT MVC wrapper)
180
 
*/
181
 
 
182
 
/*
183
 
 * Return the total number of current input mappings.
184
 
 */
185
 
int MidiMapping::numInputMidiMessages() {
186
 
    m_mappingLock.lock();
187
 
    int value = internalNumInputMidiMessages();
188
 
    m_mappingLock.unlock();
189
 
    return value;
190
 
}
191
 
int MidiMapping::internalNumInputMidiMessages() {
192
 
    return m_inputMapping.size();;
193
 
}
194
 
 
195
 
/*
196
 
 * Return true if the index corresponds to an input mapping key.
197
 
 */
198
 
bool MidiMapping::isInputIndexValid(int index) {
199
 
    if(index < 0 || index >= numInputMidiMessages()) {
200
 
        return false;
201
 
    }
202
 
    return true;
203
 
}
204
 
 
205
 
bool MidiMapping::internalIsInputIndexValid(int index) {
206
 
    if(index < 0 || index >= internalNumInputMidiMessages()) {
207
 
        return false;
208
 
    }
209
 
    return true;
210
 
}
211
 
 
212
 
bool MidiMapping::isMidiMessageMapped(MidiMessage command) {
213
 
    m_mappingLock.lock();
214
 
    bool value = m_inputMapping.contains(command);
215
 
    m_mappingLock.unlock();
216
 
    return value;
217
 
}
218
 
 
219
 
/*
220
 
 * Lookup the MidiMessage corresponding to a given index.
221
 
 */
222
 
MidiMessage MidiMapping::getInputMidiMessage(int index) {
223
 
    m_mappingLock.lock();
224
 
    MidiMessage message;
225
 
    if (internalIsInputIndexValid(index)) {
226
 
        message = m_inputMapping.keys().at(index);
227
 
    }
228
 
    m_mappingLock.unlock();
229
 
    return message;
230
 
}
231
 
 
232
 
/*
233
 
 * Lookup the MixxxControl mapped to a given MidiMessage (by index).
234
 
 */
235
 
MixxxControl MidiMapping::getInputMixxxControl(int index) {
236
 
    m_mappingLock.lock();
237
 
    MixxxControl control;
238
 
    if (internalIsInputIndexValid(index)) {
239
 
        MidiMessage key = m_inputMapping.keys().at(index);
240
 
        control = m_inputMapping.value(key);
241
 
    }
242
 
    m_mappingLock.unlock();
243
 
    return control;
244
 
}
245
 
 
246
 
/*
247
 
 * Lookup the MixxxControl mapped to a given MidiMessage.
248
 
 */
249
 
MixxxControl MidiMapping::getInputMixxxControl(MidiMessage command) {
250
 
    m_mappingLock.lock();
251
 
    MixxxControl control;
252
 
    if (m_inputMapping.contains(command)) {
253
 
        control = m_inputMapping.value(command);
254
 
    }
255
 
    m_mappingLock.unlock();
256
 
    return control;
257
 
}
258
 
 
259
 
/*
260
 
 * Set a MidiMessage -> MixxxControl mapping, replacing an existing one
261
 
 * if necessary.
262
 
 */
263
 
void MidiMapping::setInputMidiMapping(MidiMessage command, MixxxControl control) {
264
 
    m_mappingLock.lock();
265
 
    internalSetInputMidiMapping(command, control, false);
266
 
    m_mappingLock.unlock();
267
 
    emit(inputMappingChanged());
268
 
}
269
 
 
270
 
void MidiMapping::internalSetInputMidiMapping(MidiMessage command, MixxxControl control, bool shouldEmit) {
271
 
    // If the command is already in the mapping, it will be replaced
272
 
    m_inputMapping.insert(command,control);
273
 
    if (shouldEmit)
274
 
        emit(inputMappingChanged());
275
 
}
276
 
 
277
 
 
278
 
/*
279
 
 * Clear a specific mapping for a MidiMessage by index.
280
 
 */
281
 
void MidiMapping::clearInputMidiMapping(int index) {
282
 
    bool valid = false;
283
 
    m_mappingLock.lock();
284
 
    if (internalIsInputIndexValid(index)) {
285
 
        MidiMessage key = m_inputMapping.keys().at(index);
286
 
        m_inputMapping.remove(key);
287
 
        valid = true;
288
 
    }
289
 
    m_mappingLock.unlock();
290
 
 
291
 
    if (valid)
292
 
        emit(inputMappingChanged());
293
 
}
294
 
 
295
 
/*
296
 
 * Clear a specific mapping for a MidiMessage.
297
 
 */
298
 
void MidiMapping::clearInputMidiMapping(MidiMessage command) {
299
 
    m_mappingLock.lock();
300
 
    int changed = m_inputMapping.remove(command);
301
 
    m_mappingLock.unlock();
302
 
 
303
 
    if(changed > 0)
304
 
        emit(inputMappingChanged());
305
 
}
306
 
 
307
 
/*
308
 
 * Clears a range of input mappings. (This really only exists so that
309
 
 * a caller can atomically remove a range of rows)
310
 
 *
311
 
 */
312
 
void MidiMapping::clearInputMidiMapping(int index, int count) {
313
 
    m_mappingLock.lock();
314
 
    QList<MidiMessage> keys = m_inputMapping.keys();
315
 
    int changed = 0;
316
 
    for(int i=index; i < index+count; i++) {
317
 
        MidiMessage command = keys.at(i);
318
 
        changed += m_inputMapping.remove(command);
319
 
    }
320
 
    m_mappingLock.unlock();
321
 
    if(changed > 0)
322
 
        emit(inputMappingChanged());
323
 
}
324
 
 
325
 
/*==== End of MIDI input mapping modifiers (part of QT MVC wrapper) */
326
 
 
327
 
 
328
 
 
329
 
/* ============================== MIDI ***Output*** Mapping Modifiers
330
 
                                    (Part of QT MVC wrapper)
331
 
*/
332
 
 
333
 
/*
334
 
 * Return the total number of current output mappings.
335
 
 */
336
 
int MidiMapping::numOutputMixxxControls() {
337
 
    m_mappingLock.lock();
338
 
    int value = internalNumOutputMixxxControls();
339
 
    m_mappingLock.unlock();
340
 
    return value;
341
 
}
342
 
 
343
 
int MidiMapping::internalNumOutputMixxxControls() {
344
 
    return m_outputMapping.size();
345
 
}
346
 
 
347
 
 
348
 
/*
349
 
 * Return true if the index corresponds to an input mapping key.
350
 
 */
351
 
bool MidiMapping::isOutputIndexValid(int index) {
352
 
    m_mappingLock.lock();
353
 
    bool result = internalIsOutputIndexValid(index);
354
 
    m_mappingLock.unlock();
355
 
    return result;
356
 
}
357
 
 
358
 
bool MidiMapping::internalIsOutputIndexValid(int index) {
359
 
    if(index < 0 || index >= internalNumOutputMixxxControls()) {
360
 
        return false;
361
 
    }
362
 
    return true;
363
 
}
364
 
 
365
 
 
366
 
bool MidiMapping::isMixxxControlMapped(MixxxControl control) {
367
 
    m_mappingLock.lock();
368
 
    bool result = m_outputMapping.contains(control);
369
 
    m_mappingLock.unlock();
370
 
    return result;
371
 
}
372
 
 
373
 
/*
374
 
 * Lookup the MidiMessage corresponding to a given index.
375
 
 */
376
 
MixxxControl MidiMapping::getOutputMixxxControl(int index) {
377
 
    m_mappingLock.lock();
378
 
    MixxxControl control;
379
 
    if (!internalIsOutputIndexValid(index)) {
380
 
        control = m_outputMapping.keys().at(index);
381
 
    }
382
 
    m_mappingLock.unlock();
383
 
    return control;
384
 
}
385
 
 
386
 
/*
387
 
 * Lookup the MixxxControl mapped to a given MidiMessage (by index).
388
 
 */
389
 
MidiMessage MidiMapping::getOutputMidiMessage(int index) {
390
 
    qDebug() << "getOutputMidiMessage" << index;
391
 
    m_mappingLock.lock();
392
 
    MidiMessage message;
393
 
    if (internalIsOutputIndexValid(index)) {
394
 
        MixxxControl key = m_outputMapping.keys().at(index);
395
 
        message = m_outputMapping.value(key);
396
 
    }
397
 
    m_mappingLock.unlock();
398
 
    return message;
399
 
}
400
 
 
401
 
/*
402
 
 * Lookup the MixxxControl mapped to a given MidiMessage.
403
 
 */
404
 
MidiMessage MidiMapping::getOutputMidiMessage(MixxxControl control) {
405
 
    m_mappingLock.lock();
406
 
    MidiMessage message;
407
 
    if (m_outputMapping.contains(control)) {
408
 
        message = m_outputMapping.value(control);
409
 
    }
410
 
    m_mappingLock.unlock();
411
 
    return message;
412
 
}
413
 
 
414
 
/*
415
 
 * Set a MidiMessage -> MixxxControl mapping, replacing an existing one
416
 
 * if necessary.
417
 
 */
418
 
void MidiMapping::setOutputMidiMapping(MixxxControl control, MidiMessage command) {
419
 
    m_mappingLock.lock();
420
 
    internalSetOutputMidiMapping(control, command, false);
421
 
    m_mappingLock.unlock();
422
 
    emit(outputMappingChanged());
423
 
}
424
 
 
425
 
void MidiMapping::internalSetOutputMidiMapping(MixxxControl control,
426
 
                                               MidiMessage command,
427
 
                                               bool shouldEmit) {
428
 
    // If the command is already in the mapping, it will be replaced
429
 
    m_outputMapping.insert(control, command);
430
 
 
431
 
    if (shouldEmit)
432
 
        emit(outputMappingChanged());
433
 
}
434
 
 
435
 
/*
436
 
 * Clear a specific mapping for a MidiMessage by index.
437
 
 */
438
 
void MidiMapping::clearOutputMidiMapping(int index) {
439
 
    m_mappingLock.lock();
440
 
    bool changed = false;
441
 
    if (internalIsOutputIndexValid(index)) {
442
 
        qDebug() << m_outputMapping.size();
443
 
        qDebug() << "MidiMapping: removing" << index;
444
 
        MixxxControl key = m_outputMapping.keys().at(index);
445
 
        m_outputMapping.remove(key);
446
 
        qDebug() << m_outputMapping.size();
447
 
        changed = true;
448
 
    }
449
 
    m_mappingLock.unlock();
450
 
 
451
 
    if (changed)
452
 
        emit(outputMappingChanged());
453
 
}
454
 
 
455
 
/*
456
 
 * Clear a specific mapping for a MidiMessage.
457
 
 */
458
 
void MidiMapping::clearOutputMidiMapping(MixxxControl control) {
459
 
    m_mappingLock.lock();
460
 
    int changed = m_outputMapping.remove(control);
461
 
    m_mappingLock.unlock();
462
 
 
463
 
    if(changed > 0)
464
 
        emit(outputMappingChanged());
465
 
}
466
 
 
467
 
/*
468
 
 * Clears a range of input mappings. (This really only exists so that
469
 
 * a caller can atomically remove a range of rows)
470
 
 *
471
 
 */
472
 
void MidiMapping::clearOutputMidiMapping(int index, int count) {
473
 
    m_mappingLock.lock();
474
 
    QList<MixxxControl> keys = m_outputMapping.keys();
475
 
    int changed = 0;
476
 
    for(int i=index; i < index+count; i++) {
477
 
        MixxxControl control = keys.at(i);
478
 
        changed += m_outputMapping.remove(control);
479
 
    }
480
 
    m_mappingLock.unlock();
481
 
 
482
 
    if(changed > 0)
483
 
        emit(outputMappingChanged());
484
 
}
485
 
 
486
 
/*==== End of MIDI output mapping modifiers (part of QT MVC wrapper) */
487
 
 
488
 
 
489
 
#ifdef __MIDISCRIPT__
490
 
/* -------- ------------------------------------------------------
491
 
   Purpose: Adds an entry to the list of script file names
492
 
            & associated list of function prefixes
493
 
   Input:   QString file name, QString function prefix
494
 
   Output:  -
495
 
   -------- ------------------------------------------------------ */
496
 
void MidiMapping::addScriptFile(QString filename, QString functionprefix) {
497
 
    QMutexLocker Locker(&m_mappingLock);
498
 
    m_scriptFileNames.append(filename);
499
 
    m_scriptFunctionPrefixes.append(functionprefix);
500
 
}
501
 
#endif
502
 
 
503
 
/* setName(QString)
504
 
 * Sets the controller name this mapping corresponds to
505
 
 * @param name The controller name this mapping is hooked to
506
 
 */
507
 
void MidiMapping::setName(QString name) {
508
 
    QMutexLocker Locker(&m_mappingLock);
509
 
    m_deviceName = name;
510
 
}
511
 
 
512
 
/* loadPreset()
513
 
 * Overloaded function for convenience, uses the default device path
514
 
 * @param forceLoad Forces the MIDI mapping to be loaded, regardless of whether or not the controller id
515
 
 *        specified in the mapping matches the device this MidiMapping object is hooked up to.
516
 
 */
517
 
void MidiMapping::loadPreset(bool forceLoad) {
518
 
    loadPreset(DEFAULT_DEVICE_PRESET, forceLoad);
519
 
}
520
 
 
521
 
/* loadPreset(QString)
522
 
 * Overloaded function for convenience
523
 
 * @param path The path to a MIDI mapping XML file.
524
 
 * @param forceLoad Forces the MIDI mapping to be loaded, regardless of whether or not the controller id
525
 
 *        specified in the mapping matches the device this MidiMapping object is hooked up to.
526
 
 */
527
 
void MidiMapping::loadPreset(QString path, bool forceLoad) {
528
 
    qDebug() << "MidiMapping: Loading MIDI preset from" << path;
529
 
    loadPreset(WWidget::openXMLFile(path, "controller"), forceLoad);
530
 
}
531
 
 
532
 
/* loadPreset(QDomElement)
533
 
 * Loads a set of MIDI bindings from a QDomElement structure.
534
 
 * @param root The root node of the XML document for the MIDI mapping.
535
 
 * @param forceLoad Forces the MIDI mapping to be loaded, regardless of whether or not the controller id
536
 
 *        specified in the mapping matches the device this MidiMapping object is hooked up to.
537
 
 */
538
 
void MidiMapping::loadPreset(QDomElement root, bool forceLoad) {
539
 
    //qDebug() << QString("MidiMapping: loadPreset() called in thread ID=%1").arg(this->thread()->currentThreadId(),0,16);
540
 
 
541
 
    if (root.isNull()) return;
542
 
 
543
 
    m_pMidiInputMappingTableModel->removeRows(0, m_pMidiInputMappingTableModel->rowCount());
544
 
    m_pMidiOutputMappingTableModel->removeRows(0, m_pMidiOutputMappingTableModel->rowCount());
545
 
 
546
 
    // Note, the lock comes after these two lines. We mustn't touch the
547
 
    // *MappingTableModel's after we are locked because they have pointers to us
548
 
    // so when we make a call to them they might in turn call us, causing a
549
 
    // deadlock.
550
 
    m_mappingLock.lock();
551
 
 
552
 
#ifdef __MIDISCRIPT__
553
 
    m_scriptFileNames.clear();
554
 
    m_scriptFunctionPrefixes.clear();
555
 
#endif
556
 
 
557
 
    // For each controller in the DOM
558
 
    m_Bindings = root;
559
 
    QDomElement controller = m_Bindings.firstChildElement("controller");
560
 
 
561
 
    // For each controller in the MIDI mapping XML...
562
 
    //(Only parse the <controller> block if it's id matches our device name, otherwise
563
 
    //keep looking at the next controller blocks....)
564
 
    QString device;
565
 
    while (!controller.isNull()) {
566
 
        // Get deviceid
567
 
        device = controller.attribute("id","");
568
 
        if (device != m_deviceName && !forceLoad) {
569
 
            controller = controller.nextSiblingElement("controller");
570
 
        }
571
 
        else
572
 
            break;
573
 
    }
574
 
 
575
 
    if (!controller.isNull()) {
576
 
 
577
 
        qDebug() << device << " settings found";
578
 
#ifdef __MIDISCRIPT__
579
 
        // Build a list of MIDI script files to load
580
 
 
581
 
        QDomElement scriptFile = controller.firstChildElement("scriptfiles").firstChildElement("file");
582
 
 
583
 
        // Default currently required file
584
 
        addScriptFile(REQUIRED_SCRIPT_FILE,"");
585
 
 
586
 
        // Look for additional ones
587
 
        while (!scriptFile.isNull()) {
588
 
            QString functionPrefix = scriptFile.attribute("functionprefix","");
589
 
            QString filename = scriptFile.attribute("filename","");
590
 
            addScriptFile(filename, functionPrefix);
591
 
 
592
 
            scriptFile = scriptFile.nextSiblingElement("file");
593
 
        }
594
 
 
595
 
        loadScriptCode();   // Actually load code from the list built above
596
 
 
597
 
        QStringList scriptFunctions;
598
 
        if (m_pScriptEngine != NULL) {
599
 
            scriptFunctions = m_pScriptEngine->getScriptFunctions();
600
 
        }
601
 
 
602
 
#endif
603
 
 
604
 
        QDomElement control = controller.firstChildElement("controls").firstChildElement("control");
605
 
 
606
 
        //Iterate through each <control> block in the XML
607
 
        while (!control.isNull()) {
608
 
 
609
 
            //Unserialize these objects from the XML
610
 
            MidiMessage midiMessage(control);
611
 
            MixxxControl mixxxControl(control);
612
 
#ifdef __MIDISCRIPT__
613
 
            // Verify script functions are loaded
614
 
            if (mixxxControl.getMidiOption()==MIDI_OPT_SCRIPT &&
615
 
                scriptFunctions.indexOf(mixxxControl.getControlObjectValue())==-1) {
616
 
 
617
 
                QString status = QString("%1").arg(midiMessage.getMidiStatusByte(), 0, 16).toUpper();
618
 
                status = "0x"+status;
619
 
                QString byte2 = QString("%1").arg(midiMessage.getMidiNo(), 0, 16).toUpper();
620
 
                byte2 = "0x"+byte2;
621
 
 
622
 
                // If status is MIDI pitch, the 2nd byte is part of the payload so don't display it
623
 
                if (midiMessage.getMidiStatusByte() == 0xE0) byte2 = "";
624
 
 
625
 
                QString errorLog = QString("MIDI script function \"%1\" not found. "
626
 
                                           "(Mapped to MIDI message %2 %3)")
627
 
                        .arg(mixxxControl.getControlObjectValue(), status, byte2);
628
 
 
629
 
                if (m_pOutputMidiDevice != NULL
630
 
                    && m_pOutputMidiDevice->midiDebugging()) {
631
 
                        qCritical() << errorLog;
632
 
                }
633
 
                else {
634
 
                    qWarning() << errorLog;
635
 
                    ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties();
636
 
                    props->setType(DLG_WARNING);
637
 
                    props->setTitle(tr("MIDI script function not found"));
638
 
                    props->setText(QString(tr("The MIDI script function '%1' was not "
639
 
                                    "found in loaded scripts."))
640
 
                                    .arg(mixxxControl.getControlObjectValue()));
641
 
                    props->setInfoText(QString(tr("The MIDI message %1 %2 will not be bound."
642
 
                                    "\n(Click Show Details for hints.)"))
643
 
                                       .arg(status, byte2));
644
 
                    QString detailsText = QString(tr("* Check to see that the "
645
 
                        "function name is spelled correctly in the mapping "
646
 
                        "file (.xml) and script file (.js)\n"));
647
 
                    detailsText += QString(tr("* Check to see that the script "
648
 
                        "file name (.js) is spelled correctly in the mapping "
649
 
                        "file (.xml)"));
650
 
                    props->setDetails(detailsText);
651
 
 
652
 
                    ErrorDialogHandler::instance()->requestErrorDialog(props);
653
 
                }
654
 
            } else {
655
 
#endif
656
 
                //Add to the input mapping.
657
 
                internalSetInputMidiMapping(midiMessage, mixxxControl, true);
658
 
                /*Old code: m_inputMapping.insert(midiMessage, mixxxControl);
659
 
                  Reason why this is bad: Don't want to access this directly because the
660
 
                  model doesn't get notified about the update */
661
 
#ifdef __MIDISCRIPT__
662
 
            }
663
 
#endif
664
 
            control = control.nextSiblingElement("control");
665
 
        }
666
 
 
667
 
        qDebug() << "MidiMapping: Input parsed!";
668
 
 
669
 
        QDomElement output = controller.firstChildElement("outputs").firstChildElement("output");
670
 
 
671
 
        //Iterate through each <control> block in the XML
672
 
        while (!output.isNull()) {
673
 
            //Unserialize these objects from the XML
674
 
            MidiMessage midiMessage(output);
675
 
            MixxxControl mixxxControl(output, true);
676
 
 
677
 
            //Add to the output mapping.
678
 
            internalSetOutputMidiMapping(mixxxControl, midiMessage, true);
679
 
            /*Old code: m_outputMapping.insert(mixxxControl, midiMessage);
680
 
              Reason why this is bad: Don't want to access this directly because the
681
 
                                      model doesn't get notified about the update */
682
 
 
683
 
            output = output.nextSiblingElement("output");
684
 
        }
685
 
 
686
 
        qDebug() << "MidiMapping: Output parsed!";
687
 
        //controller = controller.nextSiblingElement("controller"); //FIXME: Remove this line of code permanently - Albert
688
 
    }
689
 
 
690
 
    m_mappingLock.unlock();
691
 
 
692
 
}   // END loadPreset(QDomElement)
693
 
 
694
 
/* savePreset()
695
 
 * Saves the current table of bindings to the default device XML file.
696
 
 */
697
 
void MidiMapping::savePreset() {
698
 
    savePreset(DEFAULT_DEVICE_PRESET);
699
 
}
700
 
 
701
 
/* savePreset(QString)
702
 
 * Given a path, saves the current table of bindings to an XML file.
703
 
 */
704
 
void MidiMapping::savePreset(QString path) {
705
 
    qDebug() << "Writing MIDI preset file" << path;
706
 
    QMutexLocker locker(&m_mappingLock);
707
 
    QFile output(path);
708
 
    if (!output.open(QIODevice::WriteOnly | QIODevice::Truncate)) return;
709
 
    QTextStream outputstream(&output);
710
 
    // Construct the DOM from the table
711
 
    QDomDocument docBindings = buildDomElement();
712
 
    // Save the DOM to the XML file
713
 
    docBindings.save(outputstream, 4);
714
 
    output.close();
715
 
}
716
 
 
717
 
/* applyPreset()
718
 
 * Load the current bindings set into the MIDI handler, and the outputs info into
719
 
 * the LED handler.
720
 
 */
721
 
void MidiMapping::applyPreset() {
722
 
    qDebug() << "MidiMapping::applyPreset()";
723
 
    QMutexLocker locker(&m_mappingLock);
724
 
 
725
 
#ifdef __MIDISCRIPT__
726
 
    // Since this can be called after re-enabling a device without reloading the XML preset,
727
 
    // the script engine must have its code loaded here as well
728
 
    QStringList scriptFunctions;
729
 
    if (m_pScriptEngine != NULL) {
730
 
        scriptFunctions = m_pScriptEngine->getScriptFunctions();
731
 
    }
732
 
    if (scriptFunctions.isEmpty()) loadScriptCode();
733
 
 
734
 
    initializeScripts();
735
 
#endif
736
 
 
737
 
    if (m_pOutputMidiDevice != NULL) {
738
 
        //^^^ Only execute this code if we have an output device hooked up
739
 
        //    to this MidiMapping...
740
 
 
741
 
        QDomElement controller = m_Bindings.firstChildElement("controller");
742
 
        // For each device
743
 
        while (!controller.isNull()) {
744
 
            // Device Outputs - LEDs
745
 
            QString deviceId = controller.attribute("id","");
746
 
 
747
 
            qDebug() << "MidiMapping: Processing MIDI Output Bindings for" << deviceId;
748
 
            MidiLedHandler::createHandlers(controller.namedItem("outputs").firstChild(),
749
 
                                           *m_pOutputMidiDevice);
750
 
 
751
 
            // Next device
752
 
            controller = controller.nextSiblingElement("controller");
753
 
        }
754
 
        MidiLedHandler::updateAll();
755
 
    }
756
 
}
757
 
 
758
 
/* clearPreset()
759
 
 * Creates a blank bindings preset.
760
 
 */
761
 
void MidiMapping::clearPreset() {
762
 
    // Assumes the lock is held.
763
 
    // Create a new blank DomNode
764
 
    QString blank = "<MixxxMIDIPreset schemaVersion=\"" + QString(XML_SCHEMA_VERSION) + "\" mixxxVersion=\"" + QString(VERSION) + "+\">\n"
765
 
    "</MixxxMIDIPreset>\n";
766
 
    QDomDocument doc("Bindings");
767
 
    doc.setContent(blank);
768
 
    m_Bindings = doc.documentElement();
769
 
}
770
 
 
771
 
/* buildDomElement()
772
 
 * Updates the DOM with what is currently in the table
773
 
 */
774
 
 QDomDocument MidiMapping::buildDomElement() {
775
 
     // We should hold the mapping lock. The lock is recursive so if we already
776
 
     // hold it it will relock.
777
 
     QMutexLocker locker(&m_mappingLock);
778
 
 
779
 
    clearPreset(); // Create blank document
780
 
 
781
 
    QDomDocument doc("Bindings");
782
 
    QString blank = "<MixxxMIDIPreset schemaVersion=\"" + QString(XML_SCHEMA_VERSION) + "\" mixxxVersion=\"" + QString(VERSION) + "+\">\n"
783
 
                    "</MixxxMIDIPreset>\n";
784
 
 
785
 
    doc.setContent(blank);
786
 
 
787
 
    QDomElement rootNode = doc.documentElement();
788
 
    QDomElement controller = doc.createElement("controller");
789
 
    controller.setAttribute("id", m_deviceName.right(m_deviceName.size()-m_deviceName.indexOf(" ")-1));
790
 
    rootNode.appendChild(controller);
791
 
 
792
 
#ifdef __MIDISCRIPT__
793
 
    //This sucks, put this code inside MidiScriptEngine instead of here,
794
 
    // and just ask MidiScriptEngine to spit it out for us.
795
 
//     qDebug() << "MidiMapping: Writing script block!";
796
 
 
797
 
    QDomElement scriptFiles = doc.createElement("scriptfiles");
798
 
    controller.appendChild(scriptFiles);
799
 
 
800
 
 
801
 
    for (int i = 0; i < m_scriptFileNames.count(); i++) {
802
 
//         qDebug() << "MidiMapping: writing script block for" << m_scriptFileNames[i];
803
 
        QString filename = m_scriptFileNames[i];
804
 
 
805
 
 
806
 
        //Don't need to write anything for the required mapping file.
807
 
        if (filename != REQUIRED_SCRIPT_FILE) {
808
 
            qDebug() << "MidiMapping: writing script block for" << filename;
809
 
            QString functionPrefix = m_scriptFunctionPrefixes[i];
810
 
            QDomElement scriptFile = doc.createElement("file");
811
 
 
812
 
 
813
 
            scriptFile.setAttribute("filename", filename);
814
 
            scriptFile.setAttribute("functionprefix", functionPrefix);
815
 
 
816
 
            scriptFiles.appendChild(scriptFile);
817
 
        }
818
 
    }
819
 
#endif
820
 
 
821
 
    QDomElement controls = doc.createElement("controls");
822
 
    controller.appendChild(controls);
823
 
 
824
 
 
825
 
    //Iterate over all of the command/control pairs in the input mapping
826
 
    QHashIterator<MidiMessage, MixxxControl> it(m_inputMapping);
827
 
    while (it.hasNext()) {
828
 
        it.next();
829
 
 
830
 
        QDomElement controlNode = doc.createElement("control");
831
 
 
832
 
        //Save the MidiMessage and MixxxControl objects as XML
833
 
        it.key().serializeToXML(controlNode);
834
 
        it.value().serializeToXML(controlNode);
835
 
 
836
 
        //Add the control node we just created to the XML document in the proper spot
837
 
        controls.appendChild(controlNode);
838
 
    }
839
 
 
840
 
 
841
 
    QDomElement outputs = doc.createElement("outputs");
842
 
    controller.appendChild(outputs);
843
 
 
844
 
    //Iterate over all of the control/command pairs in the OUTPUT mapping
845
 
    QHashIterator<MixxxControl, MidiMessage> outIt(m_outputMapping);
846
 
    while (outIt.hasNext()) {
847
 
        outIt.next();
848
 
 
849
 
        QDomElement outputNode = doc.createElement("output");
850
 
 
851
 
 
852
 
        //Save the MidiMessage and MixxxControl objects as XML
853
 
        outIt.key().serializeToXML(outputNode, true);
854
 
        outIt.value().serializeToXML(outputNode, true);
855
 
 
856
 
        //Add the control node we just created to the XML document in the proper spot
857
 
        outputs.appendChild(outputNode);
858
 
    }
859
 
 
860
 
    m_Bindings = doc.documentElement();
861
 
    return doc;
862
 
}
863
 
 
864
 
/* -------- ------------------------------------------------------
865
 
   Purpose: Adds an input MIDI mapping block to the XML.
866
 
   Input:   QDomElement control, QString device
867
 
   Output:  -
868
 
   -------- ------------------------------------------------------ */
869
 
void MidiMapping::addControl(QDomElement &control, QString device) {
870
 
    QDomDocument nodeMaker;
871
 
    //Add control to correct device tag - find the correct tag
872
 
    QDomElement controller = m_Bindings.firstChildElement("controller");
873
 
    while (controller.attribute("id","") != device && !controller.isNull()) {
874
 
        controller = controller.nextSiblingElement("controller");
875
 
    }
876
 
    if (controller.isNull()) {
877
 
        // No tag was found - create it
878
 
        controller = nodeMaker.createElement("controller");
879
 
        controller.setAttribute("id", device);
880
 
        m_Bindings.appendChild(controller);
881
 
    }
882
 
    // Check for controls tag
883
 
    QDomElement controls = controller.firstChildElement("controls");
884
 
    if (controls.isNull()) {
885
 
        controls = nodeMaker.createElement("controls");
886
 
        controller.appendChild(controls);
887
 
    }
888
 
    controls.appendChild(control);
889
 
}
890
 
 
891
 
/* -------- ------------------------------------------------------
892
 
   Purpose: This code sucks, temporary hack
893
 
   Input:   QDomElement control, QString device
894
 
   Output:  -
895
 
   -------- ------------------------------------------------------ */
896
 
void MidiMapping::addMidiScriptInfo(QDomElement &scriptFile, QString device) {
897
 
    QDomDocument nodeMaker;
898
 
    //Add control to correct device tag - find the correct tag
899
 
    QDomElement controller = m_Bindings.firstChildElement("controller");
900
 
    while (controller.attribute("id","") != device && !controller.isNull()) {
901
 
        controller = controller.nextSiblingElement("controller");
902
 
    }
903
 
    if (controller.isNull()) {
904
 
        // No tag was found - create it
905
 
        controller = nodeMaker.createElement("controller");
906
 
        controller.setAttribute("id", device);
907
 
        m_Bindings.appendChild(controller);
908
 
    }
909
 
    // Check for controls tag
910
 
    QDomElement scriptfiles = controller.firstChildElement("scriptfiles");
911
 
    if (scriptfiles.isNull()) {
912
 
        scriptfiles = nodeMaker.createElement("scriptfiles");
913
 
        controller.appendChild(scriptfiles);
914
 
    }
915
 
    scriptfiles.appendChild(scriptFile);
916
 
}
917
 
 
918
 
/* -------- ------------------------------------------------------
919
 
   Purpose: Adds an output (LED, etc.) MIDI mapping block to the XML.
920
 
   Input:   QDomElement output, QString device
921
 
   Output:  -
922
 
   -------- ------------------------------------------------------ */
923
 
void MidiMapping::addOutput(QDomElement &output, QString device) {
924
 
    QDomDocument nodeMaker;
925
 
    // Find the controller to attach the XML to...
926
 
    QDomElement controller = m_Bindings.firstChildElement("controller");
927
 
    while (controller.attribute("id","") != device && !controller.isNull()) {
928
 
        controller = controller.nextSiblingElement("controller");
929
 
    }
930
 
    if (controller.isNull()) {
931
 
        // No tag was found - create it
932
 
        controller = nodeMaker.createElement("controller");
933
 
        controller.setAttribute("id", device);
934
 
        m_Bindings.appendChild(controller);
935
 
    }
936
 
 
937
 
    // Find the outputs block
938
 
    QDomElement outputs = controller.firstChildElement("outputs");
939
 
    if (outputs.isNull()) {
940
 
        outputs = nodeMaker.createElement("outputs");
941
 
        controller.appendChild(outputs);
942
 
    }
943
 
    // attach the output to the outputs block
944
 
    outputs.appendChild(output);
945
 
}
946
 
 
947
 
bool MidiMapping::addInputControl(MidiStatusByte midiStatus, int midiNo, int midiChannel,
948
 
                                  QString controlObjectGroup, QString controlObjectKey,
949
 
                                  QString controlObjectDescription, MidiOption midiOption)
950
 
{
951
 
    return addInputControl(MidiMessage(midiStatus, midiNo, midiChannel),
952
 
                           MixxxControl(controlObjectGroup, controlObjectKey,
953
 
                                        controlObjectDescription, midiOption));
954
 
}
955
 
 
956
 
bool MidiMapping::addInputControl(MidiMessage message, MixxxControl control)
957
 
{
958
 
    //TODO: Check if mapping already exists for this MidiMessage.
959
 
 
960
 
    //Add to the input mapping.
961
 
    m_inputMapping.insert(message, control);
962
 
    return true; //XXX is this right? should this be returning whether the add happened successfully?
963
 
 
964
 
}
965
 
 
966
 
void MidiMapping::removeInputMapping(MidiStatusByte midiStatus, int midiNo, int midiChannel)
967
 
{
968
 
    m_inputMapping.remove(MidiMessage(midiStatus, midiNo, midiChannel));
969
 
}
970
 
 
971
 
MidiInputMappingTableModel* MidiMapping::getMidiInputMappingTableModel()
972
 
{
973
 
    return m_pMidiInputMappingTableModel;
974
 
}
975
 
 
976
 
MidiOutputMappingTableModel* MidiMapping::getMidiOutputMappingTableModel()
977
 
{
978
 
    return m_pMidiOutputMappingTableModel;
979
 
}
980
 
 
981
 
//Used by MidiObject to query what control matches a given MIDI command.
982
 
/*MixxxControl* MidiMapping::getInputMixxxControl(MidiMessage command)
983
 
{
984
 
    if (!m_inputMapping.contains(command)) {
985
 
        qWarning() << "Unbound MIDI command";
986
 
        qDebug() << "Midi Status:" << command.getMidiStatusByte();
987
 
        qDebug() << "Midi No:" << command.getMidiNo();
988
 
        qDebug() << "Midi Channel:" << command.getMidiChannel();
989
 
        return NULL;
990
 
    }
991
 
 
992
 
    MixxxControl* control = &(m_inputMapping[command]);
993
 
    return control;
994
 
    }*/
995
 
 
996
 
// BJW: Note: _prevmidivalue is not the previous MIDI value. It's the
997
 
// current controller value, scaled to 0-127 but only in the case of pots.
998
 
// (See Control*::GetMidiValue())
999
 
 
1000
 
 // static
1001
 
double MidiMapping::ComputeValue(MidiOption midioption, double _prevmidivalue, double _newmidivalue)
1002
 
{
1003
 
    double tempval = 0.;
1004
 
    double diff = 0.;
1005
 
 
1006
 
    // qDebug() << "ComputeValue: option " << midioption << ", MIDI value " << _newmidivalue << ", current control value " << _prevmidivalue;
1007
 
    if (midioption == MIDI_OPT_NORMAL) {
1008
 
        return _newmidivalue;
1009
 
    }
1010
 
    else if (midioption == MIDI_OPT_INVERT)
1011
 
    {
1012
 
        return 127. - _newmidivalue;
1013
 
    }
1014
 
    else if (midioption == MIDI_OPT_ROT64 || midioption == MIDI_OPT_ROT64_INV)
1015
 
    {
1016
 
        tempval = _prevmidivalue;
1017
 
        diff = _newmidivalue - 64.;
1018
 
        if (diff == -1 || diff == 1)
1019
 
            diff /= 16;
1020
 
        else
1021
 
            diff += (diff > 0 ? -1 : +1);
1022
 
        if (midioption == MIDI_OPT_ROT64)
1023
 
            tempval += diff;
1024
 
        else
1025
 
            tempval -= diff;
1026
 
        return (tempval < 0. ? 0. : (tempval > 127. ? 127.0 : tempval));
1027
 
    }
1028
 
    else if (midioption == MIDI_OPT_ROT64_FAST)
1029
 
    {
1030
 
        tempval = _prevmidivalue;
1031
 
        diff = _newmidivalue - 64.;
1032
 
        diff *= 1.5;
1033
 
        tempval += diff;
1034
 
        return (tempval < 0. ? 0. : (tempval > 127. ? 127.0 : tempval));
1035
 
    }
1036
 
    else if (midioption == MIDI_OPT_DIFF)
1037
 
    {
1038
 
        //Interpret 7-bit signed value using two's compliment.
1039
 
        if (_newmidivalue >= 64.)
1040
 
            _newmidivalue = _newmidivalue - 128.;
1041
 
        //Apply sensitivity to signed value. FIXME
1042
 
       // if(sensitivity > 0)
1043
 
        //    _newmidivalue = _newmidivalue * ((double)sensitivity / 50.);
1044
 
        //Apply new value to current value.
1045
 
        _newmidivalue = _prevmidivalue + _newmidivalue;
1046
 
    }
1047
 
    else if (midioption == MIDI_OPT_SELECTKNOB)
1048
 
    {
1049
 
        //Interpret 7-bit signed value using two's compliment.
1050
 
        if (_newmidivalue >= 64.)
1051
 
            _newmidivalue = _newmidivalue - 128.;
1052
 
        //Apply sensitivity to signed value. FIXME
1053
 
        //if(sensitivity > 0)
1054
 
        //    _newmidivalue = _newmidivalue * ((double)sensitivity / 50.);
1055
 
        //Since this is a selection knob, we do not want to inherit previous values.
1056
 
    }
1057
 
    else if (midioption == MIDI_OPT_BUTTON) { _newmidivalue = (_newmidivalue != 0); }
1058
 
    else if (midioption == MIDI_OPT_SWITCH) { _newmidivalue = 1; }
1059
 
    else if (midioption == MIDI_OPT_SPREAD64)
1060
 
    {
1061
 
 
1062
 
        //qDebug() << "MIDI_OPT_SPREAD64";
1063
 
        // BJW: Spread64: Distance away from centre point (aka "relative CC")
1064
 
        // Uses a similar non-linear scaling formula as ControlTTRotary::getValueFromWidget()
1065
 
        // but with added sensitivity adjustment. This formula is still experimental.
1066
 
 
1067
 
        _newmidivalue = _newmidivalue - 64.;
1068
 
        //FIXME
1069
 
        //double distance = _newmidivalue - 64.;
1070
 
        // _newmidivalue = distance * distance * sensitivity / 50000.;
1071
 
        //if (distance < 0.)
1072
 
        //    _newmidivalue = -_newmidivalue;
1073
 
 
1074
 
         //qDebug() << "Spread64: in " << distance << "  out " << _newmidivalue;
1075
 
    }
1076
 
    else if (midioption == MIDI_OPT_HERC_JOG)
1077
 
    {
1078
 
        if (_newmidivalue > 64.) { _newmidivalue -= 128.; }
1079
 
        _newmidivalue += _prevmidivalue;
1080
 
        //if (_prevmidivalue != 0.0) { qDebug() << "AAAAAAAAAAAA" << _prevmidivalue; }
1081
 
    }
1082
 
    else
1083
 
    {
1084
 
        qWarning("Unknown MIDI option %d", midioption);
1085
 
    }
1086
 
 
1087
 
    return _newmidivalue;
1088
 
}
1089
 
 
1090
 
void MidiMapping::finishMidiLearn(MidiMessage message)
1091
 
{
1092
 
    bool shouldEmit = false;
1093
 
    m_mappingLock.lock();
1094
 
    //We've received a MidiMessage that should be mapped onto some control. When
1095
 
    //beginMidiLearn() was called, we were given the control that should be
1096
 
    //mapped, so let's connect the message to the saved control and thus
1097
 
    //"create" the mapping between the two.
1098
 
    if (!m_controlToLearn.isNull()) { //Ensure we've actually been told to learn
1099
 
                                      //a control.  Note the ! out front.
1100
 
        addInputControl(message, m_controlToLearn);
1101
 
 
1102
 
        //If we caught a NOTE_ON message, add a binding for NOTE_OFF as well.
1103
 
        if (message.getMidiStatusByte() == MIDI_STATUS_NOTE_ON) {
1104
 
            MidiMessage noteOffMessage(message);
1105
 
            noteOffMessage.setMidiStatusByte(MIDI_STATUS_NOTE_OFF);
1106
 
            addInputControl(noteOffMessage, m_controlToLearn);
1107
 
        }
1108
 
 
1109
 
        //Reset the saved control.
1110
 
        m_controlToLearn = MixxxControl();
1111
 
 
1112
 
        qDebug() << "MidiMapping: Learning finished!";
1113
 
 
1114
 
        shouldEmit = true;
1115
 
    }
1116
 
    m_mappingLock.unlock();
1117
 
 
1118
 
    if (shouldEmit) {
1119
 
        //Notify the prefs dialog that we've finished doing a MIDI learn.
1120
 
        emit(midiLearningFinished(message));
1121
 
        emit(midiLearningFinished()); //Tells MidiObject to stop feeding us messages.
1122
 
        emit(inputMappingChanged());
1123
 
    }
1124
 
}
1125
 
 
1126
 
void MidiMapping::beginMidiLearn(MixxxControl control)
1127
 
{
1128
 
    m_mappingLock.lock();
1129
 
    //Save the internal control we're supposed to map/remap. After this we have
1130
 
    //to wait for the user to push a button on their controller, which should
1131
 
    //give us a MidiMessage via finishMidiLearn().
1132
 
    m_controlToLearn = control;
1133
 
 
1134
 
    qDebug() << "MidiMapping: Learning started!";
1135
 
 
1136
 
    m_mappingLock.unlock();
1137
 
 
1138
 
    //Notify the MIDI device class that it should feed us the next MIDI message...
1139
 
    emit(midiLearningStarted());
1140
 
}
1141
 
 
1142
 
void MidiMapping::cancelMidiLearn()
1143
 
{
1144
 
    m_mappingLock.lock();
1145
 
    m_controlToLearn = MixxxControl();
1146
 
    m_mappingLock.unlock();
1147
 
}
1148
 
 
1149
 
#ifdef __MIDISCRIPT__
1150
 
void MidiMapping::restartScriptEngine()
1151
 
{
1152
 
    //Note: Locking occurs inside the functions below.
1153
 
    shutdownScriptEngine();
1154
 
    startupScriptEngine();
1155
 
}
1156
 
#endif
1157
 
 
1158
 
// Reset the MIDI controller
1159
 
void MidiMapping::reset() {
1160
 
#ifdef __MIDISCRIPT__   // Can't ifdef slots in the .h file, so we just do the body.
1161
 
    restartScriptEngine();
1162
 
#endif
1163
 
    MidiLedHandler::destroyHandlers(m_pOutputMidiDevice);
1164
 
 
1165
 
    applyPreset();
1166
 
}
1167
 
 
1168
 
void MidiMapping::slotScriptEngineReady() {
1169
 
#ifdef __MIDISCRIPT__   // Can't ifdef slots in the .h file, so we just do the body.
1170
 
 
1171
 
    // The lock prevents us from waking before the main thread is waiting on the
1172
 
    // condition.
1173
 
    m_scriptEngineInitializedMutex.lock();
1174
 
    m_scriptEngineInitializedCondition.wakeAll();
1175
 
    m_scriptEngineInitializedMutex.unlock();
1176
 
 
1177
 
#endif
1178
 
}