~mhall119/ubuntu-app-reviews/qtranscribe

« back to all changes in this revision

Viewing changes to qtranscribe.cpp

  • Committer: App Bot
  • Author(s): Matt Pharoah (Exüberance)
  • Date: 2012-07-09 20:14:22 UTC
  • Revision ID: appbot@holba.ch-20120709201422-cmbglasydwxlxelx
Tags: upstream-1.0
Import upstream version 1.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  Copyright © 2012 Matt Pharoah (mr.exuberant@gmail.com)
 
3
 *
 
4
 *  This program is free software: you can redistribute it and/or modify
 
5
 *  it under the terms of the GNU General Public License as published by
 
6
 *  the Free Software Foundation, either version 3 of the License, or
 
7
 *  (at your option) any later version.
 
8
 *
 
9
 *  This program is distributed in the hope that it will be useful,
 
10
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 *  GNU General Public License for more details.
 
13
 *
 
14
 *  You should have received a copy of the GNU General Public License
 
15
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
16
 *
 
17
 */
 
18
 
 
19
#include "Dictation.hh"
 
20
#include "Joystick.hh"
 
21
#include "Actions.hh"
 
22
 
 
23
#include <cstdio>
 
24
#include <thread>
 
25
#include <mutex>
 
26
#include <chrono>
 
27
#include <cstdlib>
 
28
#include <fstream>
 
29
 
 
30
#include <QThread>
 
31
#include <QApplication>
 
32
#include <QMainWindow>
 
33
#include <QObject>
 
34
#include <QIcon>
 
35
#include <QFileDialog>
 
36
#include <QString>
 
37
#include <QMessageBox>
 
38
#include <QTimer>
 
39
#include <QModelIndex>
 
40
#include <QDialog>
 
41
 
 
42
#include "ui_mainWindow.h"
 
43
#include "ui_optionsWindow.h"
 
44
#include "ui_footPedalWindow.h"
 
45
#include "ui_command.h"
 
46
#include "ui_notConfiguredMessage.h"
 
47
#include "footPedalWidgetModels.hh"
 
48
#include "QHandler.hh"
 
49
 
 
50
static Joystick *J = NULL;
 
51
static Dictation *D = NULL;
 
52
 
 
53
static thread *waitForConnection = NULL;
 
54
volatile static bool joystickFound = false;
 
55
static mutex jsearch;
 
56
 
 
57
static Ui::MainWindow *win;
 
58
static Ui::optionsWindow *opWin;
 
59
static Ui::footPedalWindow *fpcWin;
 
60
 
 
61
static int EXTRAS_SIZE;
 
62
 
 
63
static volatile bool windowClosed = false;
 
64
static bool adjustingTimer = false;
 
65
static bool paused = true;
 
66
static bool pedalFocus = true;
 
67
static bool slowed = false;
 
68
 
 
69
static QIcon playIcon;
 
70
static QIcon pauseIcon;
 
71
 
 
72
static QHandler handler;
 
73
 
 
74
static mutex timeLock;
 
75
static mutex dictationLock;
 
76
static mutex joystickCommandLock;
 
77
 
 
78
static int PLAY_SKIPBACK = -1000;
 
79
static float SLOW_SPEED = 0.5;
 
80
 
 
81
static string configFile;
 
82
static string joystickFile;
 
83
 
 
84
static FootPedalListModel fplModel;
 
85
static PedalCommandTable fpctModel;
 
86
 
 
87
static Action pedalCommand[256];
 
88
 
 
89
static void trim(char *str) {
 
90
        int i = -1; while (str[++i] != '\0');
 
91
        while(str[--i] == ' ' || str[i] == '\t') str[i] = '\0';
 
92
}
 
93
 
 
94
static void saveConfigFile() {
 
95
        FILE *conf = fopen(configFile.c_str(), "w");
 
96
        if (conf != NULL) {
 
97
                fprintf(conf, "Rewind / Fast Forward Sound Effects = %d\n", (int) opWin->soundEffectsCheckbox->isChecked());
 
98
                fprintf(conf, "Playback Skip = %d\n", PLAY_SKIPBACK);
 
99
                fprintf(conf, "Rewind Speed = %d\n", Dictation::getRewindSpeed());
 
100
                fprintf(conf, "Fast Forward Speed = %d\n", Dictation::getFastForwardSpeed());
 
101
                fprintf(conf, "Slow Speed = %f\n", SLOW_SPEED);
 
102
                fclose(conf);
 
103
        }
 
104
}
 
105
 
 
106
static void notifyJoystickSelectionChanged(const QModelIndex &index) {
 
107
        QMetaObject::invokeMethod(fpcWin->deviceList, "setCurrentIndex", Qt::QueuedConnection, Q_ARG(QModelIndex, index));
 
108
        QMetaObject::invokeMethod(&handler, "deviceSelected", Qt::QueuedConnection, Q_ARG(QModelIndex, index), Q_ARG(QModelIndex, QModelIndex()));
 
109
}
 
110
 
 
111
class OWindow : public QMainWindow {
 
112
  public:
 
113
        OWindow(): QMainWindow() {};
 
114
  protected:
 
115
        void closeEvent(QCloseEvent *ev) {
 
116
                saveConfigFile();
 
117
        }
 
118
};
 
119
static OWindow *optionsWindow;
 
120
 
 
121
class CWindow : public QMainWindow {
 
122
  private:
 
123
        QBasicTimer refresh;
 
124
  public:
 
125
        CWindow() : QMainWindow() {};
 
126
        ~CWindow() {
 
127
                refresh.stop();
 
128
        }
 
129
  protected:
 
130
        void showEvent(QShowEvent *ev) {
 
131
                jsearch.lock();
 
132
                joystickFound = true;
 
133
                jsearch.unlock();
 
134
                pedalFocus = false;
 
135
                fplModel.recheck();
 
136
                if (J != NULL) {
 
137
                        char ID[258];
 
138
                        ID[0] = J->getPort() + 1;
 
139
                        try {
 
140
                                Joystick::getNameAndButtons(ID[0] - 1, &ID[2], 256, (unsigned char *) &ID[1]);
 
141
                                notifyJoystickSelectionChanged(fplModel.find(ID));
 
142
                        } catch (invalid_argument &e) {
 
143
                                notifyJoystickSelectionChanged(QModelIndex());
 
144
                        }
 
145
                } else {
 
146
                        QMetaObject::invokeMethod(fpcWin->deviceList, "setCurrentIndex", Qt::QueuedConnection, Q_ARG(QModelIndex, QModelIndex()));
 
147
                }
 
148
                refresh.start(1000, &fplModel);
 
149
        }
 
150
        void closeEvent(QCloseEvent *ev) {
 
151
                pedalFocus = true;
 
152
                refresh.stop();
 
153
                fpctModel.changeJoystick(0xff, 0); //causes the joystick polling for the config window to cease
 
154
        }
 
155
};
 
156
static CWindow *footPedalConfigWindow;
 
157
 
 
158
class Window : public QMainWindow {
 
159
  public:
 
160
        Window(): QMainWindow() {};
 
161
  protected:
 
162
        void closeEvent(QCloseEvent *ev) {
 
163
                saveConfigFile();
 
164
                optionsWindow->close();
 
165
                footPedalConfigWindow->close();
 
166
                windowClosed = true;
 
167
        }
 
168
};
 
169
static Window *root;
 
170
 
 
171
static Action getCommand(const char *msg, Action def) {
 
172
        Ui::dialogBox commandView;
 
173
        QDialog dial;
 
174
        commandView.setupUi(&dial);
 
175
        commandView.comment->setText(QString(msg));
 
176
        for (int a = 0; a < NUM_ACTIONS; a++) commandView.commandBox->addItem(QString(ACTION_STRING(ACTION_NUM(a))), QVariant(a));
 
177
        commandView.commandBox->setCurrentIndex(def);
 
178
        if (dial.exec() == QDialog::Accepted) {
 
179
                return ACTION_NUM(commandView.commandBox->currentIndex());
 
180
        } else return def;
 
181
}
 
182
 
 
183
/*
 
184
 * IMPORTANT! Remember to close IF_OPEN with ENDIF_OPEN and ensure
 
185
 * that control will always reach ENDIF_OPEN to prevent deadlock
 
186
 */
 
187
#define IF_OPEN \
 
188
        dictationLock.lock(); \
 
189
        if (D == NULL) { \
 
190
                dictationLock.unlock(); \
 
191
                return; \
 
192
        }
 
193
#define ENDIF_OPEN dictationLock.unlock();
 
194
 
 
195
static inline void clearPedalInfo() {
 
196
        for (short i = 0; i < 256; i++) pedalCommand[i] = A_NOOP;
 
197
        IF_OPEN
 
198
        D->setSpeed(1.f);
 
199
        D->stopRewind();
 
200
        D->stopFastForward();
 
201
        D->pause();
 
202
        slowed = false;
 
203
        ENDIF_OPEN
 
204
}
 
205
 
 
206
static void eventListener(const JSEvent &ev, void *userdata) {
 
207
        IF_OPEN
 
208
        joystickCommandLock.lock();
 
209
        if (pedalFocus && !adjustingTimer && ev.type == Joystick::BUTTON_EVENT && (ev.value == Joystick::RELEASED || ev.value == Joystick::PRESSED)) {
 
210
                if (ev.value == Joystick::PRESSED) {
 
211
                        switch (pedalCommand[ev.button]) {
 
212
                                case A_PLAY: D->play(); break;
 
213
                                case A_REWIND: D->startRewind(); break;
 
214
                                case A_FAST_FORWARD: D->startFastForward(); break;
 
215
                                case A_SLOW_TOGGLE: if (slowed) {D->setSpeed(1.f);} else D->setSpeed(SLOW_SPEED); slowed = !slowed; break;
 
216
                                case A_SLOW_PLAY: D->setSpeed(SLOW_SPEED); D->play(); break;
 
217
                                case A_BACK1: D->skipForward(-1000); break;
 
218
                                case A_BACK5: D->skipForward(-5000); break;
 
219
                                case A_BACK10: D->skipForward(-10000); break;
 
220
                                default: break; //make the compiler happy
 
221
                        }
 
222
                } else {
 
223
                        switch (pedalCommand[ev.button]) {
 
224
                                case A_PLAY: D->pause(); break;
 
225
                                case A_REWIND: D->stopRewind(); break;
 
226
                                case A_FAST_FORWARD: D->stopFastForward(); break;
 
227
                                case A_SLOW_PLAY: D->setSpeed(1.f); D->pause(); break;
 
228
                                default: break; //make the compiler happy
 
229
                        }
 
230
                }
 
231
        }
 
232
        joystickCommandLock.unlock();
 
233
        ENDIF_OPEN
 
234
}
 
235
 
 
236
/*
 
237
 * returns true iff the file could be opened (even if the file is empty)
 
238
 * If name is non-null, copies the name of the joystick into name, or an
 
239
 * empty string if the joystick name could not be loaded from the file
 
240
 */
 
241
static bool loadFootpedalConfig(char *name = NULL) {
 
242
        if (J != NULL) {
 
243
                delete J;
 
244
                J = NULL;
 
245
        }
 
246
        if (name != NULL) name[0] = '\0';
 
247
        FILE *jfile = fopen(joystickFile.c_str(), "r");
 
248
        if (jfile != NULL) {
 
249
                char jname[257]; char line[256];
 
250
                if (fgets(jname, 257, jfile) == NULL) return true;
 
251
                for (short i = 0; i < 255; i++) {
 
252
                        if (jname[i] == '\n') {
 
253
                                jname[i] = '\0';
 
254
                                break;
 
255
                        }
 
256
                }
 
257
                jname[255] = '\0';
 
258
                if (name != NULL) strcpy(name, jname);
 
259
                if (fgets(line, 256, jfile) == NULL) return true;
 
260
                unsigned char jbuttons = atoi(line);
 
261
                char id[256]; unsigned char b;
 
262
                joystickCommandLock.lock();
 
263
                for (unsigned char i = 0; i <= 9; i++) {
 
264
                        try {
 
265
                                Joystick::getNameAndButtons(i, id, 256, &b);
 
266
                                if (jbuttons == b && strcmp(jname, id) == 0) {
 
267
                                        J = new Joystick(i, eventListener);
 
268
                                        break;
 
269
                                }
 
270
                        } catch (const invalid_argument& e) {};
 
271
                }
 
272
                for (unsigned char i = 0; i < jbuttons; i++) {
 
273
                        if (fgets(line, 256, jfile) != NULL) {
 
274
                                pedalCommand[i] = ACTION_NUM(atoi(line));
 
275
                                fpctModel.changeCommand(i, pedalCommand[i]);
 
276
                        } else {
 
277
                                pedalCommand[i] = A_NOOP;
 
278
                        }
 
279
                }
 
280
                for (unsigned short i = jbuttons; i < 256; i++) pedalCommand[i] = A_NOOP;
 
281
                joystickCommandLock.unlock();
 
282
                return true;
 
283
        } else return false;
 
284
}
 
285
 
 
286
/*
 
287
 * Qt Main Window Widget Listeners
 
288
 */
 
289
static bool playingBeforeAdjust;
 
290
 
 
291
void QHandler::timeSliderPressed() {
 
292
        IF_OPEN
 
293
        playingBeforeAdjust = !D->isPaused();
 
294
        D->pause();
 
295
        adjustingTimer = true;
 
296
        ENDIF_OPEN
 
297
}
 
298
 
 
299
void QHandler::timeSliderReleased() {
 
300
        IF_OPEN
 
301
        timeLock.lock();
 
302
        D->setPositionMilliseconds((unsigned int) win->timeSlider->sliderPosition());
 
303
        if (playingBeforeAdjust) D->play();
 
304
        adjustingTimer = false;
 
305
        timeLock.unlock();
 
306
        ENDIF_OPEN
 
307
}
 
308
 
 
309
void QHandler::playPauseButtonClicked() {
 
310
        IF_OPEN
 
311
        if (D->isPaused()) {
 
312
                D->skipForward(PLAY_SKIPBACK);
 
313
                D->play();
 
314
        } else {
 
315
                D->pause();
 
316
        }
 
317
        ENDIF_OPEN
 
318
}
 
319
 
 
320
void QHandler::restartButtonClicked() {
 
321
        IF_OPEN
 
322
        D->setPositionMilliseconds(0);
 
323
        ENDIF_OPEN
 
324
}
 
325
 
 
326
void QHandler::back10ButtonClicked() {
 
327
        IF_OPEN
 
328
        D->skipForward(-10000);
 
329
        ENDIF_OPEN
 
330
}
 
331
 
 
332
void QHandler::back5ButtonClicked() {
 
333
        IF_OPEN
 
334
        D->skipForward(-5000);
 
335
        ENDIF_OPEN
 
336
}
 
337
 
 
338
void QHandler::slowButtonClicked() {
 
339
        IF_OPEN
 
340
        if (slowed) {
 
341
                D->setSpeed(1.0);
 
342
        } else {
 
343
                D->setSpeed(SLOW_SPEED);
 
344
        }
 
345
        slowed = !slowed;
 
346
        ENDIF_OPEN
 
347
}
 
348
 
 
349
void QHandler::rewindButtonPressed() {
 
350
        IF_OPEN
 
351
        D->startRewind();
 
352
        ENDIF_OPEN
 
353
}
 
354
 
 
355
void QHandler::rewindButtonReleased() {
 
356
        IF_OPEN
 
357
        D->stopRewind();
 
358
        ENDIF_OPEN
 
359
}
 
360
 
 
361
void QHandler::fastForwardButtonPressed() {
 
362
        IF_OPEN
 
363
        D->startFastForward();
 
364
        ENDIF_OPEN
 
365
}
 
366
 
 
367
void QHandler::fastForwardButtonReleased() {
 
368
        IF_OPEN
 
369
        D->stopFastForward();
 
370
        ENDIF_OPEN
 
371
}
 
372
 
 
373
void QHandler::openFileButtonClicked() {
 
374
        /*
 
375
         * I SUPPOSE I could rewrite the dictation class to support
 
376
         * changing audio files, but for now I just associate each
 
377
         * Dictation with only one file, then delete it and create
 
378
         * a new one when switching files. It's just easier to code,
 
379
         * and the inefficiency doesn't matter since it's not like
 
380
         * you're switching audio files every few milliseconds!
 
381
         */
 
382
        dictationLock.lock();
 
383
        string file = QFileDialog::getOpenFileName(NULL, QString(), QString(), tr("Supported Audio Files (*.ogg *.wav *.flac *.aiff *.aif *.aifc *.au *.snd *.iff *.8svx *.pvf *.caf);; All Files (*)")).toStdString();
 
384
 
 
385
        if (file != "") {
 
386
                timeLock.lock();
 
387
                delete D;
 
388
                try {
 
389
                        D = new Dictation(file.c_str());
 
390
                } catch (exception &e) {
 
391
                        QMessageBox::critical(root, QString("Error"), QString(e.what()));
 
392
                        D = NULL;
 
393
                        win->fileNameLabel->setText(QString("Nothing"));
 
394
                        win->positionLabel->setText(QString(" 0:00"));
 
395
                        win->durationLabel->setText(QString(" 0:00"));
 
396
                        win->timeSlider->setRange(0, 60000);
 
397
                        timeLock.unlock();
 
398
                        dictationLock.unlock();
 
399
                        return;
 
400
                }
 
401
 
 
402
                unsigned int len = D->getLengthMilliseconds() / 1000;
 
403
                char length[6];
 
404
                sprintf(length, "%2d:%02d", len / 60, len % 60);
 
405
                win->durationLabel->setText(length);
 
406
                win->timeSlider->setRange(0, D->getLengthMilliseconds());
 
407
                win->fileNameLabel->setText(QString(file.substr(file.find_last_of('/') + 1).c_str()));
 
408
                timeLock.unlock();
 
409
        }
 
410
        dictationLock.unlock();
 
411
}
 
412
 
 
413
void QHandler::optionsButtonClicked() {
 
414
        optionsWindow->show();
 
415
}
 
416
 
 
417
void QHandler::pedalConfigButtonClicked() {
 
418
        footPedalConfigWindow->show();
 
419
}
 
420
 
 
421
void QHandler::showExtrasButtonClicked() {
 
422
        const bool visibility = !win->extraStuff->isVisible();
 
423
        if (visibility) root->setFixedWidth(root->width() + EXTRAS_SIZE);
 
424
        win->extraStuff->setVisible(visibility);
 
425
        win->showExtrasButton->setChecked(visibility);
 
426
        if (!visibility) root->setFixedWidth(root->width() - EXTRAS_SIZE);
 
427
}
 
428
 
 
429
/*
 
430
 * Qt Options Window Widget Listeners
 
431
 */
 
432
 
 
433
void QHandler::joystickConfigButtonClicked() {
 
434
        footPedalConfigWindow->show();
 
435
}
 
436
 
 
437
void QHandler::soundEffectsCheckboxClicked(bool checked) {
 
438
        Dictation::setEffectsEnabled(checked);
 
439
}
 
440
 
 
441
void QHandler::skipOnPlayCheckboxClicked(bool checked) {
 
442
        opWin->skipOnPlayDelay->setEnabled(checked);
 
443
        if (checked) {
 
444
                PLAY_SKIPBACK = -1 * opWin->skipOnPlayDelay->value();
 
445
        } else {
 
446
                PLAY_SKIPBACK = 0;
 
447
        }
 
448
}
 
449
 
 
450
void QHandler::skipOnPlaySpinnerChanged(int ms) {
 
451
        PLAY_SKIPBACK = -1 * ms;
 
452
}
 
453
 
 
454
void QHandler::rewindSpeedSliderChanged(int exp) {
 
455
        int mult;
 
456
        if (exp == 0) {
 
457
                mult = 1;
 
458
        } else {
 
459
                mult = 2 << (exp - 1);
 
460
        }
 
461
        Dictation::setRewindSpeed(mult);
 
462
        char label[7];
 
463
        if (mult < 10) {
 
464
                sprintf(label, "   x%d", mult);
 
465
        } else if (mult < 100) {
 
466
                sprintf(label, "  x%d", mult);
 
467
        } else {
 
468
                sprintf(label, " x%d", mult);
 
469
        }
 
470
        opWin->rewindSpeedLabel->setText(QString(label));
 
471
}
 
472
 
 
473
void QHandler::fastForwardSpeedSliderChanged(int exp) {
 
474
        int mult;
 
475
        if (exp == 0) {
 
476
                mult = 1;
 
477
        } else {
 
478
                mult = 2 << (exp - 1);
 
479
        }
 
480
        Dictation::setFastForwardSpeed(mult);
 
481
        char label[7];
 
482
        if (mult < 10) {
 
483
                        sprintf(label, "   x%d", mult);
 
484
                } else if (mult < 100) {
 
485
                        sprintf(label, "  x%d", mult);
 
486
                } else {
 
487
                        sprintf(label, " x%d", mult);
 
488
                }
 
489
        opWin->fastForwardSpeedLabel->setText(QString(label));
 
490
}
 
491
 
 
492
void QHandler::slowSpeedSliderChanged(int percent) {
 
493
        SLOW_SPEED = ((float) percent) / (float) 100;
 
494
        char label[7]; char label2[5];
 
495
        sprintf(label, "  %d%%", percent); sprintf(label2, "%d%%", percent);
 
496
        opWin->slowSpeedLabel->setText(QString(label));
 
497
        opWin->slowSpeedSlider->setValue(percent);
 
498
        win->speedLabel->setText(QString(label2));
 
499
        win->slowDial->setValue(percent);
 
500
        IF_OPEN
 
501
        if (D->isSlowed()) D->setSpeed(SLOW_SPEED);
 
502
        ENDIF_OPEN
 
503
}
 
504
 
 
505
void QHandler::closeButtonClicked() {
 
506
        optionsWindow->close();
 
507
}
 
508
 
 
509
/*
 
510
 * Qt Footpedal Configuration Window Widget Listeners
 
511
 */
 
512
 
 
513
void QHandler::footPedalApply() {
 
514
        //update the footpedal config file
 
515
        FILE *conf = fopen(joystickFile.c_str(), "w");
 
516
        char info[258];
 
517
        fplModel.getStringID(fpcWin->deviceList->selectionModel()->currentIndex().row(), info);
 
518
        if (conf != NULL) {
 
519
                if (strcmp(info, "") != 0) {
 
520
                        fprintf(conf, "%s\n%d\n", &info[2], (unsigned char) info[1]);
 
521
                        for (unsigned short i = 0; i < (unsigned char) info[1]; i++) fprintf(conf, "%d\n", fpctModel.getCommand(i));
 
522
                }
 
523
                fclose(conf);
 
524
        }
 
525
        if (J != NULL) {
 
526
                delete J;
 
527
                J = NULL;
 
528
        }
 
529
        joystickCommandLock.lock();
 
530
        clearPedalInfo();
 
531
        if (strcmp(info, "") != 0) {
 
532
                J = new Joystick(info[0] - 1, eventListener);
 
533
                for (short i = 0; i < info[1]; i++) pedalCommand[i] = fpctModel.getCommand(i);
 
534
        }
 
535
        joystickCommandLock.unlock();
 
536
        footPedalConfigWindow->close();
 
537
}
 
538
 
 
539
void QHandler::footPedalCancel() {
 
540
        if (!loadFootpedalConfig()) clearPedalInfo();
 
541
        footPedalConfigWindow->close();
 
542
}
 
543
 
 
544
static char *selectedDeviceString;
 
545
void QHandler::deviceSelected(const QModelIndex &index, const QModelIndex &prev) {
 
546
        fplModel.getStringID(index.row(), selectedDeviceString);
 
547
        if (strcmp(selectedDeviceString, "") != 0) {
 
548
                fpctModel.changeJoystick(selectedDeviceString[0] - 1, selectedDeviceString[1]);
 
549
        } else {
 
550
                fpctModel.changeJoystick(0xff, 0);
 
551
        }
 
552
}
 
553
 
 
554
void QHandler::selectionValidCheck(const QModelIndex& dontcare, const QModelIndex& stilldontcare) {
 
555
        if (!fplModel.stringIDequals(fpcWin->deviceList->selectionModel()->currentIndex().row() , selectedDeviceString)) {
 
556
                notifyJoystickSelectionChanged(QModelIndex());
 
557
        }
 
558
}
 
559
 
 
560
void QHandler::commandChosen(const QModelIndex &index) {
 
561
        char msg[33];
 
562
        sprintf(msg, "Choose action for footpedal #%d", index.row() + 1);
 
563
        fpctModel.changeCommand(index, getCommand(msg, fpctModel.getCommand(index)));
 
564
}
 
565
 
 
566
//a'right, that's enough listeners!
 
567
 
 
568
class MThread : public QThread {
 
569
  public:
 
570
        MThread() : QThread() {}
 
571
  protected:
 
572
        void run() {
 
573
                unsigned int then = 0xffffffff;
 
574
                unsigned int now = 0;
 
575
                chrono::milliseconds tick(15);
 
576
 
 
577
                while (!windowClosed) {
 
578
                        dictationLock.lock();
 
579
                        timeLock.lock();
 
580
                        if (D != NULL) {
 
581
                                now = D->getPositionMilliseconds() / 1000;
 
582
 
 
583
                                if (paused xor D->isPaused()) {
 
584
                                        paused = D->isPaused();
 
585
                                        if (paused) {
 
586
                                                win->playPauseButton->setIcon(playIcon);
 
587
                                        } else {
 
588
                                                win->playPauseButton->setIcon(pauseIcon);
 
589
                                        }
 
590
                                }
 
591
                                win->slowButton->setChecked(D->isSlowed());
 
592
                                win->rewindButton->setChecked(D->isRewinding());
 
593
                                win->fastForwardButton->setChecked(D->isFastForwarding());
 
594
 
 
595
                                if (adjustingTimer) {
 
596
                                        unsigned int time = win->timeSlider->sliderPosition() / 1000;
 
597
                                        char ctime[6];
 
598
                                        sprintf(ctime, "%2d:%02d", time / 60, time % 60);
 
599
                                        win->positionLabel->setText(ctime);
 
600
                                        win->positionLabel->update();
 
601
                                } else {
 
602
                                        if (now != then) {
 
603
                                                char ctime[6];
 
604
                                                sprintf(ctime, "%2d:%02d", now / 60, now % 60);
 
605
                                                win->positionLabel->setText(ctime);
 
606
                                                win->positionLabel->update();
 
607
                                                then = now;
 
608
                                        }
 
609
 
 
610
                                        win->timeSlider->setSliderPosition(D->getPositionMilliseconds());
 
611
                                        win->timeSlider->update();
 
612
                                }
 
613
                        }
 
614
                        timeLock.unlock();
 
615
                        dictationLock.unlock();
 
616
 
 
617
                        this_thread::sleep_for(tick);
 
618
                }
 
619
        }
 
620
};
 
621
 
 
622
static void doConnectionWait(char *jname) {
 
623
        /*
 
624
         * If no joystick is connected when the program starts but there is a
 
625
         * joystick named in the configuration file, keep scanning ports to see if
 
626
         * the joystick becomes connected until either it is found, or the user
 
627
         * opens the joystick configuration window to select one manually
 
628
         */
 
629
        chrono::milliseconds ONE_SECOND(1000);
 
630
        char otherName[256];
 
631
        while (!joystickFound) {
 
632
                for (unsigned char i = 0; i <= 9; i++) {
 
633
                        try {
 
634
                                Joystick::getNameAndButtons(i, otherName, 256);
 
635
                        } catch (const invalid_argument &e) {
 
636
                                continue;
 
637
                        }
 
638
                        if (strcmp(otherName, jname) == 0) {
 
639
                                jsearch.lock();
 
640
                                if (!joystickFound) J = new Joystick(i, eventListener);
 
641
                                jsearch.unlock();
 
642
                                break;
 
643
                        }
 
644
                }
 
645
                this_thread::sleep_for(ONE_SECOND);
 
646
        }
 
647
        //clean up self
 
648
        joystickFound = true;
 
649
        waitForConnection->detach();
 
650
        delete waitForConnection;
 
651
}
 
652
 
 
653
 
 
654
int main() {
 
655
        playIcon.addFile(QString::fromUtf8(":/res/svg/play.svg"));
 
656
        pauseIcon.addFile(QString::fromUtf8(":/res/svg/pause.svg"));
 
657
        selectedDeviceString = new char[258]; selectedDeviceString[0] = '\0';
 
658
 
 
659
        configFile = getenv("HOME");
 
660
        configFile += "/.config/qtranscribe/settings.conf";
 
661
        joystickFile = getenv("HOME");
 
662
        joystickFile += "/.config/qtranscribe/footpedal.conf";
 
663
 
 
664
        clearPedalInfo();
 
665
 
 
666
        int zero = 0;
 
667
        QApplication app(zero, (char **) 0);
 
668
 
 
669
        root = new Window();
 
670
        win = new Ui::MainWindow();
 
671
        win->setupUi(root);
 
672
        EXTRAS_SIZE = win->extraStuff->width() + 2;
 
673
        win->extraStuff->setVisible(false);
 
674
        root->setFixedSize(root->width() - EXTRAS_SIZE, root->height());
 
675
 
 
676
        optionsWindow = new OWindow();
 
677
        opWin = new Ui::optionsWindow();
 
678
        opWin->setupUi(optionsWindow);
 
679
 
 
680
        footPedalConfigWindow = new CWindow();
 
681
        fpcWin = new Ui::footPedalWindow();
 
682
        fpcWin->setupUi(footPedalConfigWindow);
 
683
        fpcWin->deviceList->setModel(&fplModel);
 
684
        fpcWin->mappingTable->setModel(&fpctModel);
 
685
        fpcWin->mappingTable->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
 
686
 
 
687
        //main window widget events
 
688
        QObject::connect(win->timeSlider, SIGNAL(sliderPressed()), &handler, SLOT(timeSliderPressed()));
 
689
        QObject::connect(win->timeSlider, SIGNAL(sliderReleased()), &handler, SLOT(timeSliderReleased()));
 
690
 
 
691
        QObject::connect(win->playPauseButton, SIGNAL(clicked()), &handler, SLOT(playPauseButtonClicked()));
 
692
        QObject::connect(win->restartButton, SIGNAL(clicked()), &handler, SLOT(restartButtonClicked()));
 
693
        QObject::connect(win->back10Button, SIGNAL(clicked()), &handler, SLOT(back10ButtonClicked()));
 
694
        QObject::connect(win->back5Button, SIGNAL(clicked()), &handler, SLOT(back5ButtonClicked()));
 
695
        QObject::connect(win->slowButton, SIGNAL(clicked()), &handler, SLOT(slowButtonClicked()));
 
696
        QObject::connect(win->rewindButton, SIGNAL(pressed()), &handler, SLOT(rewindButtonPressed()));
 
697
        QObject::connect(win->rewindButton, SIGNAL(released()), &handler, SLOT(rewindButtonReleased()));
 
698
        QObject::connect(win->fastForwardButton, SIGNAL(pressed()), &handler, SLOT(fastForwardButtonPressed()));
 
699
        QObject::connect(win->fastForwardButton, SIGNAL(released()), &handler, SLOT(fastForwardButtonReleased()));
 
700
        QObject::connect(win->slowDial, SIGNAL(valueChanged(int)), &handler, SLOT(slowSpeedSliderChanged(int)));
 
701
 
 
702
        QObject::connect(win->openFileButton, SIGNAL(clicked()), &handler, SLOT(openFileButtonClicked()));
 
703
        QObject::connect(win->optionsButtons, SIGNAL(clicked()), &handler, SLOT(optionsButtonClicked()));
 
704
        QObject::connect(win->pedalConfigButton, SIGNAL(clicked()), &handler, SLOT(pedalConfigButtonClicked()));
 
705
        QObject::connect(win->showExtrasButton, SIGNAL(clicked()), &handler, SLOT(showExtrasButtonClicked()));
 
706
 
 
707
        //options window widget events
 
708
        QObject::connect(opWin->joystickConfigButton, SIGNAL(clicked()), &handler, SLOT(joystickConfigButtonClicked()));
 
709
        QObject::connect(opWin->closeButton, SIGNAL(clicked()), &handler, SLOT(closeButtonClicked()));
 
710
 
 
711
        QObject::connect(opWin->soundEffectsCheckbox, SIGNAL(toggled(bool)), &handler, SLOT(soundEffectsCheckboxClicked(bool)));
 
712
 
 
713
        QObject::connect(opWin->toggleSkipOnPlay, SIGNAL(toggled(bool)), &handler, SLOT(skipOnPlayCheckboxClicked(bool)));
 
714
        QObject::connect(opWin->skipOnPlayDelay, SIGNAL(valueChanged(int)), &handler, SLOT(skipOnPlaySpinnerChanged(int)));
 
715
 
 
716
        QObject::connect(opWin->rewindSpeedSlider, SIGNAL(valueChanged(int)), &handler, SLOT(rewindSpeedSliderChanged(int)));
 
717
        QObject::connect(opWin->fastForwardSpeedSlider, SIGNAL(valueChanged(int)), &handler, SLOT(fastForwardSpeedSliderChanged(int)));
 
718
        QObject::connect(opWin->slowSpeedSlider, SIGNAL(valueChanged(int)), &handler, SLOT(slowSpeedSliderChanged(int)));
 
719
 
 
720
        //footpedal config window widget events
 
721
        QObject::connect(fpcWin->cancelButton, SIGNAL(clicked()), &handler, SLOT(footPedalCancel()));
 
722
        QObject::connect(fpcWin->OkayButton, SIGNAL(clicked()), &handler, SLOT(footPedalApply()));
 
723
 
 
724
        QObject::connect(fpcWin->deviceList->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), &handler, SLOT(deviceSelected(const QModelIndex&, const QModelIndex&)));
 
725
        QObject::connect(&fplModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), &handler, SLOT(selectionValidCheck(const QModelIndex&, const QModelIndex&)));
 
726
        QObject::connect(fpcWin->mappingTable, SIGNAL(activated(const QModelIndex&)), &handler, SLOT(commandChosen(const QModelIndex&)));
 
727
 
 
728
        //load options from configuration file
 
729
        fstream conf(configFile.c_str(), ios_base::in);
 
730
        char *line = new char[256];
 
731
        while (conf.good() && !conf.eof()) {
 
732
                conf.getline(line, 256, '=');
 
733
                trim(line);
 
734
                if (strcmp(line, "Playback Skip") == 0) {
 
735
                        int temp; conf >> temp;
 
736
                        if (conf.good() && temp <= 0 && temp >= -10000) {
 
737
                                PLAY_SKIPBACK = temp;
 
738
                                opWin->toggleSkipOnPlay->setChecked(temp != 0);
 
739
                                if (temp != 0) opWin->skipOnPlayDelay->setValue(-1 * temp);
 
740
                        }
 
741
                } else if (strcmp(line, "Rewind Speed") == 0) {
 
742
                        int temp; conf >> temp;
 
743
                        if (conf.good() && temp >= 1) {
 
744
                                int v = 0;
 
745
                                while (temp != 1) {
 
746
                                        v++;
 
747
                                        temp>>=1;
 
748
                                }
 
749
                                if (v <= 7) opWin->rewindSpeedSlider->setValue(v);
 
750
                        }
 
751
                } else if (strcmp(line, "Fast Forward Speed") == 0) {
 
752
                        int temp; conf >> temp;
 
753
                        if (conf.good() && temp >= 1) {
 
754
                                int v = 0;
 
755
                                while (temp != 1) {
 
756
                                        v++;
 
757
                                        temp>>=1;
 
758
                                }
 
759
                                if (v <= 7) opWin->fastForwardSpeedSlider->setValue(v);
 
760
                        }
 
761
                } else if (strcmp(line, "Slow Speed") == 0) {
 
762
                        float temp; conf >> temp;
 
763
                        if (conf.good() && temp >= 0.2 && temp <= 0.95) {
 
764
                                SLOW_SPEED = temp;
 
765
                                opWin->slowSpeedSlider->setValue((int) (100 * temp));
 
766
                                win->slowDial->setValue((int) (100 * temp));
 
767
                        }
 
768
                } else if (strcmp(line, "Rewind / Fast Forward Sound Effects") == 0) {
 
769
                        int temp; conf >> temp;
 
770
                        if (conf.good() && (temp == 0 || temp == 1)) {
 
771
                                Dictation::setEffectsEnabled((bool) temp);
 
772
                                opWin->soundEffectsCheckbox->setChecked((bool) temp);
 
773
                        }
 
774
                }
 
775
                conf.getline(line, 256, '\n');
 
776
        }
 
777
        conf.close();
 
778
        delete line;
 
779
 
 
780
        //load footpedal options
 
781
        char joyName[257];
 
782
        if (loadFootpedalConfig(joyName)) {
 
783
                if (J == NULL && joyName[0] != '\0') waitForConnection = new thread(doConnectionWait, joyName);
 
784
        } else {
 
785
                clearPedalInfo();
 
786
                Ui::notConfiguredMessage ncmView;
 
787
                QDialog dial;
 
788
                ncmView.setupUi(&dial);
 
789
                bool saidYes = false;
 
790
                if (dial.exec() == QDialog::Accepted) saidYes = true;
 
791
                if (ncmView.dontAsk->isChecked()) {
 
792
                        FILE *joyConf = fopen(joystickFile.c_str(), "w");
 
793
                        fputc('\n', joyConf);
 
794
                        fclose(joyConf);
 
795
                }
 
796
                if (saidYes) footPedalConfigWindow->show();
 
797
        }
 
798
 
 
799
        root->show();
 
800
 
 
801
        MThread *worker = new MThread();
 
802
        worker->start();
 
803
 
 
804
        app.exec();
 
805
        worker->wait();
 
806
 
 
807
        app.quit();
 
808
 
 
809
        if (J != NULL) delete J;
 
810
        if (D != NULL) delete D;
 
811
 
 
812
        delete win;
 
813
        delete opWin;
 
814
        delete fpcWin;
 
815
        delete worker;
 
816
 
 
817
        delete root;
 
818
        delete optionsWindow;
 
819
        delete footPedalConfigWindow;
 
820
 
 
821
        if (selectedDeviceString != NULL) delete[] selectedDeviceString;
 
822
 
 
823
        return 0;
 
824
}