Subversion Repositories svn.mios32

Rev

Rev 1542 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
935 tk 1
/* -*- mode:C++; tab-width:4; c-basic-offset:4; indent-tabs-mode:nil -*- */
2
// $Id: MbCvTool.cpp 1724 2013-03-28 20:23:40Z tk $
3
/*
4
 * MIDIbox CV Tool Window
5
 *
6
 * ==========================================================================
7
 *
8
 *  Copyright (C) 2010 Thorsten Klose (tk@midibox.org)
9
 *  Licensed for personal non-commercial use only.
10
 *  All other rights reserved.
11
 *
12
 * ==========================================================================
13
 */
14
 
15
#include "MbCvTool.h"
16
#include "MiosStudio.h"
17
 
18
 
19
MbCvToolConfigGlobals::MbCvToolConfigGlobals()
20
{
21
    addAndMakeVisible(mergerLabel = new Label(String::empty, T("MIDI Merger:")));
22
    mergerLabel->setJustificationType(Justification::right);
23
    addAndMakeVisible(mergerComboBox = new ComboBox(String::empty));
24
    mergerComboBox->setWantsKeyboardFocus(true);
25
    mergerComboBox->addItem(T("Disabled"), 1);
26
    mergerComboBox->addItem(T("Enabled (received MIDI events forwarded to MIDI Out)"), 2);
1134 tk 27
    mergerComboBox->addItem(T("MIDIbox Link Endpoint (last core in the MIDIbox chain)"), 3);
28
    mergerComboBox->addItem(T("MIDIbox Link Forwarding Point (core in a MIDIbox chain)"), 4);
935 tk 29
    mergerComboBox->setSelectedId(1, true);
30
 
31
    addAndMakeVisible(clockDividerLabel = new Label(String::empty, T("Output Clock Divider:")));
32
    clockDividerLabel->setJustificationType(Justification::right);
33
    addAndMakeVisible(clockDividerComboBox = new ComboBox(String::empty));
34
    clockDividerComboBox->setWantsKeyboardFocus(true);
35
    clockDividerComboBox->addItem(T("96 ppqn"), 1);
36
    clockDividerComboBox->addItem(T("48 ppqn"), 2);
37
    clockDividerComboBox->addItem(T("32 ppqn"), 3);
38
    clockDividerComboBox->addItem(T("24 ppqn"), 4);
39
    for(int i=2; i<=16; ++i)
40
        clockDividerComboBox->addItem(T("24 ppqn / " + String(i)), 5+i-2);
41
    clockDividerComboBox->setSelectedId(4, true);
42
 
43
    addAndMakeVisible(nameLabel = new Label(String::empty, T("Patch Name:")));
44
    nameLabel->setJustificationType(Justification::right);
45
    addAndMakeVisible(nameEditor = new TextEditor(String::empty));
46
    nameEditor->setTextToShowWhenEmpty(T("<No Name>"), Colours::grey);
47
    nameEditor->setInputRestrictions(16);
48
}
49
 
50
 
51
MbCvToolConfigGlobals::~MbCvToolConfigGlobals()
52
{
53
}
54
 
55
//==============================================================================
56
void MbCvToolConfigGlobals::resized()
57
{
58
    int labelX = 10;
59
    int labelY0 = 10;
60
    int labelYOffset = 28;
61
    int labelWidth = 250;
62
    int labelHeight = 24;
63
 
64
    int controlX = labelX + labelWidth + 20;
65
    int controlY0 = labelY0;
66
    int controlYOffset = labelYOffset;
67
    int controlWidth = getWidth()-labelWidth-40-2*labelX;
68
    int controlHeight = labelHeight;
69
 
1131 tk 70
    nameLabel->setBounds(labelX, labelY0 + 0*labelYOffset, labelWidth, labelHeight);
71
    nameEditor->setBounds(controlX, controlY0 + 0*controlYOffset, controlWidth, controlHeight);
935 tk 72
 
1131 tk 73
    mergerLabel->setBounds(labelX, labelY0 + 1*labelYOffset, labelWidth, labelHeight);
74
    mergerComboBox->setBounds(controlX, controlY0 + 1*controlYOffset, controlWidth, controlHeight);
935 tk 75
 
1131 tk 76
    clockDividerLabel->setBounds(labelX, labelY0 + 2*labelYOffset, labelWidth, labelHeight);
77
    clockDividerComboBox->setBounds(controlX, controlY0 + 2*controlYOffset, controlWidth, controlHeight);
935 tk 78
}
79
 
80
 
81
//==============================================================================
82
void MbCvToolConfigGlobals::getDump(Array<uint8> &syxDump)
83
{
84
    int merger = mergerComboBox->getSelectedId()-1;
85
    int clockDivider = clockDividerComboBox->getSelectedId()-1;
86
 
87
    syxDump.set(0x00, merger);
88
    syxDump.set(0x03, clockDivider & 0x7f);
89
 
90
    int i;
91
    String txt = nameEditor->getText();
92
    for(i=0; i<16 && i<txt.length(); ++i)
93
        syxDump.set(0xf0 + i, txt[i]);
94
    while( i < 16 ) {
95
        syxDump.set(0xf0 + i, 0);
96
        i++;
97
    }
98
}
99
 
100
void MbCvToolConfigGlobals::setDump(const Array<uint8> &syxDump)
101
{
102
    int merger = syxDump[0x00];
103
    int clockDivider = syxDump[0x03];
104
 
105
    mergerComboBox->setSelectedId(merger+1);
106
    clockDividerComboBox->setSelectedId(clockDivider+1);
107
 
108
    String txt;
109
    for(int i=0; i<16 && syxDump[0xf0+i]; ++i) {
110
        char dummy = syxDump[0xf0 + i];
111
        txt += String(&dummy, 1);
112
    }
113
    nameEditor->setText(txt);
114
}
115
 
116
 
117
//==============================================================================
118
//==============================================================================
119
//==============================================================================
120
MbCvToolConfigChannels::MbCvToolConfigChannels()
121
    : font(14.0f)
122
    , numRows(8)
123
{
124
    for(int cv=0; cv<numRows; ++cv) {
125
        cvChannel.add(1+cv); // Default MIDI Channel
126
        cvEvent.add(1); // Note
127
        cvLegato.add(1); // enabled
128
        cvPoly.add(0); // disabled
129
        cvGateInverted.add(0); // disabled
130
        cvPitchrange.add(2);
131
        cvSplitLower.add(0x00+1);
132
        cvSplitUpper.add(0x7f+1);
133
        cvTransposeOctave.add(0);
134
        cvTransposeSemitones.add(0);
135
        cvCcNumber.add(0x10 + cv);
136
        cvCurve.add(1);
137
        cvSlewRate.add(1);
138
    }
139
 
140
    addAndMakeVisible(table = new TableListBox(T("CV Channels Table"), this));
141
    table->setColour(ListBox::outlineColourId, Colours::grey);
142
    table->setOutlineThickness(1);
143
 
1187 tk 144
    table->getHeader().addColumn(T("CV"), 1, 25);
145
    table->getHeader().addColumn(T("Channel"), 2, 70);
146
    table->getHeader().addColumn(T("Event"), 3, 100);
147
    table->getHeader().addColumn(T("Legato"), 4, 40);
148
    table->getHeader().addColumn(T("Poly"), 5, 30);
149
    table->getHeader().addColumn(T("Inv.Gate"), 6, 50);
150
    table->getHeader().addColumn(T("Pitchrange"), 7, 65);
151
    table->getHeader().addColumn(T("Lower"), 8, 50);
152
    table->getHeader().addColumn(T("Upper"), 9, 50);
153
    table->getHeader().addColumn(T("Tr.Octave"), 10, 70);
154
    table->getHeader().addColumn(T("Tr.Semitones"), 11, 70);
155
    table->getHeader().addColumn(T("CC Number"), 12, 70);
156
    table->getHeader().addColumn(T("Curve"), 13, 80);
157
    table->getHeader().addColumn(T("Slew Rate"), 14, 70);
935 tk 158
 
159
    setSize(800, 200);
160
}
161
 
162
MbCvToolConfigChannels::~MbCvToolConfigChannels()
163
{
164
}
165
 
166
//==============================================================================
167
int MbCvToolConfigChannels::getNumRows()
168
{
169
    return numRows;
170
}
171
 
172
void MbCvToolConfigChannels::paintRowBackground(Graphics &g, int rowNumber, int width, int height, bool rowIsSelected)
173
{
174
    if( rowIsSelected )
175
        g.fillAll(Colours::lightblue);
176
}
177
 
178
void MbCvToolConfigChannels::paintCell(Graphics &g, int rowNumber, int columnId, int width, int height, bool rowIsSelected)
179
{
180
    g.setColour(Colours::black);
181
    g.setFont(font);
182
 
183
    switch( columnId ) {
184
    case 1: {
185
        const String text(rowNumber);
186
        g.drawText(text, 2, 0, width - 4, height, Justification::centred, true);
187
    } break;
188
    }
189
 
190
    g.setColour(Colours::black.withAlpha (0.2f));
191
    g.fillRect(width - 1, 0, 1, height);
192
}
193
 
194
 
195
Component* MbCvToolConfigChannels::refreshComponentForCell(int rowNumber, int columnId, bool isRowSelected, Component* existingComponentToUpdate)
196
{
197
    switch( columnId ) {
198
    case 2: {
199
        ConfigTableSlider *slider = (ConfigTableSlider *)existingComponentToUpdate;
200
 
201
        if( slider == 0 ) {
202
            slider = new ConfigTableSlider(*this);
203
            slider->setRange(1, 16);
204
        }
205
 
206
        slider->setRowAndColumn(rowNumber, columnId);
207
        return slider;
208
    } break;
209
 
210
    case 3: {
211
        ConfigTableComboBox *comboBox = (ConfigTableComboBox *)existingComponentToUpdate;
212
 
213
        if( comboBox == 0 ) {
214
            comboBox = new ConfigTableComboBox(*this);
215
            comboBox->addItem(T("Note"), 1);
216
            comboBox->addItem(T("Velocity"), 2);
217
            comboBox->addItem(T("Aftertouch"), 3);
218
            comboBox->addItem(T("Controller (CC)"), 4);
219
            comboBox->addItem(T("NRPN"), 5);
220
            comboBox->addItem(T("Pitch Bender"), 6);
221
        }
222
 
223
        comboBox->setRowAndColumn(rowNumber, columnId);
224
        return comboBox;
225
    } break;
226
 
227
    case 4:
228
    case 5:
229
    case 6: {
230
        ConfigTableToggleButton *toggleButton = (ConfigTableToggleButton *)existingComponentToUpdate;
231
 
232
        if( toggleButton == 0 )
233
            toggleButton = new ConfigTableToggleButton(*this);
234
 
235
        toggleButton->setRowAndColumn(rowNumber, columnId);
236
        return toggleButton;
237
    } break;
238
 
239
    case 7: {
240
        ConfigTableSlider *slider = (ConfigTableSlider *)existingComponentToUpdate;
241
 
242
        if( slider == 0 ) {
243
            slider = new ConfigTableSlider(*this);
244
            slider->setRange(0, 24);
245
        }
246
 
247
        slider->setRowAndColumn(rowNumber, columnId);
248
        return slider;
249
    } break;
250
 
251
    case 8:
252
    case 9: {
253
        ConfigTableComboBox *comboBox = (ConfigTableComboBox *)existingComponentToUpdate;
254
 
255
        if( comboBox == 0 ) {
256
            const char noteTab[12][3] = { "C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-" };
257
            comboBox = new ConfigTableComboBox(*this);
258
            comboBox->addItem(T("---"), 1);
259
            for(int i=1; i<128; ++i) {
260
                comboBox->addItem(String(noteTab[i%12]) + String((i/12)-2), i+1);
261
            }
262
        }
263
 
264
        comboBox->setRowAndColumn(rowNumber, columnId);
265
        return comboBox;
266
    } break;
267
 
268
    case 10:
269
    case 11: {
270
        ConfigTableSlider *slider = (ConfigTableSlider *)existingComponentToUpdate;
271
 
272
        if( slider == 0 ) {
273
            slider = new ConfigTableSlider(*this);
274
            slider->setRange(-8, 7);
275
        }
276
 
277
        slider->setRowAndColumn(rowNumber, columnId);
278
        return slider;
279
    } break;
280
 
281
    case 12: {
282
        ConfigTableSlider *slider = (ConfigTableSlider *)existingComponentToUpdate;
283
 
284
        if( slider == 0 ) {
285
            slider = new ConfigTableSlider(*this);
286
            slider->setRange(0, 127);
287
        }
288
 
289
        slider->setRowAndColumn(rowNumber, columnId);
290
        return slider;
291
    } break;
292
 
293
    case 13: {
294
        ConfigTableComboBox *comboBox = (ConfigTableComboBox *)existingComponentToUpdate;
295
 
296
        if( comboBox == 0 ) {
297
            comboBox = new ConfigTableComboBox(*this);
298
            comboBox->addItem(T("V/Octave"), 1);
299
            comboBox->addItem(T("Hz/V"), 2);
300
            comboBox->addItem(T("Inverted"), 3);
301
            comboBox->addItem(T("Exponential"), 4);
302
        }
303
 
304
        comboBox->setRowAndColumn(rowNumber, columnId);
305
        return comboBox;
306
    } break;
307
 
308
    case 14: {
309
        ConfigTableComboBox *comboBox = (ConfigTableComboBox *)existingComponentToUpdate;
310
 
311
        if( comboBox == 0 ) {
312
            comboBox = new ConfigTableComboBox(*this);
313
            comboBox->addItem(T("off"), 1);
314
            comboBox->addItem(T("1 mS"), 2);
315
            comboBox->addItem(T("2 mS"), 3);
316
            comboBox->addItem(T("4 mS"), 4);
317
            comboBox->addItem(T("8 mS"), 5);
318
            comboBox->addItem(T("16 mS"), 6);
319
            comboBox->addItem(T("32 mS"), 7);
320
            comboBox->addItem(T("64 mS"), 8);
321
        }
322
 
323
        comboBox->setRowAndColumn(rowNumber, columnId);
324
        return comboBox;
325
    } break;
326
    }
327
 
328
    return 0;
329
}
330
 
331
 
332
//==============================================================================
333
void MbCvToolConfigChannels::resized()
334
{
335
    // position our table with a gap around its edge
1724 tk 336
    table->setBoundsInset(BorderSize<int>(8));
935 tk 337
}
338
 
339
 
340
//==============================================================================
341
int MbCvToolConfigChannels::getTableValue(const int rowNumber, const int columnId)
342
{
343
    switch( columnId ) {
344
    case 2: return cvChannel[rowNumber];
345
    case 3: return cvEvent[rowNumber];
346
    case 4: return cvLegato[rowNumber];
347
    case 5: return cvPoly[rowNumber];
348
    case 6: return cvGateInverted[rowNumber];
349
    case 7: return cvPitchrange[rowNumber];
350
    case 8: return cvSplitLower[rowNumber];
351
    case 9: return cvSplitUpper[rowNumber];
352
    case 10: return cvTransposeOctave[rowNumber];
353
    case 11: return cvTransposeSemitones[rowNumber];
354
    case 12: return cvCcNumber[rowNumber];
355
    case 13: return cvCurve[rowNumber];
356
    case 14: return cvSlewRate[rowNumber];
357
    }
358
    return 0;
359
}
360
 
361
void MbCvToolConfigChannels::setTableValue(const int rowNumber, const int columnId, const int newValue)
362
{
363
    switch( columnId ) {
364
    case 2: cvChannel.set(rowNumber, newValue); break;
365
    case 3: cvEvent.set(rowNumber, newValue); break;
366
    case 4: cvLegato.set(rowNumber, newValue); break;
367
    case 5: cvPoly.set(rowNumber, newValue); break;
368
    case 6: cvGateInverted.set(rowNumber, newValue); break;
369
    case 7: cvPitchrange.set(rowNumber, newValue); break;
370
    case 8: cvSplitLower.set(rowNumber, newValue); break;
371
    case 9: cvSplitUpper.set(rowNumber, newValue); break;
372
    case 10: cvTransposeOctave.set(rowNumber, newValue); break;
373
    case 11: cvTransposeSemitones.set(rowNumber, newValue); break;
374
    case 12: cvCcNumber.set(rowNumber, newValue); break;
375
    case 13: cvCurve.set(rowNumber, newValue); break;
376
    case 14: cvSlewRate.set(rowNumber, newValue); break;
377
    }
378
}
379
 
380
 
381
//==============================================================================
382
void MbCvToolConfigChannels::getDump(Array<uint8> &syxDump)
383
{
384
    for(int cv=0; cv<numRows; ++cv) {
385
        syxDump.set(1*8+cv, (cvChannel[cv]-1) & 0xf);
386
        syxDump.set(2*8+cv, ((cvEvent[cv]-1) & 0xf) | (cvLegato[cv] << 4) | (cvPoly[cv] << 5));
387
        syxDump.set(3*8+cv, cvPitchrange[cv]);
388
        syxDump.set(4*8+cv, cvSplitLower[cv]-1);
389
        syxDump.set(5*8+cv, cvSplitUpper[cv]-1);
390
        syxDump.set(6*8+cv, (cvTransposeOctave[cv] >= 0) ? cvTransposeOctave[cv] : (cvTransposeOctave[cv]+16));
391
        syxDump.set(7*8+cv, (cvTransposeSemitones[cv] >= 0) ? cvTransposeSemitones[cv] : (cvTransposeSemitones[cv]+16));
392
        syxDump.set(8*8+cv, cvCcNumber[cv]);
393
        syxDump.set(9*8+cv, (cvCurve[cv]-1));
394
        syxDump.set(10*8+cv, (cvSlewRate[cv]-1));
395
 
396
        uint8 andMask = ~(1 << cv);
397
        uint8 orMask = cvGateInverted[cv] ? (1 << cv) : 0;
398
        syxDump.set(0x02, (syxDump[0x02] & andMask) | orMask);
399
    }
400
}
401
 
402
void MbCvToolConfigChannels::setDump(const Array<uint8> &syxDump)
403
{
404
    for(int cv=0; cv<numRows; ++cv) {
405
        cvChannel.set(cv, (syxDump[1*8+cv] & 0x0f)+1);
406
        cvEvent.set(cv, (syxDump[2*8+cv] & 0x0f)+1);
407
        cvLegato.set(cv, (syxDump[2*8+cv] & 0x10) ? 1 : 0);
408
        cvPoly.set(cv, (syxDump[2*8+cv] & 0x20) ? 1 : 0);
409
        cvPitchrange.set(cv, syxDump[3*8+cv]);
410
        cvSplitLower.set(cv, syxDump[4*8+cv]+1);
411
        cvSplitUpper.set(cv, syxDump[5*8+cv]+1);
412
        cvTransposeOctave.set(cv, (syxDump[6*8+cv] < 8) ? syxDump[6*8+cv] : syxDump[6*8+cv] - 16);
413
        cvTransposeSemitones.set(cv, (syxDump[7*8+cv] < 8) ? syxDump[7*8+cv] : syxDump[7*8+cv] - 16);
414
        cvCcNumber.set(cv, syxDump[8*8+cv]);
415
        cvCurve.set(cv, syxDump[9*8+cv]+1);
416
        cvSlewRate.set(cv, syxDump[10*8+cv]+1);
417
        cvGateInverted.set(cv, (syxDump[0x02] & (1 << cv)) ? 1 : 0);
418
    }
419
    table->updateContent();
420
}
421
 
422
//==============================================================================
423
//==============================================================================
424
//==============================================================================
425
MbCvToolConfig::MbCvToolConfig(MiosStudio *_miosStudio)
426
    : miosStudio(_miosStudio)
427
    , TabbedComponent(TabbedButtonBar::TabsAtTop)
428
{
429
    addTab(T("Channels"), Colour(0xfff0f0d0), configChannels = new MbCvToolConfigChannels(), true);
430
    addTab(T("Global"),   Colour(0xfff0f0e0), configGlobals = new MbCvToolConfigGlobals(), true);
431
    setSize(800, 200);
432
}
433
 
434
MbCvToolConfig::~MbCvToolConfig()
435
{
436
}
437
 
438
//==============================================================================
439
void MbCvToolConfig::getDump(Array<uint8> &syxDump)
440
{
441
    for(int addr=0x000; addr<0x460; ++addr)
442
        syxDump.set(addr, 0x00);
443
    for(int addr=0x460; addr<0x600; ++addr)
444
        syxDump.set(addr, 0x7f);
445
 
446
    configGlobals->getDump(syxDump);
447
    configChannels->getDump(syxDump);
448
}
449
 
450
void MbCvToolConfig::setDump(const Array<uint8> &syxDump)
451
{
452
    configGlobals->setDump(syxDump);
453
    configChannels->setDump(syxDump);
454
}
455
 
456
 
457
 
458
//==============================================================================
459
//==============================================================================
460
//==============================================================================
461
MbCvToolControl::MbCvToolControl(MiosStudio *_miosStudio, MbCvToolConfig *_mbCvToolConfig)
462
    : miosStudio(_miosStudio)
463
    , mbCvToolConfig(_mbCvToolConfig)
464
    , progress(0)
465
    , receiveDump(false)
466
    , dumpRequested(false)
467
    , dumpReceived(false)
468
    , checksumError(false)
469
    , dumpSent(false)
470
{
1131 tk 471
    addAndMakeVisible(loadButton = new TextButton(T("Load")));
1502 tk 472
    loadButton->addListener(this);
935 tk 473
 
1131 tk 474
    addAndMakeVisible(saveButton = new TextButton(T("Save")));
1502 tk 475
    saveButton->addListener(this);
935 tk 476
 
477
    addAndMakeVisible(deviceIdLabel = new Label(T("Device ID"), T("Device ID:")));
478
    deviceIdLabel->setJustificationType(Justification::right);
479
 
480
    addAndMakeVisible(deviceIdSlider = new Slider(T("Device ID")));
481
    deviceIdSlider->setRange(0, 127, 1);
482
    deviceIdSlider->setSliderStyle(Slider::IncDecButtons);
1131 tk 483
    deviceIdSlider->setTextBoxStyle(Slider::TextBoxLeft, false, 30, 20);
935 tk 484
    deviceIdSlider->setDoubleClickReturnValue(true, 0);
485
 
486
    addAndMakeVisible(patchLabel = new Label(T("Patch"), T("Patch:")));
487
    patchLabel->setJustificationType(Justification::right);
488
 
489
    addAndMakeVisible(patchSlider = new Slider(T("Patch")));
490
    patchSlider->setRange(1, 128, 1);
491
    patchSlider->setSliderStyle(Slider::IncDecButtons);
1131 tk 492
    patchSlider->setTextBoxStyle(Slider::TextBoxLeft, false, 30, 20);
935 tk 493
    patchSlider->setDoubleClickReturnValue(true, 0);
494
 
1131 tk 495
    addAndMakeVisible(receiveButton = new TextButton(T("Receive")));
1502 tk 496
    receiveButton->addListener(this);
935 tk 497
 
1131 tk 498
    addAndMakeVisible(sendButton = new TextButton(T("Send")));
1502 tk 499
    sendButton->addListener(this);
935 tk 500
 
501
    addAndMakeVisible(progressBar = new ProgressBar(progress));
502
 
503
    // restore settings
1724 tk 504
    PropertiesFile *propertiesFile = MiosStudioProperties::getInstance()->getCommonSettings(true);
935 tk 505
    if( propertiesFile ) {
506
        deviceIdSlider->setValue(propertiesFile->getIntValue(T("mbCvDeviceId"), 0) & 0x7f);
507
        patchSlider->setValue(propertiesFile->getIntValue(T("mbCvPatch"), 0) & 0x7f);
508
        String syxFileName(propertiesFile->getValue(T("mbCvSyxFile"), String::empty));
509
        if( syxFileName != String::empty )
510
            syxFile = File(syxFileName);
511
    }
512
 
513
    setSize(800, 200);
514
}
515
 
516
MbCvToolControl::~MbCvToolControl()
517
{
518
}
519
 
520
//==============================================================================
521
void MbCvToolControl::paint (Graphics& g)
522
{
523
    g.fillAll(Colours::white);
524
}
525
 
526
void MbCvToolControl::resized()
527
{
528
    int buttonX0 = 10;
529
    int buttonXOffset = 80;
530
    int buttonY = 8;
531
    int buttonWidth = 72;
532
    int buttonHeight = 24;
533
 
534
    loadButton->setBounds(buttonX0 + 0*buttonXOffset, buttonY, buttonWidth, buttonHeight);
535
    saveButton->setBounds(buttonX0 + 1*buttonXOffset, buttonY, buttonWidth, buttonHeight);
536
 
537
    deviceIdLabel->setBounds(buttonX0 + 20+2*buttonXOffset, buttonY, buttonWidth, buttonHeight);
538
    deviceIdSlider->setBounds(buttonX0 + 20+3*buttonXOffset, buttonY, buttonWidth, buttonHeight);
539
 
540
    patchLabel->setBounds(buttonX0 + 20+4*buttonXOffset, buttonY, buttonWidth, buttonHeight);
541
    patchSlider->setBounds(buttonX0 + 20+5*buttonXOffset, buttonY, buttonWidth, buttonHeight);
542
 
543
    receiveButton->setBounds(buttonX0 + 40+6*buttonXOffset, buttonY, buttonWidth, buttonHeight);
544
    sendButton->setBounds(buttonX0 + 40+7*buttonXOffset, buttonY, buttonWidth, buttonHeight);
545
 
546
    int progressBarX = sendButton->getX() + buttonWidth + 20;
547
    progressBar->setBounds(progressBarX, buttonY, getWidth()-progressBarX-buttonX0, buttonHeight);
548
}
549
 
550
//==============================================================================
551
void MbCvToolControl::buttonClicked(Button* buttonThatWasClicked)
552
{
553
    if( buttonThatWasClicked == loadButton ) {
554
        FileChooser fc(T("Choose a .syx file that you want to open..."),
555
                       syxFile,
556
                       T("*.syx"));
557
 
558
        if( fc.browseForFileToOpen() ) {
559
            syxFile = fc.getResult();
560
            if( loadSyx(syxFile) ) {
1724 tk 561
                PropertiesFile *propertiesFile = MiosStudioProperties::getInstance()->getCommonSettings(true);
935 tk 562
                if( propertiesFile )
563
                    propertiesFile->setValue(T("mbCvSyxFile"), syxFile.getFullPathName());
564
            }
565
        }
566
    } else if( buttonThatWasClicked == saveButton ) {
567
        FileChooser fc(T("Choose a .syx file that you want to save..."),
568
                       syxFile,
569
                       T("*.syx"));
570
        if( fc.browseForFileToSave(true) ) {
571
            syxFile = fc.getResult();
572
            if( saveSyx(syxFile) ) {
1724 tk 573
                PropertiesFile *propertiesFile = MiosStudioProperties::getInstance()->getCommonSettings(true);
935 tk 574
                if( propertiesFile )
575
                    propertiesFile->setValue(T("mbCvSyxFile"), syxFile.getFullPathName());
576
            }
577
        }
578
    } else if( buttonThatWasClicked == sendButton || buttonThatWasClicked == receiveButton ) {
579
        loadButton->setEnabled(false);
580
        saveButton->setEnabled(false);
581
        sendButton->setEnabled(false);
582
        receiveButton->setEnabled(false);
583
        progress = 0;
1502 tk 584
        checksumError = false;
935 tk 585
        if( buttonThatWasClicked == receiveButton ) {
586
            dumpRequested = false;
587
            receiveDump = true;
588
            currentSyxDump.clear();
589
        } else {
590
            receiveDump = false;
591
            dumpSent = false;
592
            mbCvToolConfig->getDump(currentSyxDump);
593
        }
594
        startTimer(1);
595
    }
596
}
597
 
598
 
599
//==============================================================================
600
void MbCvToolControl::sliderValueChanged(Slider* slider)
601
{
602
    if( slider == deviceIdSlider ) {
603
        // store setting
1724 tk 604
        PropertiesFile *propertiesFile = MiosStudioProperties::getInstance()->getCommonSettings(true);
935 tk 605
        if( propertiesFile )
606
            propertiesFile->setValue(T("mbCvDeviceId"), (int)slider->getValue());
607
    } else if( slider == patchSlider ) {
608
        // store setting
1724 tk 609
        PropertiesFile *propertiesFile = MiosStudioProperties::getInstance()->getCommonSettings(true);
935 tk 610
        if( propertiesFile )
611
            propertiesFile->setValue(T("mbCvPatch"), (int)slider->getValue());
612
    }
613
}
614
 
615
 
616
//==============================================================================
617
void MbCvToolControl::timerCallback()
618
{
619
    stopTimer(); // will be restarted if required
620
 
621
    bool transferFinished = false;
622
 
623
    if( receiveDump ) {
624
        if( dumpRequested ) {
625
            if( !dumpReceived ) {
626
                transferFinished = true;
627
                AlertWindow::showMessageBox(AlertWindow::WarningIcon,
628
                                            T("No response from core."),
629
                                            T("Check:\n- MIDI In/Out connections\n- Device ID\n- that MIDIbox CV firmware has been uploaded"),
630
                                            String::empty);
631
            } else if( checksumError ) {
632
                transferFinished = true;
633
                AlertWindow::showMessageBox(AlertWindow::WarningIcon,
634
                                            T("Detected checksum error!"),
635
                                            T("Check:\n- MIDI In/Out connections\n- your MIDI interface"),
636
                                            String::empty);
637
            } else {
638
                mbCvToolConfig->setDump(currentSyxDump);
639
                transferFinished = true;
640
            }
641
        } else {
642
            dumpReceived = false;
643
            checksumError = false;
644
            dumpRequested = true;
645
            Array<uint8> data = SysexHelper::createMbCvReadPatch((uint8)deviceIdSlider->getValue(),
646
                                                                 (int)patchSlider->getValue()-1);
647
            MidiMessage message = SysexHelper::createMidiMessage(data);
648
            miosStudio->sendMidiMessage(message);
649
 
650
            progress = 1.0;
651
            startTimer(1000);
652
        }
653
    } else {
654
        if( dumpSent ) {
655
            transferFinished = true;
656
        } else {
657
            Array<uint8> data = SysexHelper::createMbCvWritePatch((uint8)deviceIdSlider->getValue(),
658
                                                                  (int)patchSlider->getValue()-1,
659
                                                                  &currentSyxDump.getReference(0));
660
            MidiMessage message = SysexHelper::createMidiMessage(data);
661
            miosStudio->sendMidiMessage(message);
662
 
663
            dumpSent = true;
664
            progress = 1.0;
665
            startTimer(1000);
666
        }
667
    }
668
 
669
    if( transferFinished ) {
670
        progress = 0;
671
        loadButton->setEnabled(true);
672
        saveButton->setEnabled(true);
673
        sendButton->setEnabled(true);
674
        receiveButton->setEnabled(true);
675
    }
676
}
677
 
678
//==============================================================================
679
void MbCvToolControl::handleIncomingMidiMessage(const MidiMessage& message, uint8 runningStatus)
680
{
1724 tk 681
    uint8 *data = (uint8 *)message.getRawData();
935 tk 682
    uint32 size = message.getRawDataSize();
683
 
684
    if( receiveDump ) {
685
        if( dumpRequested &&
686
            SysexHelper::isValidMbCvWritePatch(data, size, (int)deviceIdSlider->getValue()) &&
687
            data[7] == ((int)patchSlider->getValue()-1) ) {
688
            dumpReceived = true;
689
 
690
            if( size != 266 ) {
691
                checksumError = true;
692
            } else {
693
                uint8 checksum = 0x00;
694
                int pos;
695
                for(pos=8; pos<7+1+256; ++pos)
696
                    checksum += data[pos];
697
                if( data[pos] != (-(int)checksum & 0x7f) )
698
                    checksumError = true;
699
                else {
700
                    for(pos=8; pos<7+1+256; ++pos)
701
                        currentSyxDump.add(data[pos]);
702
                }
703
            }
704
 
705
            // trigger timer immediately
706
            stopTimer();
707
            startTimer(1);
708
        }
709
    } else if( isTimerRunning() ) {
710
        if( SysexHelper::isValidMbCvAcknowledge(data, size, (int)deviceIdSlider->getValue()) ) {
711
            // trigger timer immediately
712
            stopTimer();
713
            startTimer(1);
714
        }
715
    }
716
}
717
 
718
 
719
//==============================================================================
720
bool MbCvToolControl::loadSyx(File &syxFile)
721
{
722
    FileInputStream *inFileStream = syxFile.createInputStream();
723
 
1542 tk 724
    if( !inFileStream ) {
935 tk 725
        AlertWindow::showMessageBox(AlertWindow::WarningIcon,
726
                                    T("The file ") + syxFile.getFileName(),
727
                                    T("doesn't exist!"),
728
                                    String::empty);
729
        return false;
730
    } else if( inFileStream->isExhausted() || !inFileStream->getTotalLength() ) {
731
        AlertWindow::showMessageBox(AlertWindow::WarningIcon,
732
                                    T("The file ") + syxFile.getFileName(),
733
                                    T("is empty!"),
734
                                    String::empty);
735
        return false;
736
    }
737
 
738
    int64 size = inFileStream->getTotalLength();
739
    uint8 *buffer = (uint8 *)juce_malloc(size);
740
    int64 readNumBytes = inFileStream->read(buffer, size);
741
    int numValidPatches = 0;
742
    Array<uint8> syxDump;
743
    String errorMessage;
744
 
745
    if( readNumBytes != size ) {
746
        errorMessage = String(T("cannot be read completely!"));
747
    } else {
748
        for(int pos=0; pos<size; ++pos) {
749
            if( SysexHelper::isValidMbCvWritePatch(buffer+pos, size - pos, -1) ) {
750
                pos += 7;
751
 
752
                if( pos < size ) {
753
                    int patch = buffer[pos++]; // not relevant, will be ignored
754
                    int patchPos = 0;
755
                    uint8 checksum = 0x00;
756
                    while( pos < size && buffer[pos] != 0xf7 && patchPos < 256 ) {
757
                        uint8 b = buffer[pos++];
758
                        syxDump.set(patchPos, b);
759
                        checksum += b;
760
                        ++patchPos;
761
                    }
762
 
763
                    if( patchPos != 256 ) {
764
                        errorMessage = String::formatted(T("contains invalid data: patch %d not complete (only %d bytes)"), patch, patchPos);
765
                    } else {
766
                        checksum = (-(int)checksum) & 0x7f;
767
                        if( buffer[pos] != checksum ) {
768
                            errorMessage = String::formatted(T("contains invalid checksum in patch %d (expected %02X but read %02X)!"), patch, checksum, buffer[pos]);
769
                        } else {
770
                            ++pos;
771
                            ++numValidPatches;
772
                        }
773
                    }
774
                }
775
            }
776
        }
777
    }
778
 
779
    juce_free(buffer);
780
 
781
    if( errorMessage == String::empty ) {
782
        if( !numValidPatches )
783
            errorMessage = String(T("doesn't contain a valid patch!"));
784
    }
785
 
786
    if( errorMessage != String::empty ) {
787
        AlertWindow::showMessageBox(AlertWindow::WarningIcon,
788
                                    T("The file ") + syxFile.getFileName(),
789
                                    errorMessage,
790
                                    String::empty);
791
        return false;
792
    }
793
 
794
    mbCvToolConfig->setDump(syxDump);
795
 
796
    return true;
797
}
798
 
799
bool MbCvToolControl::saveSyx(File &syxFile)
800
{
801
    syxFile.deleteFile();
802
    FileOutputStream *outFileStream = syxFile.createOutputStream();
803
 
804
    if( !outFileStream || outFileStream->failedToOpen() ) {
805
        AlertWindow::showMessageBox(AlertWindow::WarningIcon,
806
                                    String::empty,
807
                                    T("File cannot be created!"),
808
                                    String::empty);
809
        return false;
810
    }
811
 
812
    Array<uint8> syxDump;
813
    mbCvToolConfig->getDump(syxDump);
814
 
815
    Array<uint8> data = SysexHelper::createMbCvWritePatch((uint8)deviceIdSlider->getValue(),
816
                                                          (int)patchSlider->getValue()-1,
817
                                                          &syxDump.getReference(0));
818
    outFileStream->write(&data.getReference(0), data.size());
819
 
820
    delete outFileStream;
821
 
822
    return true;
823
}
824
 
825
 
826
//==============================================================================
827
//==============================================================================
828
//==============================================================================
829
MbCvTool::MbCvTool(MiosStudio *_miosStudio)
830
    : miosStudio(_miosStudio)
831
{
832
    addAndMakeVisible(mbCvToolConfig = new MbCvToolConfig(miosStudio));
833
    addAndMakeVisible(mbCvToolControl = new MbCvToolControl(miosStudio, mbCvToolConfig));
834
 
835
    resizeLimits.setSizeLimits(100, 300, 2048, 2048);
836
    addAndMakeVisible(resizer = new ResizableCornerComponent(this, &resizeLimits));
837
 
936 tk 838
    setSize(860, 265);
935 tk 839
}
840
 
841
MbCvTool::~MbCvTool()
842
{
843
}
844
 
845
//==============================================================================
846
void MbCvTool::paint (Graphics& g)
847
{
848
    //g.fillAll(Colour(0xffc1d0ff));
849
    g.fillAll(Colours::white);
850
}
851
 
852
void MbCvTool::resized()
853
{
854
    mbCvToolControl->setBounds(0, 0, getWidth(), 40);
855
    mbCvToolConfig->setBounds(0, 40, getWidth(), getHeight()-40);
856
    resizer->setBounds(getWidth()-16, getHeight()-16, 16, 16);
857
}