├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CMakeLists.txt ├── CockHeroine.pro ├── CockHeroine.pro.user ├── LICENSING.txt ├── README.md ├── Resources ├── CMakeLists.txt ├── CockHeroine.desktop ├── WandIcon.png └── resources.qrc ├── Source ├── .buttplugdeviceconfigdialog.ui.un~ ├── abstractbeatinterval.cpp ├── abstractbeatinterval.h ├── abstractnewbeatvaluewidget.cpp ├── abstractnewbeatvaluewidget.h ├── adddialog.cpp ├── adddialog.h ├── adddialog.ui ├── adjustdialog.cpp ├── adjustdialog.h ├── adjustdialog.ui ├── analysisoptionsdialog.cpp ├── analysisoptionsdialog.h ├── analysisoptionsdialog.ui ├── beatanalysis.cpp ├── beatanalysis.h ├── beatdatamodel.cpp ├── beatdatamodel.h ├── beatinterval.cpp ├── beatinterval.h ├── beatmeter │ ├── editorgridline.cpp │ └── editorgridline.h ├── beatoptimisation.cpp ├── beatoptimisation.h ├── beatpattern.cpp ├── beatpattern.h ├── beattimestamp.cpp ├── beattimestamp.h ├── beatvalue.cpp ├── beatvalue.h ├── beatvaluewidget.cpp ├── beatvaluewidget.h ├── buttplug │ ├── buttplugdevice.cpp │ ├── buttplugdevice.h │ ├── buttplugdevicefeature.cpp │ ├── buttplugdevicefeature.h │ ├── buttpluginterface.cpp │ └── buttpluginterface.h ├── buttplugdeviceconfigdialog.cpp ├── buttplugdeviceconfigdialog.h ├── buttplugdeviceconfigdialog.ui ├── buttplugdeviceconfigdialog.ui~ ├── buttplugdispatcher.cpp ├── buttplugdispatcher.h ├── buttplugfeatureparams.cpp ├── buttplugfeatureparams.h ├── chmlhandler.cpp ├── chmlhandler.h ├── config.h ├── croppedvideosurface.cpp ├── croppedvideosurface.h ├── customeventaction.cpp ├── customeventaction.h ├── deletedialog.cpp ├── deletedialog.h ├── deletedialog.ui ├── editorwindow.cpp ├── editorwindow.h ├── editorwindow.ui ├── enableidentifyintervalsdialog.cpp ├── enableidentifyintervalsdialog.h ├── enableidentifyintervalsdialog.ui ├── event.cpp ├── event.h ├── eventdatamodel.cpp ├── eventdatamodel.h ├── eventdataproxymodel.cpp ├── eventdataproxymodel.h ├── eventdispatcher.cpp ├── eventdispatcher.h ├── eventmetadata.cpp ├── eventmetadata.h ├── eventtabledelegate.cpp ├── eventtabledelegate.h ├── exportbeatmeterdialog.cpp ├── exportbeatmeterdialog.h ├── exportbeatmeterdialog.ui ├── funscriptwriter.cpp ├── funscriptwriter.h ├── globals.cpp ├── globals.h ├── graphicsscenevideodialog.cpp ├── graphicsscenevideodialog.h ├── graphicsscenevideodialog.ui ├── handycsvwriter.cpp ├── handycsvwriter.h ├── helperfunctions.cpp ├── helperfunctions.h ├── intervaldatamodel.cpp ├── intervaldatamodel.h ├── keyboardshortcutsdialog.cpp ├── keyboardshortcutsdialog.h ├── keyboardshortcutsdialog.ui ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── midifilereader.cpp ├── midifilereader.h ├── midifilewriter.cpp ├── midifilewriter.h ├── newbeatvaluewidget.cpp ├── newbeatvaluewidget.h ├── newcustombeatvaluewidget.cpp ├── newcustombeatvaluewidget.h ├── optimisationoptionsdialog.cpp ├── optimisationoptionsdialog.h ├── optimisationoptionsdialog.ui ├── optionsdialog.cpp ├── optionsdialog.h ├── optionsdialog.ui ├── patterndatamodel.cpp ├── patterndatamodel.h ├── pch.h ├── playbackassociatedaction.cpp ├── playbackassociatedaction.h ├── playbackassociatedactions │ ├── pregenerateestimsignalaction.cpp │ └── pregenerateestimsignalaction.h ├── preplaybackactionmanager.cpp ├── preplaybackactionmanager.h ├── preplaybackinfodialog.cpp ├── preplaybackinfodialog.h ├── preplaybackinfodialog.ui ├── seektotimecodedialog.cpp ├── seektotimecodedialog.h ├── seektotimecodedialog.ui ├── splitdialog.cpp ├── splitdialog.h ├── splitdialog.ui ├── stimsignal │ ├── dualchannelsignalgenerator.cpp │ ├── dualchannelsignalgenerator.h │ ├── estimwavfilewriter.cpp │ ├── estimwavfilewriter.h │ ├── modifiers │ │ ├── boostfaststrokesmodifier.cpp │ │ ├── boostfaststrokesmodifier.h │ │ ├── breaksoftenermodifier.cpp │ │ ├── breaksoftenermodifier.h │ │ ├── channelbalancemodifier.cpp │ │ ├── channelbalancemodifier.h │ │ ├── fadefromcoldmodifier.cpp │ │ ├── fadefromcoldmodifier.h │ │ ├── phaseinvertermodifier.cpp │ │ ├── phaseinvertermodifier.h │ │ ├── phasesettermodifier.cpp │ │ ├── phasesettermodifier.h │ │ ├── progressincreasemodifier.cpp │ │ ├── progressincreasemodifier.h │ │ ├── singlechannelbeatproximitymodifier.cpp │ │ ├── singlechannelbeatproximitymodifier.h │ │ ├── triphasebeatproximitymodifier.cpp │ │ ├── triphasebeatproximitymodifier.h │ │ ├── triphasemodifier.cpp │ │ ├── triphasemodifier.h │ │ ├── waypoint.cpp │ │ ├── waypoint.h │ │ ├── waypointfollowermodifier.cpp │ │ ├── waypointfollowermodifier.h │ │ ├── waypointlist.cpp │ │ └── waypointlist.h │ ├── monostimsignalsample.cpp │ ├── monostimsignalsample.h │ ├── multithreadedsamplepipelineprocessor.cpp │ ├── multithreadedsamplepipelineprocessor.h │ ├── separatelnrsignalgenerator.cpp │ ├── separatelnrsignalgenerator.h │ ├── singlechannelsignalgenerator.cpp │ ├── singlechannelsignalgenerator.h │ ├── stereostimsignalsample.cpp │ ├── stereostimsignalsample.h │ ├── stimsignalfile.cpp │ ├── stimsignalfile.h │ ├── stimsignalgenerator.cpp │ ├── stimsignalgenerator.h │ ├── stimsignalmodifier.cpp │ ├── stimsignalmodifier.h │ ├── stimsignalsample.cpp │ ├── stimsignalsample.h │ ├── stimsignalsamplefactory.cpp │ ├── stimsignalsamplefactory.h │ ├── stimsignalsource.cpp │ ├── stimsignalsource.h │ ├── stimsignalworker.cpp │ ├── stimsignalworker.h │ ├── triphasesignalgenerator.cpp │ └── triphasesignalgenerator.h ├── syncfilewriter.cpp ├── syncfilewriter.h ├── uniquebeatinterval.cpp ├── uniquebeatinterval.h ├── valuedatamodel.cpp ├── valuedatamodel.h ├── valuetablekeyboardeventhandler.cpp ├── valuetablekeyboardeventhandler.h ├── vibratorpulsefeatureparams.cpp ├── vibratorpulsefeatureparams.h ├── wavefileexporter.cpp └── wavefileexporter.h ├── notes_on_deploying.txt └── release64.bat /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Explicitly declare text files you want to always be normalized and converted 5 | # to native line endings on checkout. 6 | *.cpp text 7 | *.h text 8 | *.ui text 9 | *.pro text 10 | *.md text 11 | *.txt text 12 | 13 | # Declare files that will always have CRLF line endings on checkout. 14 | # None, yet... 15 | 16 | # Denote all files that are truly binary and should not be modified. 17 | *.png binary 18 | *.qrc binary 19 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | include: 16 | - os: windows-2022 17 | name: Windows64 18 | - os: ubuntu-20.04 # needs to be 20.04 or else linuxdeployqt will not work 19 | name: Linux 20 | - os: macos-latest 21 | name: macOS 22 | 23 | steps: 24 | - name: Checkout repository 25 | uses: actions/checkout@v2 26 | 27 | - name: Install Qt 5.15.2 on Windows64 28 | if: matrix.name == 'Windows64' 29 | uses: jurplel/install-qt-action@v4 30 | with: 31 | version: '5.15.2' 32 | arch: 'win64_msvc2019_64' 33 | 34 | - name: Install Qt 5.15.2 on Linux 35 | if: matrix.name == 'Linux' 36 | uses: jurplel/install-qt-action@v4 37 | with: 38 | version: '5.15.2' 39 | arch: 'gcc_64' 40 | 41 | - name: Install Qt5 on macOS 42 | if: matrix.name == 'macOS' 43 | run: | 44 | brew install qt5 45 | 46 | - name: Build on Windows64 47 | if: matrix.name == 'Windows64' 48 | run: | 49 | mkdir build 50 | cd build 51 | cmake .. -G"Visual Studio 17 2022" -DQt5_DIR="$env:QT_ROOT_DIR\lib\cmake\Qt5" 52 | cmake --build . --target CockHeroine --config Release 53 | 54 | - name: Build on Linux 55 | if: matrix.name == 'Linux' 56 | run: | 57 | mkdir build 58 | cd build 59 | cmake .. -G"Unix Makefiles" -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release 60 | make -j$(nproc) 61 | 62 | - name: Build on macOS 63 | if: matrix.name == 'macOS' 64 | run: | 65 | mkdir build 66 | cd build 67 | cmake .. -G"Unix Makefiles" -DQt5_DIR=$(brew --prefix qt5)/lib/cmake/Qt5 68 | make -j4 69 | 70 | - name: Deploy Qt libraries on Windows64 71 | if: matrix.name == 'Windows64' 72 | run: | 73 | cd $env:QT_ROOT_DIR\bin 74 | windeployqt --release $env:GITHUB_WORKSPACE\build\Release\CockHeroine.exe 75 | 76 | #- name: Deploy on Linux 77 | # if: matrix.name == 'Linux' 78 | # run: | 79 | # sudo apt-get install libfuse2 80 | # cd $GITHUB_WORKSPACE/build 81 | # wget https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage 82 | # chmod a+x linuxdeployqt-continuous-x86_64.AppImage 83 | # make install DESTDIR=AppDir ; find AppDir/ 84 | # ./linuxdeployqt-continuous-x86_64.AppImage ./AppDir/usr/share/applications/CockHeroine.desktop -appimage 85 | # mv ./CockHeroine*.AppImage CockHeroine-x86_64.AppImage 86 | # ls -lF 87 | 88 | - name: Zip Windows64 artifacts 89 | if: matrix.name == 'Windows64' 90 | run: | 91 | Compress-Archive -Path $env:GITHUB_WORKSPACE\build\Release\* -DestinationPath $env:GITHUB_WORKSPACE\build\CockHeroine.zip 92 | 93 | - name: Upload artifacts on Windows64 94 | if: matrix.name == 'Windows64' 95 | uses: actions/upload-artifact@v4 96 | with: 97 | name: CockHeroine-Windows64 98 | path: ${{ github.workspace }}/build/CockHeroine.zip 99 | 100 | #- name: Upload artifacts on Linux 101 | # if: matrix.name == 'Linux' 102 | # uses: actions/upload-artifact@v4 103 | # with: 104 | # name: CockHeroine-Linux 105 | # path: ${{ github.workspace }}/build/CockHeroine-x86_64.AppImage 106 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | build 3 | *.pro.user 4 | -------------------------------------------------------------------------------- /Resources/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | if (UNIX AND NOT APPLE) 3 | install(FILES CockHeroine.desktop DESTINATION share/applications) 4 | install(FILES WandIcon.png DESTINATION share/icons/hicolor/150x150/apps) 5 | endif() 6 | -------------------------------------------------------------------------------- /Resources/CockHeroine.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=CockHeroine 3 | Comment=A tool for recording and editing beat patterns for cock hero videos 4 | Exec=CockHeroine 5 | Icon=WandIcon 6 | Type=Application 7 | Categories=Utility; 8 | -------------------------------------------------------------------------------- /Resources/WandIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memmaa/cock-heroine/02dcbdd5e2b31b6f09d8af28e2db7248fe6181ff/Resources/WandIcon.png -------------------------------------------------------------------------------- /Resources/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | WandIcon.png 4 | 5 | -------------------------------------------------------------------------------- /Source/.buttplugdeviceconfigdialog.ui.un~: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memmaa/cock-heroine/02dcbdd5e2b31b6f09d8af28e2db7248fe6181ff/Source/.buttplugdeviceconfigdialog.ui.un~ -------------------------------------------------------------------------------- /Source/abstractbeatinterval.cpp: -------------------------------------------------------------------------------- 1 | #include "abstractbeatinterval.h" 2 | #include "helperfunctions.h" 3 | #include 4 | #include 5 | #include "config.h" 6 | #include "globals.h" 7 | 8 | AbstractBeatInterval::AbstractBeatInterval() 9 | { 10 | } 11 | 12 | AbstractBeatInterval::~AbstractBeatInterval() 13 | { 14 | } 15 | 16 | bool AbstractBeatInterval::matchesThisInterval(int otherInterval) const 17 | { 18 | return isWithinXPercentOf(getLength(),otherInterval,BeatAnalysis::Configuration::maxPercentAcceptableBeatError); 19 | } 20 | 21 | bool AbstractBeatInterval::matchesThisInterval(AbstractBeatInterval &otherInterval) const 22 | { 23 | return matchesThisInterval(otherInterval.getLength()); 24 | } 25 | 26 | bool AbstractBeatInterval::matchesThisInterval(BeatValue & otherValue, double atTempo) const 27 | { 28 | return matchesThisInterval(roundToInt(otherValue.getLength(atTempo))); 29 | } 30 | 31 | void AbstractBeatInterval::calculateValue() const 32 | { 33 | if (beatValues.size() == 0) 34 | { 35 | qDebug() << "Warning: Trying to calculate value of interval before beat values are initialised!"; 36 | value = 0; 37 | return; 38 | } 39 | 40 | if (BeatAnalysis::Configuration::tempoEstablished == false) 41 | { 42 | qDebug() << "Warning: Trying to calculate value of interval before tempo is calculated!"; 43 | value = 0; 44 | return; 45 | } 46 | 47 | value = nearestValue(true); 48 | } 49 | 50 | float AbstractBeatInterval::deviationFromNearestKnownBeatValue() const 51 | { 52 | if ( ! value ) 53 | calculateValue(); 54 | 55 | if ( ! value ) 56 | return FLT_MAX; 57 | 58 | return percentageDifferenceBetween(value->getLength(),getLength()); 59 | } 60 | 61 | float AbstractBeatInterval::absoluteDifferenceFromNearestKnownBeatValue() const 62 | { 63 | if ( ! value ) 64 | calculateValue(); 65 | 66 | if ( ! value ) 67 | return FLT_MAX; 68 | 69 | return absoluteDifferenceBetween(getLength(), value->getLength()); 70 | } 71 | 72 | bool AbstractBeatInterval::isKnownBeatValue(double tempo) const 73 | { 74 | bool acceptableProportion = (deviationFromNearestKnownBeatValue() <= BeatAnalysis::Configuration::maxPercentAcceptableBeatError); 75 | if (!acceptableProportion) 76 | return false; //return early as access to 'value->' below may NPE in this case 77 | double tempoInterval = tempoToBeatLength(tempo); 78 | float absoluteDifference = absoluteDifferenceFromNearestKnownBeatValue(); 79 | float partialBeatLength = tempoInterval / (BeatAnalysis::Configuration::allowHalfBeatsInBreaks ? 4.0 : 2.0); 80 | bool acceptableError = absoluteDifference <= partialBeatLength; 81 | return acceptableError; // && acceptableProportion (we wouldn't be here otherwise) 82 | } 83 | 84 | BeatValue const * AbstractBeatInterval::getValue() const 85 | { 86 | if (isKnownBeatValue()) 87 | return value; 88 | else 89 | return NULL; 90 | } 91 | 92 | const BeatValue *AbstractBeatInterval::nearestValue(bool mustBeActive) const 93 | { 94 | float valueDifference = 100.0; 95 | const BeatValue * nearestValue = nullptr; 96 | if (BeatAnalysis::Configuration::tempoEstablished == false) 97 | { 98 | return nearestValue; 99 | } 100 | for (int i = 0; i < beatValues.size(); ++i) 101 | { 102 | if ((beatValues.at(i).active || !mustBeActive) && 103 | percentageDifferenceBetween(getLength(), beatValues.at(i).value() * BeatAnalysis::Configuration::tempoInterval()) < valueDifference) 104 | { 105 | valueDifference = percentageDifferenceBetween(getLength(), beatValues.at(i).value() * BeatAnalysis::Configuration::tempoInterval()); 106 | nearestValue = &beatValues.at(i); 107 | } 108 | } 109 | return nearestValue; 110 | } 111 | -------------------------------------------------------------------------------- /Source/abstractbeatinterval.h: -------------------------------------------------------------------------------- 1 | #ifndef ABSTRACTBEATINTERVAL_H 2 | #define ABSTRACTBEATINTERVAL_H 3 | 4 | #include "beatvalue.h" 5 | #include "globals.h" 6 | 7 | class AbstractBeatInterval 8 | { 9 | public: 10 | AbstractBeatInterval(); 11 | virtual ~AbstractBeatInterval(); 12 | 13 | virtual float getLength() const = 0; 14 | 15 | bool matchesThisInterval(int otherInterval) const; 16 | bool matchesThisInterval(AbstractBeatInterval &otherInterval) const; 17 | bool matchesThisInterval(BeatValue & otherValue, double atTempo = BeatAnalysis::Configuration::currentBPM) const; 18 | 19 | void calculateValue() const; 20 | float deviationFromNearestKnownBeatValue() const; 21 | float absoluteDifferenceFromNearestKnownBeatValue() const; 22 | bool isKnownBeatValue(double atTempo = BeatAnalysis::Configuration::currentBPM) const; 23 | const BeatValue * getValue() const; 24 | 25 | protected: 26 | mutable BeatValue const * value = NULL; 27 | const BeatValue * nearestValue(bool mustBeActive) const; 28 | }; 29 | 30 | #endif // ABSTRACTBEATINTERVAL_H 31 | -------------------------------------------------------------------------------- /Source/abstractnewbeatvaluewidget.h: -------------------------------------------------------------------------------- 1 | #ifndef ABSTRACTNEWBEATVALUEWIDGET_H 2 | #define ABSTRACTNEWBEATVALUEWIDGET_H 3 | 4 | #include 5 | 6 | class QVBoxLayout; 7 | class QHBoxLayout; 8 | class QRadioButton; 9 | 10 | class AbstractNewBeatValueWidget : public QFrame 11 | { 12 | Q_OBJECT 13 | public: 14 | AbstractNewBeatValueWidget(float valueInBeats, QWidget * parent = nullptr); 15 | virtual void updateForValue(float); 16 | virtual QString getName(); 17 | virtual float getMatchResistance() = 0; 18 | void select(); 19 | void deselect(); 20 | virtual int getNumerator() = 0; 21 | virtual int getDivisor() = 0; 22 | virtual float getCurrentValue(); 23 | //! 24 | //! \brief poopness returns a higher number if it's a poor match. 25 | //! \return 0 = perfect match, 1 = exceptionallly poor match, anything higher is not a match at all 26 | //! 27 | float poopness(); 28 | //! 29 | //! \brief matchness the opposite of poopness. If this is not a match, matchness will be 0 30 | //! \return between 0 (not a match) and 100 (perfect match) 31 | //! 32 | float matchness(); 33 | //! 34 | //! \brief isAMatch is this value a possible (but not necessarily the best) match for the interval? 35 | //! It only has to be within the maximum allowed deviation to count 36 | //! \return true if it could be a match 37 | //! 38 | bool isAMatch(); 39 | bool isSelected(); 40 | signals: 41 | void selected(); 42 | void selected(AbstractNewBeatValueWidget *); 43 | protected: 44 | float valueInBeats; 45 | QVBoxLayout * valueLayout; 46 | QHBoxLayout * valueFractionLayout; 47 | QHBoxLayout * valueNameLayout; 48 | QRadioButton * valueNameRadioButton; 49 | virtual void updateBackground(); 50 | 51 | private slots: 52 | void on_selected(bool on_selected); 53 | }; 54 | 55 | #endif // ABSTRACTNEWBEATVALUEWIDGET_H 56 | -------------------------------------------------------------------------------- /Source/adddialog.h: -------------------------------------------------------------------------------- 1 | #ifndef ADDDIALOG_H 2 | #define ADDDIALOG_H 3 | 4 | #define SHORTCUT_ADD_INSERT_BEFORE_SELECTION "SHORTCUT_ADD_INSERT_BEFORE_SELECTION" 5 | #define SHORTCUT_ADD_INSERT_AFTER_SELECTION "SHORTCUT_ADD_INSERT_AFTER_SELECTION" 6 | #define SHORTCUT_ADD_MOVE_OTHERS_BACK "SHORTCUT_ADD_MOVE_OTHERS_BACK" 7 | #define SHORTCUT_ADD_MOVE_OTHERS_FORWARD "SHORTCUT_ADD_MOVE_OTHERS_FORWARD" 8 | 9 | #include 10 | class QShortcut; 11 | class BeatValue; 12 | 13 | namespace Ui { 14 | class AddDialog; 15 | } 16 | 17 | class AddDialog : public QDialog 18 | { 19 | Q_OBJECT 20 | 21 | public: 22 | explicit AddDialog(QWidget *parent = nullptr); 23 | ~AddDialog(); 24 | 25 | virtual void accept() override; 26 | 27 | private slots: 28 | void on_beforeRadioButton_toggled(bool checked); 29 | 30 | void on_afterRadioButton_toggled(bool checked); 31 | 32 | void on_shiftBackRadioButton_toggled(bool checked); 33 | 34 | void on_shiftForwardRadioButton_toggled(bool checked); 35 | 36 | void newValueSelected(BeatValue * newValue); 37 | 38 | void ensureSingleSelection(); 39 | 40 | private: 41 | Ui::AddDialog *ui; 42 | void setShortcuts(); 43 | void setLabels(); 44 | void showEditorAddPage(); 45 | void setButtonState(); 46 | QShortcut * addBeforeShortcut; 47 | QShortcut * addAfterShortcut; 48 | QShortcut * moveBackShortcut; 49 | QShortcut * moveForwardShortcut; 50 | QShortcut * doneShortcut; 51 | }; 52 | 53 | #endif // ADDDIALOG_H 54 | -------------------------------------------------------------------------------- /Source/adddialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | AddDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | Add Interval 15 | 16 | 17 | 18 | 19 | 20 | QFrame::StyledPanel 21 | 22 | 23 | QFrame::Raised 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | QFrame::StyledPanel 32 | 33 | 34 | QFrame::Raised 35 | 36 | 37 | 38 | 39 | 40 | Add interval... 41 | 42 | 43 | 44 | 45 | 46 | (K) Before selected interval(s) 47 | 48 | 49 | 50 | 51 | 52 | 53 | (J) After selected interval(s) 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | Move other intervals... 64 | 65 | 66 | 67 | 68 | 69 | (L) Back (right) 70 | 71 | 72 | 73 | 74 | 75 | 76 | (H) Forward (left) 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | Qt::Horizontal 90 | 91 | 92 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | buttonBox 102 | accepted() 103 | AddDialog 104 | accept() 105 | 106 | 107 | 248 108 | 254 109 | 110 | 111 | 157 112 | 274 113 | 114 | 115 | 116 | 117 | buttonBox 118 | rejected() 119 | AddDialog 120 | reject() 121 | 122 | 123 | 316 124 | 260 125 | 126 | 127 | 286 128 | 274 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /Source/adjustdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef ADJUSTDIALOG_H 2 | #define ADJUSTDIALOG_H 3 | 4 | #define SHORTCUT_ADJUST_MOVE_BEGINNING "SHORTCUT_ADJUST_MOVE_BEGINNING" 5 | #define SHORTCUT_ADJUST_MOVE_END "SHORTCUT_ADJUST_MOVE_END" 6 | #define SHORTCUT_ADJUST_MOVE_BOTH "SHORTCUT_ADJUST_MOVE_BOTH" 7 | #define SHORTCUT_ADJUST_SMART_ADJUST "SHORTCUT_ADJUST_SMART_ADJUST" 8 | #define SHORTCUT_ADJUST_OVERWRITE_OTHERS "SHORTCUT_ADJUST_OVERWIRTE_OTHERS" 9 | 10 | #include 11 | class QShortcut; 12 | class BeatValue; 13 | 14 | namespace Ui { 15 | class AdjustDialog; 16 | } 17 | 18 | class AdjustDialog : public QDialog 19 | { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit AdjustDialog(QWidget *parent = nullptr); 24 | AdjustDialog(BeatValue presetValue, QWidget *parent = nullptr); 25 | ~AdjustDialog(); 26 | 27 | virtual void accept() override; 28 | 29 | private slots: 30 | void newValueSelected(BeatValue * newValue); 31 | 32 | void ensureSingleSelection(); 33 | 34 | void on_moveBeginningRadioButton_toggled(bool checked); 35 | 36 | void on_moveEndRadioButton_toggled(bool checked); 37 | 38 | void on_moveBothRadioButton_toggled(bool checked); 39 | 40 | void on_smartAdjustRadioButton_toggled(bool checked); 41 | 42 | void on_allowOverwritesCheckBox_toggled(bool checked); 43 | 44 | private: 45 | Ui::AdjustDialog *ui; 46 | void setShortcuts(); 47 | void setLabels(); 48 | void showEditorAdjustPage(); 49 | void populateValues(); 50 | void populateSingleValue(BeatValue value); 51 | void clearValues(); 52 | void setButtonState(); 53 | QVector valueShortcuts; 54 | QShortcut * moveEndShortcut; 55 | QShortcut * moveBeginningShortcut; 56 | QShortcut * moveBothShortcut; 57 | QShortcut * smartAdjustShortcut; 58 | QShortcut * overwriteOthersShortcut; 59 | QShortcut * doneShortcut; 60 | }; 61 | 62 | #endif // ADJUSTDIALOG_H 63 | -------------------------------------------------------------------------------- /Source/adjustdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | AdjustDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | Adjust Interval 15 | 16 | 17 | 18 | 19 | 20 | QFrame::StyledPanel 21 | 22 | 23 | QFrame::Raised 24 | 25 | 26 | 27 | 28 | 29 | Allow overwriting other timestamps 30 | 31 | 32 | 33 | 34 | 35 | 36 | Move beginning 37 | 38 | 39 | 40 | 41 | 42 | 43 | Move end 44 | 45 | 46 | 47 | 48 | 49 | 50 | Move both equally 51 | 52 | 53 | 54 | 55 | 56 | 57 | Smart adjust 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | QFrame::StyledPanel 68 | 69 | 70 | QFrame::Raised 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | Qt::Horizontal 79 | 80 | 81 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | buttonBox 91 | accepted() 92 | AdjustDialog 93 | accept() 94 | 95 | 96 | 248 97 | 254 98 | 99 | 100 | 157 101 | 274 102 | 103 | 104 | 105 | 106 | buttonBox 107 | rejected() 108 | AdjustDialog 109 | reject() 110 | 111 | 112 | 316 113 | 260 114 | 115 | 116 | 286 117 | 274 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /Source/analysisoptionsdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef ANALYSISOPTIONSDIALOG_H 2 | #define ANALYSISOPTIONSDIALOG_H 3 | 4 | #include 5 | 6 | class QTime; 7 | class QTimer; 8 | 9 | namespace Ui { 10 | class AnalysisOptionsDialog; 11 | } 12 | 13 | class AnalysisOptionsDialog : public QDialog 14 | { 15 | Q_OBJECT 16 | 17 | public: 18 | explicit AnalysisOptionsDialog(QWidget *parent = 0); 19 | ~AnalysisOptionsDialog(); 20 | 21 | private slots: 22 | void on_useProvidedTempoRadioButton_clicked(); 23 | 24 | void on_automaticTempoRadioButton_clicked(); 25 | 26 | void on_preferLongPatternsCheckBox_clicked(); 27 | 28 | void on_buttons_accepted(); 29 | 30 | void on_matchPatternMembersByActualValueCheckBox_clicked(); 31 | 32 | void on_matchPatternMembersByNearestKnownValueCheckBox_clicked(); 33 | 34 | void on_tapTempoInputButton_clicked(); 35 | 36 | void onTempoTimeout(); 37 | 38 | 39 | void on_tapTempoResetButton_clicked(); 40 | 41 | private: 42 | Ui::AnalysisOptionsDialog *ui; 43 | QSettings settings; 44 | short tapTempoCounter; 45 | float previousTempo; 46 | QTime * tapTempoTimer; 47 | QTimer * tempoTimeoutTimer; 48 | }; 49 | 50 | #endif // ANALYSISOPTIONSDIALOG_H 51 | -------------------------------------------------------------------------------- /Source/beatdatamodel.h: -------------------------------------------------------------------------------- 1 | #ifndef BEATDATAMODEL_H 2 | #define BEATDATAMODEL_H 3 | 4 | #include 5 | #include 6 | class EventDataProxyModel; 7 | class Event; 8 | class BeatTimestamp; 9 | 10 | class BeatDataModel : public QAbstractTableModel 11 | { 12 | Q_OBJECT 13 | public: 14 | explicit BeatDataModel(QObject *parent = 0); 15 | int rowCount ( const QModelIndex & parent = QModelIndex() ) const; 16 | int columnCount ( const QModelIndex & parent = QModelIndex() ) const; 17 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 18 | Qt::ItemFlags flags(const QModelIndex &index) const; 19 | QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; 20 | 21 | //! \param deleted almost always false - only true if restoring pre-deleted timestamps from an undo snapshot 22 | void addEvent(const Event &newEvent, int index = -1); 23 | void addBeat(const QModelIndex & index); 24 | void addBeats(EventDataProxyModel * model, const QModelIndexList & indexList); 25 | void addEventsFromSnapshot(EditorWin::RollbackSnapshot snapshot); 26 | 27 | Event eventFromRow(int row); 28 | void changeEventAt(int index, const Event & event); 29 | void changeTimestampAt(int index, long newTimestamp); 30 | 31 | void removeBeat(int index); 32 | bool removeRows (int row, int count, const QModelIndex &); 33 | 34 | BeatTimestamp & operator[](int index); 35 | 36 | void cancelQueuedDeletions(); 37 | void writeChangesToOriginalModel(); 38 | 39 | private: 40 | EventDataProxyModel * originModel; 41 | QVector indexesToDelete; 42 | 43 | friend void EditorWindow::createRollbackSnapshot(); 44 | friend void EditorWindow::applySnapshot(EditorWin::RollbackSnapshot); 45 | 46 | signals: 47 | 48 | public slots: 49 | 50 | }; 51 | 52 | #endif // BEATDATAMODEL_H 53 | -------------------------------------------------------------------------------- /Source/beatinterval.cpp: -------------------------------------------------------------------------------- 1 | #include "beatinterval.h" 2 | #include "beattimestamp.h" 3 | #include "globals.h" 4 | 5 | BeatInterval::BeatInterval() 6 | : 7 | index(-1) 8 | { 9 | //nothing here 10 | } 11 | 12 | BeatInterval::BeatInterval(int index) 13 | : 14 | index(index) 15 | { 16 | //nothing here 17 | } 18 | 19 | BeatInterval::~BeatInterval() 20 | { 21 | } 22 | 23 | int BeatInterval::startsAtTimestamp() 24 | { 25 | return beatTimestamps.at(index).eventData.timestamp; 26 | } 27 | 28 | int BeatInterval::endsAtTimestamp() 29 | { 30 | return beatTimestamps.at(index + 1).eventData.timestamp; 31 | } 32 | 33 | float BeatInterval::getLength() const 34 | { 35 | return (float) getIntLength(); 36 | } 37 | 38 | int BeatInterval::getIntLength() const 39 | { 40 | return beatTimestamps.at(index + 1).eventData.timestamp - beatTimestamps.at(index).eventData.timestamp; 41 | } 42 | -------------------------------------------------------------------------------- /Source/beatinterval.h: -------------------------------------------------------------------------------- 1 | #ifndef BEATINTERVAL_H 2 | #define BEATINTERVAL_H 3 | 4 | #include "abstractbeatinterval.h" 5 | 6 | class BeatTimestamp; 7 | 8 | class BeatInterval : public AbstractBeatInterval 9 | { 10 | public: 11 | BeatInterval(); 12 | BeatInterval(int index); 13 | ~BeatInterval(); 14 | 15 | BeatInterval * prev = NULL; 16 | BeatInterval * next = NULL; 17 | 18 | int index; 19 | int startsAtTimestamp(); 20 | int endsAtTimestamp(); 21 | 22 | float getLength() const; 23 | int getIntLength() const; 24 | }; 25 | 26 | #endif // BEATINTERVAL_H 27 | -------------------------------------------------------------------------------- /Source/beatmeter/editorgridline.cpp: -------------------------------------------------------------------------------- 1 | #include "editorgridline.h" 2 | #include 3 | #include "beatanalysis.h" 4 | #include "optionsdialog.h" 5 | 6 | EditorGridLine::EditorGridLine(float beatsFromNow, float opacity, QGraphicsItem * parent) 7 | : 8 | QGraphicsLineItem(parent), 9 | beatsFromNow(beatsFromNow) 10 | { 11 | reposition(); 12 | 13 | setOpacity(opacity); 14 | 15 | QPen pen; 16 | pen.setWidth(1); 17 | setPen(pen); 18 | } 19 | 20 | void EditorGridLine::reposition() 21 | { 22 | qreal xCoordinate = beatsFromNow * getPixelsPerBeat(); 23 | int yHalfLength = (qreal) OptionsDialog::getBeatMeterHeight() / 2; 24 | QLineF line(xCoordinate, yHalfLength, xCoordinate, -yHalfLength); 25 | setLine(line); 26 | } 27 | 28 | float EditorGridLine::value() 29 | { 30 | return beatsFromNow; 31 | } 32 | 33 | qreal EditorGridLine::getPixelsPerBeat() 34 | { 35 | if (false == BeatAnalysis::Configuration::tempoEstablished) 36 | return (qreal) OptionsDialog::getBeatMeterSpeed() / 2; 37 | return (BeatAnalysis::Configuration::tempoInterval() * OptionsDialog::getBeatMeterSpeed()) / 1000; 38 | } 39 | -------------------------------------------------------------------------------- /Source/beatmeter/editorgridline.h: -------------------------------------------------------------------------------- 1 | #ifndef EDITORGRIDLINE_H 2 | #define EDITORGRIDLINE_H 3 | 4 | #include 5 | 6 | class EditorGridLine : public QGraphicsLineItem 7 | { 8 | // Q_OBJECT 9 | public: 10 | EditorGridLine(float beatsFromNow, float opacity, QGraphicsItem *parent = nullptr); 11 | void reposition(); 12 | float value(); 13 | private: 14 | float beatsFromNow; 15 | qreal getPixelsPerBeat(); 16 | }; 17 | 18 | #endif // EDITORGRIDLINE_H 19 | -------------------------------------------------------------------------------- /Source/beatoptimisation.h: -------------------------------------------------------------------------------- 1 | #ifndef BEATOPTIMISATION_H 2 | #define BEATOPTIMISATION_H 3 | 4 | struct BeatOptimisation 5 | { 6 | static void optimiseBeats(); 7 | static void applyBeats(); 8 | 9 | struct Configuration 10 | { 11 | static bool outputTempoProvided; 12 | static double providedOutputTempo; 13 | static bool startingTimestampProvided; 14 | static long providedStartingTimestamp; 15 | static bool roundToNearestBPM; 16 | static bool roundToEvenBPM; 17 | static float outputTempoInterval(); 18 | static void setImprovedValuesFromProvided(); 19 | }; 20 | 21 | private: 22 | static long shiftWhenWorking; 23 | static float improvedStartTimestamp; 24 | static float improvedTempoInterval; 25 | static QVector targetTimestamps; 26 | static QVector trialTimestamps; 27 | static char lastProgressChar; 28 | static short outputCharCounter; 29 | 30 | static void configureTrialTimestamps(float start, float interval); 31 | static double compareTrialTimestamps(); 32 | static void outputProgressChar(char progressChar); 33 | 34 | friend class BeatDataModel; 35 | friend class EditorWindow; 36 | }; 37 | 38 | #endif // BEATOPTIMISATION_H 39 | -------------------------------------------------------------------------------- /Source/beatpattern.h: -------------------------------------------------------------------------------- 1 | #ifndef BEATPATTERN_H 2 | #define BEATPATTERN_H 3 | 4 | class BeatValue; 5 | 6 | class BeatPattern 7 | { 8 | public: 9 | BeatPattern(); 10 | BeatPattern(int startBeat); 11 | 12 | //all forward to underlying vector 13 | void clear(); 14 | void append(const BeatValue * const &t); 15 | int size() const; 16 | bool isEmpty() const; 17 | void remove(int i); 18 | void removeLast(); 19 | const BeatValue * const & at(int i) const; 20 | const BeatValue *& operator[](int i); 21 | const BeatValue * takeFirst(); 22 | void resize (int i); 23 | 24 | int startsAtBeat() const; 25 | void setStartBeat(int ); 26 | 27 | int lengthInStrokes() const; 28 | bool containsUnknownIntervals() const; 29 | float lengthInBeats() const; 30 | float lengthInMsAtTempo(); 31 | 32 | void reduceToShortestForm(); 33 | 34 | int totalStrokesCovered() const; 35 | void setTotalStrokesCovered(int count); 36 | float totalBeatsCovered() const; 37 | int repetitions() const; 38 | int framesRequiredPerBeat() const; 39 | 40 | QVector listOfDenominators() const; 41 | 42 | QString name() const; 43 | 44 | bool operator==(const BeatPattern & other) const; 45 | bool cyclesTheSameAs(const BeatPattern &other) const; 46 | 47 | static BeatPattern * byName(QString name); 48 | 49 | private: 50 | QVector vector; 51 | int startsAt; 52 | int strokesCovered; 53 | }; 54 | 55 | #endif // BEATPATTERN_H 56 | -------------------------------------------------------------------------------- /Source/beattimestamp.cpp: -------------------------------------------------------------------------------- 1 | #include "beattimestamp.h" 2 | 3 | BeatTimestamp::BeatTimestamp() 4 | : 5 | originalRowIndex(-1), 6 | isDeleted(false) 7 | { 8 | } 9 | 10 | BeatTimestamp::BeatTimestamp(int index, Event event, bool deleted) 11 | : 12 | originalRowIndex(index), 13 | eventData(event), 14 | isDeleted(deleted) 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /Source/beattimestamp.h: -------------------------------------------------------------------------------- 1 | #ifndef BEATTIMESTAMP_H 2 | #define BEATTIMESTAMP_H 3 | 4 | #include "event.h" 5 | 6 | class BeatTimestamp 7 | { 8 | public: 9 | BeatTimestamp(); 10 | //! 11 | //! \brief BeatTimestamp 12 | //! \param index 13 | //! \param event 14 | //! \param deleted almost always false - only true if restoring pre-deleted timestamps from an undo snapshot 15 | //! 16 | BeatTimestamp(int index, Event event, bool deleted = false); 17 | 18 | int originalRowIndex; 19 | Event eventData; 20 | bool isDeleted; 21 | }; 22 | 23 | #endif // BEATTIMESTAMP_H 24 | -------------------------------------------------------------------------------- /Source/beatvalue.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "beatvalue.h" 4 | #include "globals.h" 5 | #include "config.h" 6 | #include "uniquebeatinterval.h" 7 | #include "helperfunctions.h" 8 | 9 | float BeatValue::value() const 10 | { 11 | return ((float)numerator / (float)denominator); 12 | } 13 | 14 | float BeatValue::getLength(double atTempo) const 15 | { 16 | return value() * tempoToBeatLength(atTempo); 17 | } 18 | 19 | bool BeatValue::isBestEnabledMatchFor(int intervalInMs, double tempo) 20 | { 21 | BeatValue * nearestValue = nullptr; 22 | double beatLength = tempoToBeatLength(tempo); 23 | double valueDifference = 100.0; 24 | for (int i = 0; i < beatValues.size(); ++i) 25 | { 26 | if (beatValues.at(i).active && 27 | percentageDifferenceBetween(intervalInMs, beatValues.at(i).value() * beatLength) < valueDifference) 28 | { 29 | valueDifference = percentageDifferenceBetween(intervalInMs, beatValues.at(i).value() * beatLength); 30 | nearestValue = &beatValues[i]; 31 | } 32 | } 33 | return *nearestValue == *this; 34 | } 35 | 36 | bool BeatValue::isBestPossibleMatchFor(int intervalInMs, double tempo) 37 | { 38 | BeatValue * nearestValue = nullptr; 39 | double beatLength = tempoToBeatLength(tempo); 40 | double valueDifference = 100.0; 41 | for (int i = 0; i < beatValues.size(); ++i) 42 | { 43 | if (percentageDifferenceBetween(intervalInMs, beatValues.at(i).value() * beatLength) < valueDifference) 44 | { 45 | valueDifference = percentageDifferenceBetween(intervalInMs, beatValues.at(i).value() * beatLength); 46 | nearestValue = &beatValues[i]; 47 | } 48 | } 49 | return *nearestValue == *this; 50 | } 51 | 52 | bool BeatValue::isPossibleMatchFor(int intervalInMs, double tempo) 53 | { 54 | float deviationFromValue = percentageDifferenceBetween(getLength(tempo),intervalInMs); 55 | bool acceptableProportion = (deviationFromValue <= BeatAnalysis::Configuration::maxPercentAcceptableBeatError); 56 | if (!acceptableProportion) 57 | return false; //return early as access to 'value->' below may NPE in this case 58 | double tempoInterval = tempoToBeatLength(tempo); 59 | float absoluteDifference = abs(getLength(tempo) - intervalInMs); 60 | float partialBeatLength = tempoInterval / (BeatAnalysis::Configuration::allowHalfBeatsInBreaks ? 4.0 : 2.0); 61 | bool acceptableError = absoluteDifference <= partialBeatLength; 62 | return acceptableError; // && acceptableProportion (we wouldn't be here otherwise) 63 | } 64 | 65 | bool BeatValue::operator==(const BeatValue & other) 66 | { 67 | return numerator == other.numerator && 68 | denominator == other.denominator && 69 | name == other.name; 70 | } 71 | -------------------------------------------------------------------------------- /Source/beatvalue.h: -------------------------------------------------------------------------------- 1 | #ifndef BEATVALUE_H 2 | #define BEATVALUE_H 3 | 4 | #include 5 | #include "beatanalysis.h" 6 | 7 | class BeatValue 8 | { 9 | public: 10 | 11 | BeatValue() : active(false) {} //Because of QVector's annoying requirement for a default ctr. 12 | BeatValue(unsigned short numerator, 13 | unsigned char denominator, 14 | QString name, 15 | bool active = true, 16 | bool preset = false) 17 | : numerator(numerator), 18 | denominator(denominator), 19 | name(name), 20 | active(active), 21 | preset(preset) 22 | { 23 | //nothing 24 | } 25 | unsigned short numerator; 26 | unsigned char denominator; 27 | QString name; 28 | bool active; 29 | bool preset; 30 | //! 31 | //! \brief value the number of beats in this value. For example, 'Four Beats' is 4.0, 'Five Semisquavers' is 1.25. 32 | //! \return 33 | //! 34 | float value() const; 35 | float getLength(double atTempo = BeatAnalysis::Configuration::currentBPM) const; 36 | //! 37 | //! \brief isBestEnabledMatchFor is this value the one that would be given if asked for the best match for the given length of time 38 | //! \param intervalInMs 39 | //! \return true if it's the best match 40 | //! 41 | bool isBestEnabledMatchFor(int intervalInMs, double tempo = BeatAnalysis::Configuration::currentBPM); 42 | //! 43 | //! \brief isBestPossibleMatchFor is this value the best match for the given length of time (even if it's not active/enabled) 44 | //! \param intervalInMs 45 | //! \return true if it's the best match 46 | //! 47 | bool isBestPossibleMatchFor(int intervalInMs, double tempo = BeatAnalysis::Configuration::currentBPM); 48 | //! 49 | //! \brief isBestPossibleMatchFor would this value match the given length of time (if it were enabled and unmasked by any other better matches) 50 | //! \param intervalInMs 51 | //! \return true if it's a possible match 52 | //! 53 | bool isPossibleMatchFor(int intervalInMs, double tempo = BeatAnalysis::Configuration::currentBPM); 54 | 55 | bool operator==(const BeatValue & other); 56 | }; 57 | 58 | #endif // BEATVALUE_H 59 | -------------------------------------------------------------------------------- /Source/beatvaluewidget.cpp: -------------------------------------------------------------------------------- 1 | #include "beatvaluewidget.h" 2 | #include 3 | #include "beatvalue.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | BeatValueWidget::BeatValueWidget(QWidget *parent, BeatValue value, SelectionMode mode) 10 | : 11 | QFrame(parent), 12 | value(value), 13 | layout(new QVBoxLayout(this)), 14 | label(new QLabel(value.name, this)), 15 | selectionMode(mode) 16 | { 17 | setFrameShape(QFrame::StyledPanel); 18 | setFrameShadow(QFrame::Raised); 19 | switch (mode) 20 | { 21 | case RepeatableSelection: 22 | selectionButton = new QPushButton(tr("Add"),this); 23 | connect(selectionButton, SIGNAL(clicked(bool)), this, SLOT(on_selected(bool))); 24 | break; 25 | case SingleSelection: 26 | selectionButton = new QRadioButton(parent); 27 | connect(selectionButton, SIGNAL(clicked(bool)), this, SLOT(on_selected(bool))); 28 | break; 29 | case NoSelection: 30 | selectionButton = nullptr; 31 | break; 32 | } 33 | 34 | layout->addWidget(label); 35 | layout->addWidget(selectionButton); 36 | } 37 | 38 | void BeatValueWidget::select() 39 | { 40 | switch (selectionMode) { 41 | case SingleSelection: 42 | if (!selectionButton->isChecked()) 43 | selectionButton->click(); 44 | selectionButton->setChecked(true); 45 | break; 46 | case RepeatableSelection: 47 | selectionButton->click(); 48 | break; 49 | default: 50 | break; 51 | } 52 | } 53 | 54 | void BeatValueWidget::deselect() 55 | { 56 | switch (selectionMode) { 57 | case SingleSelection: 58 | selectionButton->setChecked(false); 59 | break; 60 | default: 61 | break; 62 | } 63 | } 64 | 65 | void BeatValueWidget::on_selected(bool is_selected) 66 | { 67 | if (is_selected || selectionMode == RepeatableSelection) 68 | { 69 | emit selected(); 70 | emit selected(&value); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Source/beatvaluewidget.h: -------------------------------------------------------------------------------- 1 | #ifndef BEATVALUEWIDGET_H 2 | #define BEATVALUEWIDGET_H 3 | 4 | #include 5 | #include "beatvalue.h" 6 | class QVBoxLayout; 7 | class QLabel; 8 | class QAbstractButton; 9 | 10 | class BeatValueWidget : public QFrame 11 | { 12 | public: 13 | enum SelectionMode { 14 | RepeatableSelection, 15 | SingleSelection, 16 | NoSelection 17 | }; 18 | private: 19 | Q_OBJECT 20 | public: 21 | explicit BeatValueWidget(QWidget *parent, BeatValue value, SelectionMode mode); 22 | void deselect(); 23 | BeatValue getValue() {return value;}; 24 | 25 | signals: 26 | void selected(); 27 | void selected(BeatValue * value); 28 | 29 | public slots: 30 | void select(); 31 | 32 | private slots: 33 | void on_selected(bool on_selected); 34 | 35 | private: 36 | BeatValue value; 37 | QVBoxLayout * layout; 38 | QLabel * label; 39 | SelectionMode selectionMode; 40 | QAbstractButton * selectionButton; 41 | }; 42 | 43 | #endif // BEATVALUEWIDGET_H 44 | -------------------------------------------------------------------------------- /Source/buttplug/buttplugdevice.cpp: -------------------------------------------------------------------------------- 1 | #include "buttplugdevice.h" 2 | #include "buttplugdevicefeature.h" 3 | #include "buttpluginterface.h" 4 | 5 | int ButtplugDevice::eventCollationTime = 1; 6 | 7 | ButtplugDevice::ButtplugDevice(QObject * parent) 8 | : 9 | QObject(parent) 10 | { 11 | 12 | } 13 | 14 | ButtplugDevice::ButtplugDevice(ButtplugInterface * parent) 15 | : 16 | QObject(parent), 17 | parent(parent), 18 | name(""), 19 | index(0), 20 | supportsStop(false), 21 | supportsVibrate(false), 22 | numVibrators(0), 23 | vibrationLevels(0), 24 | supportsLinear(false), 25 | numLinearAxes(0), 26 | supportsRotate(false), 27 | numRotationalAxes(0), 28 | supportsBatteryLevel(false), 29 | supportsSignalStrength(false), 30 | vibrateCmdTimer(this) 31 | { 32 | } 33 | 34 | ButtplugDevice::ButtplugDevice(ButtplugInterface * parent, 35 | QString name, 36 | int index, 37 | bool supportsStop, 38 | QVector vibratorLevels, 39 | int linearAxes, 40 | int rotaionalAxes, 41 | bool supportsBatteryLevel, 42 | bool supportsSignalStrength) 43 | : 44 | QObject(parent), 45 | parent(parent), 46 | name(name), 47 | index(index), 48 | supportsStop(supportsStop), 49 | supportsVibrate(!vibratorLevels.isEmpty()), 50 | numVibrators(vibratorLevels.size()), 51 | vibrationLevels(vibratorLevels), 52 | supportsLinear(linearAxes > 0), 53 | numLinearAxes(linearAxes), 54 | supportsRotate(rotaionalAxes > 0), 55 | numRotationalAxes(rotaionalAxes), 56 | supportsBatteryLevel(supportsBatteryLevel), 57 | supportsSignalStrength(supportsSignalStrength), 58 | vibrateCmdTimer(this) 59 | { 60 | populateFeatureList(); 61 | vibrateCmdTimer.setSingleShot(true); 62 | vibrateCmdTimer.setTimerType(Qt::PreciseTimer); 63 | connect(&vibrateCmdTimer, &QTimer::timeout, this, &ButtplugDevice::sendVibrationCommand); 64 | } 65 | 66 | ButtplugDevice::~ButtplugDevice() 67 | { 68 | 69 | } 70 | 71 | int ButtplugDevice::totalFeatures() 72 | { 73 | return numVibrators + numLinearAxes + numRotationalAxes; 74 | } 75 | 76 | void ButtplugDevice::populateFeatureList() 77 | { 78 | for (int index = 0; index < numVibrators; ++index) 79 | { 80 | currentVibratorIntensities.append(0); 81 | ButtplugDeviceFeature * feature = new ButtplugDeviceFeature(this, ButtplugDeviceFeatureType::VibratorMotor, index, vibrationLevels[index]); 82 | featureList.append(feature); 83 | } 84 | 85 | for (int index = 0; index < numLinearAxes; ++index) 86 | { 87 | ButtplugDeviceFeature * feature = new ButtplugDeviceFeature(this, ButtplugDeviceFeatureType::StrokerAxis, index); 88 | featureList.append(feature); 89 | } 90 | 91 | for (int index = 0; index < numRotationalAxes; ++index) 92 | { 93 | currentRotationalIntensities.append(0); 94 | ButtplugDeviceFeature * feature = new ButtplugDeviceFeature(this, ButtplugDeviceFeatureType::RotatorMotor, index); 95 | featureList.append(feature); 96 | } 97 | } 98 | 99 | void ButtplugDevice::setVibration(int idx, double intensity) 100 | { 101 | currentVibratorIntensities[idx] = intensity; 102 | vibrateCmdTimer.start(eventCollationTime); 103 | } 104 | 105 | void ButtplugDevice::sendVibrationCommand() 106 | { 107 | parent->sendVibrateCmd(index, currentVibratorIntensities); 108 | } 109 | -------------------------------------------------------------------------------- /Source/buttplug/buttplugdevice.h: -------------------------------------------------------------------------------- 1 | #ifndef BUTTPLUGDEVICE_H 2 | #define BUTTPLUGDEVICE_H 3 | 4 | #include 5 | 6 | class ButtplugInterface; 7 | class ButtplugDeviceFeature; 8 | 9 | class ButtplugDevice : public QObject 10 | { 11 | Q_OBJECT 12 | 13 | public: 14 | ButtplugDevice(QObject * parent = nullptr); 15 | ButtplugDevice(ButtplugInterface * parent = nullptr); 16 | ButtplugDevice(ButtplugInterface * parent, 17 | QString name, 18 | int index, 19 | bool supportsStop, 20 | QVector vibratorLevels, 21 | int linearAxes, 22 | int rotaionalAxes, 23 | bool supportsBatteryLevel, 24 | bool supportsSignalStrength); 25 | ~ButtplugDevice(); 26 | 27 | static int eventCollationTime; 28 | 29 | ButtplugInterface * parent; 30 | QString name; 31 | 32 | int index; 33 | bool supportsStop; 34 | bool supportsVibrate; 35 | int numVibrators; 36 | QVector vibrationLevels; 37 | bool supportsLinear; 38 | int numLinearAxes; 39 | bool supportsRotate; 40 | int numRotationalAxes; 41 | bool supportsBatteryLevel; 42 | bool supportsSignalStrength; 43 | 44 | QVector featureList; 45 | int totalFeatures(); 46 | void populateFeatureList(); 47 | 48 | QVector currentVibratorIntensities; 49 | // QVector currentStrokerSpeeds; //needed? 50 | QVector currentRotationalIntensities; 51 | 52 | void setVibration(int index, double intensity); 53 | void setRotation(int index, double intensity); 54 | QTimer vibrateCmdTimer; 55 | 56 | private slots: 57 | void sendVibrationCommand(); 58 | }; 59 | 60 | #endif // BUTTPLUGDEVICE_H 61 | -------------------------------------------------------------------------------- /Source/buttplug/buttplugdevicefeature.cpp: -------------------------------------------------------------------------------- 1 | #include "buttplugdevicefeature.h" 2 | #include "buttplugfeatureparams.h" 3 | #include "buttplugdeviceconfigdialog.h" 4 | #include 5 | #include "buttplugdevice.h" 6 | 7 | ButtplugDeviceFeature::ButtplugDeviceFeature(ButtplugDevice * device, ButtplugDeviceFeatureType type, int index, int featureLevels) 8 | : 9 | device(device), 10 | type(type), 11 | index(index), 12 | levels(featureLevels) 13 | { 14 | //nothing here 15 | } 16 | 17 | ButtplugDeviceFeature::~ButtplugDeviceFeature() 18 | { 19 | //or here 20 | } 21 | 22 | QString ButtplugDeviceFeature::getName() 23 | { 24 | if (device->totalFeatures() == 1) 25 | return device->name; 26 | switch (type) 27 | { 28 | case VibratorMotor: 29 | if (device->numVibrators == 1) 30 | return QString("%1 (Vibrator)").arg(device->name); 31 | else 32 | return QString("%1 (Vibrator %2)").arg(device->name).arg(index + 1); 33 | case StrokerAxis: 34 | if (device->numLinearAxes == 1) 35 | return QString("%1 (Stroker)").arg(device->name); 36 | else 37 | return QString("%1 (Axis %2)").arg(device->name).arg(index + 1); 38 | case RotatorMotor: 39 | if (device->numRotationalAxes == 1) 40 | return QString("%1 (Rotation)").arg(device->name); 41 | else 42 | return QString("%1 (Rotator %2)").arg(device->name).arg(index + 1); 43 | } 44 | return QString("Wat."); 45 | } 46 | 47 | void ButtplugDeviceFeature::dispatch(Event event) 48 | { 49 | ButtplugFeatureParams * params = getParamsFor(event); 50 | if (params == nullptr) 51 | { 52 | return; 53 | } 54 | 55 | params->handleEvent(event, this); 56 | } 57 | 58 | ButtplugFeatureParams * ButtplugDeviceFeature::getParamsFor(Event event) 59 | { 60 | ButtplugFeatureParams * featureDefault = nullptr; 61 | ButtplugFeatureParams * deviceFeatureDefault = nullptr; 62 | ButtplugFeatureParams * specificFeatureEvent = nullptr; 63 | for (ButtplugFeatureParams * params : ButtplugDeviceConfigDialog::entries) 64 | { 65 | if (params->featureType == type) //ignore params for incompatible types of feature 66 | { 67 | if (params->isDefaultParams()) 68 | featureDefault = params; 69 | else if (params->featureName == getName()) 70 | { 71 | if (params->eventType == -1) 72 | deviceFeatureDefault = params; 73 | else if (params->eventType == event.type()) 74 | specificFeatureEvent = params; 75 | //TODO: This is temporary - really params should have a type that is correct 76 | else 77 | specificFeatureEvent = params; 78 | } 79 | } 80 | } 81 | if (specificFeatureEvent != nullptr) 82 | return specificFeatureEvent; 83 | else if (deviceFeatureDefault != nullptr) 84 | return deviceFeatureDefault; 85 | if (featureDefault == nullptr) 86 | qDebug() << "Oh dear... something went wrong: Can't find parameters for event " << event.toAsciiString() << " on feature " << getName(); 87 | return featureDefault; 88 | } 89 | 90 | void ButtplugDeviceFeature::setVibration(double level) 91 | { 92 | device->setVibration(index, level); 93 | } 94 | -------------------------------------------------------------------------------- /Source/buttplug/buttplugdevicefeature.h: -------------------------------------------------------------------------------- 1 | #ifndef BUTTPLUGDEVICEFEATURE_H 2 | #define BUTTPLUGDEVICEFEATURE_H 3 | 4 | #include "event.h" 5 | class ButtplugDevice; 6 | class ButtplugFeatureParams; 7 | 8 | enum ButtplugDeviceFeatureType 9 | { 10 | VibratorMotor, 11 | StrokerAxis, 12 | RotatorMotor 13 | }; 14 | 15 | class ButtplugDeviceFeature : public QObject 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | ButtplugDeviceFeature(); 21 | ButtplugDeviceFeature(ButtplugDevice *device, ButtplugDeviceFeatureType type, int index, int featureLevels = 1); 22 | ~ButtplugDeviceFeature(); 23 | 24 | ButtplugDevice * device; 25 | ButtplugDeviceFeatureType type; 26 | int index; 27 | int levels; 28 | 29 | QString getName(); 30 | void dispatch(Event event); 31 | ButtplugFeatureParams * getParamsFor(Event event); 32 | void setVibration(double level); 33 | }; 34 | 35 | #endif // BUTTPLUGDEVICEFEATURE_H 36 | -------------------------------------------------------------------------------- /Source/buttplug/buttpluginterface.h: -------------------------------------------------------------------------------- 1 | #ifndef BUTTPLUGINTERFACE_H 2 | #define BUTTPLUGINTERFACE_H 3 | 4 | #include 5 | #include 6 | 7 | class ButtplugDeviceFeature; 8 | class ButtplugDevice; 9 | 10 | class ButtplugInterface : public QObject 11 | { 12 | Q_OBJECT 13 | public: 14 | explicit ButtplugInterface(QObject *parent = nullptr); 15 | void openSocket(QString address = "ws://127.0.0.1:12345"); 16 | void closeSocket(); 17 | QVector getAllFeatures(); 18 | ButtplugDeviceFeature * getFeatureByName(const QString & name); 19 | 20 | public slots: 21 | void serverConnected(); 22 | void serverDisconnected(); 23 | void serverError(QAbstractSocket::SocketError error); 24 | void processTextMessage(QString message); 25 | void processDataMessage(QByteArray message); 26 | void startScanning(); 27 | void stopScanning(); 28 | void stopAllDevices(); 29 | void sendStopDeviceCmd(int deviceIndex); 30 | //! 31 | //! \brief sendVibrateCmd vibrates all the vibrators on... 32 | //! \param deviceIndex ..the specified device (by id) at... 33 | //! \param intensity ...the specified intensity (0.0-1.0). 34 | //! 35 | void sendVibrateCmdAll(int deviceIndex, double intensity); 36 | void sendVibrateCmd(int deviceIndex, QVector intensities); 37 | void vibrateAll(double intensity); 38 | 39 | signals: 40 | void connected(); 41 | void disconnected(); 42 | void error(QAbstractSocket::SocketError error, QString message); 43 | void deviceAdded(int deviceIndex); 44 | void deviceRemoved(int deviceIndex); 45 | 46 | private: 47 | void sendTextMessage(QString message); 48 | int getNewRequestId(); 49 | void registerRequestSent(unsigned int id, QJsonObject request); 50 | QJsonObject registerResponseReceived(unsigned int id); 51 | void handleOk(QJsonObject payload); 52 | void handleError(QJsonObject payload); 53 | QJsonObject createMessageWithPayload(QString messageType, QJsonObject payload); 54 | QJsonObject sendMessageWithPayload(QString messageType, QJsonObject payload); 55 | void sendPing(); 56 | void sendHandshake(); 57 | void handleServerInfo(QJsonObject payload); 58 | void handleScanningFinished(); 59 | void requestDeviceList(); 60 | void handleDeviceList(QJsonObject payload); 61 | void handleDeviceAdded(QJsonObject payload); 62 | void handleNewDeviceInfo(QJsonObject deviceJson); 63 | void handleDeviceRemoved(QJsonObject payload); 64 | 65 | QWebSocket socket; 66 | QMap activeRequests; 67 | //! 68 | //! \brief currentlyConnected are we currently connected to the server? 69 | //! 70 | bool currentlyConnected = false; 71 | //! 72 | //! \brief currentlyScanning are we currently scanning on bluetooth or the like? 73 | //! 74 | bool currentlyScanning = false; 75 | QMap attachedDevicesById; 76 | QMap attachedDevicesByName; 77 | }; 78 | 79 | #endif // BUTTPLUGINTERFACE_H 80 | -------------------------------------------------------------------------------- /Source/buttplugdeviceconfigdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef BUTTPLUGDEVICECONFIGDIALOG_H 2 | #define BUTTPLUGDEVICECONFIGDIALOG_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class ButtplugDeviceConfigDialog; 8 | } 9 | 10 | class VibratorPulseFeatureParams; 11 | class ButtplugFeatureParams; 12 | class ButtplugDeviceFeature; 13 | 14 | class ButtplugDeviceConfigDialog : public QDialog 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | explicit ButtplugDeviceConfigDialog(QWidget *parent = nullptr); 20 | ~ButtplugDeviceConfigDialog(); 21 | static QVector entries; 22 | static void readInConfigs(); 23 | static VibratorPulseFeatureParams *getDefaultVibratorParams(); 24 | void writeOutConfigs(); 25 | 26 | public slots: 27 | void refreshFeatureList(); 28 | void saveAndClose(); 29 | void deleteParams(ButtplugFeatureParams *); 30 | 31 | private slots: 32 | void on_addButton_clicked(); 33 | 34 | private: 35 | Ui::ButtplugDeviceConfigDialog *ui; 36 | QHash nonDefaultPanels; 37 | void createNewVibratorParams(ButtplugDeviceFeature * feature); 38 | }; 39 | 40 | #endif // BUTTPLUGDEVICECONFIGDIALOG_H 41 | -------------------------------------------------------------------------------- /Source/buttplugdispatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "buttplugdispatcher.h" 2 | #include "buttplugfeatureparams.h" 3 | //#include "vibratorpulsefeatureparams.h" 4 | //#include "buttplugdeviceconfigdialog.h" 5 | #include "buttplug/buttpluginterface.h" 6 | 7 | ButtplugDispatcher::ButtplugDispatcher(ButtplugInterface * nterface, QObject * parent) 8 | : 9 | EventDispatcher(parent), 10 | nterface(nterface) 11 | { 12 | 13 | } 14 | 15 | void ButtplugDispatcher::dispatch(Event event) 16 | { 17 | for (ButtplugDeviceFeature * feature : nterface->getAllFeatures()) 18 | feature->dispatch(event); 19 | 20 | // float intensity = getIntensity(event); 21 | // nterface->vibrateAll(intensity); 22 | // QTimer::singleShot(180, nterface, SLOT(stopAllDevices())); 23 | } 24 | 25 | -------------------------------------------------------------------------------- /Source/buttplugdispatcher.h: -------------------------------------------------------------------------------- 1 | #ifndef BUTTPLUGDISPATCHER_H 2 | #define BUTTPLUGDISPATCHER_H 3 | 4 | #include "eventdispatcher.h" 5 | 6 | class ButtplugInterface; 7 | 8 | class ButtplugDispatcher : public EventDispatcher 9 | { 10 | public: 11 | ButtplugDispatcher(ButtplugInterface * nterface, QObject * parent = nullptr); 12 | void dispatch(Event event); 13 | 14 | private: 15 | //! 16 | //! \brief nterface can't call it 'interface' because that seems to be defined to 'struct' :-o 17 | //! 18 | ButtplugInterface * nterface; 19 | 20 | void triggerEventNow(Event event); 21 | }; 22 | 23 | #endif // BUTTPLUGDISPATCHER_H 24 | -------------------------------------------------------------------------------- /Source/buttplugfeatureparams.h: -------------------------------------------------------------------------------- 1 | #ifndef BUTTPLUGDEVICEPARAMS_H 2 | #define BUTTPLUGDEVICEPARAMS_H 3 | 4 | #include "buttplug/buttplugdevicefeature.h" 5 | 6 | #define PARAMS_TYPE_SETTING "Params type" 7 | #define PARAMS_ENABLED_SETTING "Parameters enabled" 8 | #define DEVICE_NAME_SETTING "Device name" 9 | #define FEATURE_NAME_SETTING "Feature name" 10 | #define SYNC_ADJUST_SETTING "Synchronisation adjustment" 11 | 12 | class QHBoxLayout; 13 | class QCheckBox; 14 | class QComboBox; 15 | class QSpinBox; 16 | class QBoxLayout; 17 | class QToolButton; 18 | 19 | namespace ButtplugDeviceParameter { 20 | enum Type { 21 | VibratorPulse, 22 | StrokerRange, 23 | RotatorBurst 24 | }; 25 | } 26 | 27 | class ButtplugFeatureParams : public QObject 28 | { 29 | Q_OBJECT 30 | public: 31 | explicit ButtplugFeatureParams(QObject *parent, ButtplugDeviceFeatureType featureType, ButtplugDeviceParameter::Type type, bool enabled = true, short syncAdjust = 0); 32 | explicit ButtplugFeatureParams(QObject *parent, ButtplugDeviceFeature * feature, ButtplugDeviceParameter::Type type, bool enabled = true, short syncAdjust = 0); 33 | ButtplugDeviceFeatureType featureType; 34 | ButtplugDeviceParameter::Type paramsType; 35 | bool enabled; 36 | QString deviceName; 37 | QString featureName; 38 | int eventType; 39 | short syncAdjust = 0; 40 | //! 41 | //! \brief isDefaultParams are these parameters intended for new or uncustomised devices/features? 42 | //! \return true for yes 43 | //! 44 | bool isDefaultParams(); 45 | 46 | virtual void writeSettingsGroup(QSettings & settings); 47 | //! 48 | //! \brief fromSettingsGroup creates an instance of a ButtplugFeatureParams from a QSettings group 49 | //! \param settings the QSettings. It is expected that 'beginGroup' has been called on this QSettings so that 50 | //! the values of the parameters can be accessed directly using 'value()'. 51 | //! \return 52 | //! 53 | static ButtplugFeatureParams * fromSettingsGroup(QSettings & settings); 54 | virtual void readSettingsGroup(QSettings & settings); 55 | 56 | //this should really belong to a separate class like ButtplugFeatureBehaviour 57 | virtual void handleEvent(Event event, ButtplugDeviceFeature * feature) = 0; 58 | 59 | signals: 60 | void aboutToDelete(ButtplugFeatureParams *); 61 | 62 | protected: 63 | virtual void setUiEnabled(bool enabled) = 0; 64 | QHBoxLayout * layout; 65 | QCheckBox *enableCheckBox; 66 | QComboBox *eventTypeComboBox; 67 | QSpinBox *syncAdjustSpinBox; 68 | 69 | QToolButton *deleteButton; 70 | public: 71 | virtual void createUi(QWidget *parentWidget, QBoxLayout *parentLayout); 72 | void addDeleteButton(QWidget *parent, QHBoxLayout * layout); 73 | void adoptUi(QHBoxLayout *layout, QCheckBox *enableCheckBox, QComboBox *eventTypeComboBox, QSpinBox *syncAdjustSpinBox); 74 | virtual void adoptUiValues(); 75 | virtual void connectWidgetSignals(); 76 | virtual void updateUiFromData(); 77 | virtual void copyParamsFrom(ButtplugFeatureParams * other); 78 | 79 | public slots: 80 | void newEnabledState(bool enabled); 81 | void newEventType(int eventType); 82 | void deleteRequested(); 83 | 84 | private slots: 85 | void on_SyncAdjustChanged(int newValue); 86 | }; 87 | 88 | #endif // BUTTPLUGDEVICEPARAMS_H 89 | -------------------------------------------------------------------------------- /Source/chmlhandler.cpp: -------------------------------------------------------------------------------- 1 | #include "chmlhandler.h" 2 | #include 3 | #include "globals.h" 4 | #include "mainwindow.h" 5 | 6 | CHMLHandler::CHMLHandler() 7 | { 8 | } 9 | 10 | bool CHMLHandler::startDocument() 11 | { 12 | inEventsSection = inEvent = false; 13 | currentEventProperty = "none"; 14 | return true; 15 | } 16 | 17 | bool CHMLHandler::startElement( const QString &, const QString & name, const QString &, const QXmlAttributes &atts) 18 | { 19 | if (name == "events") 20 | { 21 | inEventsSection = true; 22 | } 23 | else if (name == "event") 24 | { 25 | currentEvent = new Event; 26 | inEvent = true; 27 | } 28 | else if (name == "timestamp" || 29 | name == "type" || 30 | name == "value") 31 | { 32 | currentEventProperty = name; 33 | } 34 | else if (name == "metadata") 35 | { 36 | inMetadata = true; 37 | currentEvent->metadata = new EventMetadata; 38 | } 39 | else if (name == "tempo" || 40 | name == "numerator" || 41 | name == "denominator" || 42 | name == "startsNewPattern" || 43 | name == "startsNewRound" || 44 | name == "endsRound") 45 | { 46 | currentEventProperty = name; 47 | } 48 | else if (name == "pattern") 49 | { 50 | currentEvent->metadata->patternIndex = atts.value(QString(),"patternIndex").toInt(); 51 | currentEventProperty = name; 52 | } 53 | else 54 | { 55 | qDebug() << name << " is not a recognised XML node."; 56 | return false; 57 | } 58 | 59 | return true; 60 | } 61 | 62 | bool CHMLHandler::characters ( const QString & text ) 63 | { 64 | if (inEventsSection && inEvent && currentEventProperty != "none") 65 | { 66 | if (currentEventProperty == "timestamp") 67 | currentEvent->timestamp = text.toLong(); 68 | else if (currentEventProperty == "type") 69 | currentEvent->setTypeRaw(text.toInt()); 70 | else if (currentEventProperty == "value") 71 | currentEvent->value = text.toInt(); 72 | else if (inMetadata) 73 | { 74 | if (currentEventProperty == "tempo") 75 | currentEvent->metadata->tempo = text.toFloat(); 76 | else if (currentEventProperty == "numerator") 77 | currentEvent->metadata->valueNumerator = text.toInt(); 78 | else if (currentEventProperty == "denominator") 79 | currentEvent->metadata->valueDenominator = text.toInt(); 80 | else if (currentEventProperty == "startsNewPattern") 81 | currentEvent->metadata->startsNewPattern = text == "true"; 82 | else if (currentEventProperty == "startsNewRound") 83 | currentEvent->metadata->startsNewRound = text == "true"; 84 | else if (currentEventProperty == "endsRound") 85 | currentEvent->metadata->endsRound = text == "true"; 86 | else if (currentEventProperty == "pattern") 87 | currentEvent->metadata->patternName = text; 88 | } 89 | } 90 | 91 | return true; 92 | } 93 | 94 | bool CHMLHandler::endElement( const QString & , const QString & name, const QString &) 95 | { 96 | if (name == "events") 97 | { 98 | inEventsSection = false; 99 | } 100 | else if (name == "event") 101 | { 102 | mainWindow->addEventToTable(*currentEvent); 103 | delete currentEvent; 104 | currentEvent = NULL; 105 | inEvent = false; 106 | } 107 | else if (name == "metadata") 108 | { 109 | inMetadata = false; 110 | } 111 | else if (name == "timestamp" || 112 | name == "type" || 113 | name == "value" || 114 | name == "tempo" || 115 | name == "numerator" || 116 | name == "denominator" || 117 | name == "startsNewPattern" || 118 | name == "startsNewRound" || 119 | name == "endsRound" || 120 | name == "pattern") 121 | { 122 | currentEventProperty = "none"; 123 | } 124 | else 125 | { 126 | qDebug() << name << " is not a recognised XML node."; 127 | return false; 128 | } 129 | 130 | return true; 131 | } 132 | -------------------------------------------------------------------------------- /Source/chmlhandler.h: -------------------------------------------------------------------------------- 1 | #ifndef CHMLHANDLER_H 2 | #define CHMLHANDLER_H 3 | 4 | #include 5 | 6 | class Event; 7 | 8 | class CHMLHandler : public QXmlDefaultHandler 9 | { 10 | public: 11 | CHMLHandler(); 12 | 13 | bool startDocument(); 14 | 15 | bool startElement( const QString & namespaceURI, const QString & name, const QString & qName, const QXmlAttributes & atts ); 16 | 17 | bool characters ( const QString & text ); 18 | 19 | bool endElement( const QString & namespaceURI, const QString & name, const QString & qName ); 20 | 21 | private: 22 | bool inEventsSection; 23 | bool inEvent; 24 | bool inMetadata; 25 | QString currentEventProperty; 26 | Event * currentEvent; 27 | 28 | }; 29 | 30 | #endif // CHMLHANDLER_H 31 | -------------------------------------------------------------------------------- /Source/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | #include "beatanalysis.h" 5 | 6 | #define SEMIQUAVER (1.0/4.0) 7 | #define QUAVER_TRIPLET (1.0/3.0) 8 | #define QUAVER (1.0/2.0) 9 | #define CROCHET_TRIPLET (2.0/3.0) 10 | #define THREE_SEMIQUAVERS (3.0/4.0) 11 | #define CROCHET 1 12 | #define FIVE_SEMIQUAVERS (5.0/4.0) 13 | #define TWO_CROCHET_TRIPLETS (4.0/3.0) 14 | #define THREE_QUAVERS (3.0/2.0) 15 | #define TWO_BEATS 2 16 | #define THREE_BEATS 3 17 | #define FOUR_BEATS 4 18 | #define FIVE_BEATS 5 19 | #define SIX_BEATS 6 20 | #define SEVEN_BEATS 7 21 | #define EIGHT_BEATS 8 22 | //replaced by 'break length detection': 23 | //#define TWELVE_BEATS 12 24 | //#define SIXTEEN_BEATS 16 25 | //#define TWENTY_FOUR_BEATS 24 26 | //#define THIRTY_TWO_BEATS 32 27 | 28 | #ifdef CH_GROOVE 29 | #define SEVEN_SEMIQUAVERS (7.0/4.0) 30 | #define NINE_SEMIQUAVERS (9.0/4.0) 31 | #define SEVENTEEN_SEMIQUAVERS (17.0/4.0) 32 | #endif 33 | 34 | 35 | 36 | #endif // CONFIG_H 37 | -------------------------------------------------------------------------------- /Source/croppedvideosurface.h: -------------------------------------------------------------------------------- 1 | #ifndef CROPPEDVIDEOSURFACE_H 2 | #define CROPPEDVIDEOSURFACE_H 3 | 4 | #include 5 | #include 6 | 7 | class CroppedVideoSurface : public QVideoWidget, public QAbstractVideoSurface 8 | { 9 | public: 10 | CroppedVideoSurface(QWidget *widget, QObject *parent = 0); 11 | QList supportedPixelFormats( 12 | QAbstractVideoBuffer::HandleType handleType) const; 13 | bool present(const QVideoFrame &frame); 14 | 15 | public: 16 | bool isFormatSupported(const QVideoSurfaceFormat &format) const; 17 | 18 | bool start(const QVideoSurfaceFormat &format); 19 | void stop(); 20 | 21 | QRect videoRect() const { return targetRect; } 22 | void updateVideoRect(); 23 | 24 | void paint(QPainter *painter); 25 | 26 | private: 27 | QWidget *widget; 28 | QImage::Format imageFormat; 29 | QRect targetRect; 30 | QSize imageSize; 31 | QRect sourceRect; 32 | }; 33 | 34 | #endif // CROPPEDVIDEOSURFACE_H 35 | -------------------------------------------------------------------------------- /Source/customeventaction.h: -------------------------------------------------------------------------------- 1 | #ifndef CUSTOMEVENTACTION_H 2 | #define CUSTOMEVENTACTION_H 3 | 4 | #include 5 | class QHBoxLayout; 6 | class QKeySequenceEdit; 7 | class QSpinBox; 8 | class QCheckBox; 9 | class QWidget; 10 | 11 | #include "event.h" 12 | 13 | class CustomEventAction : public QAction 14 | { 15 | Q_OBJECT 16 | public: 17 | bool operator== (const CustomEventAction & other) const; 18 | CustomEventAction(Event event, bool trigger, bool record, QObject *parent); 19 | CustomEventAction(bool trigger, bool record, QObject *parent); 20 | QHBoxLayout * createGuiEditorLayout(QWidget *parent); 21 | QKeySequenceEdit * getPrimaryKeySequenceEdit() {return primaryKeySequenceEdit;} 22 | QKeySequenceEdit * getSecondaryKeySequenceEdit() {return secondaryKeySequenceEdit;} 23 | QString getCustomActionID(); 24 | QString getPrimaryShortcutID(); 25 | QString getSecondaryShortcutID(); 26 | static CustomEventAction * fromString(const QString & string, QObject *parent); 27 | void addShortcut(QKeySequence newShortcut); 28 | 29 | public slots: 30 | void newTriggerValue(int newValue); 31 | void newRecordValue(int newValue); 32 | void newEventType(int newValue); 33 | void newEventValue(int newValue); 34 | void performAction(bool checked); 35 | 36 | private: 37 | Event actionEvent; 38 | bool shouldTrigger; 39 | bool shouldRecord; 40 | bool useSelectedEventValues; 41 | QWidget * originatingWidget = nullptr; 42 | QSpinBox * eventTypeBox; 43 | QSpinBox * eventValueBox; 44 | QCheckBox * triggerCheckbox; 45 | QCheckBox * recordCheckbox; 46 | QKeySequenceEdit * primaryKeySequenceEdit; 47 | QKeySequenceEdit * secondaryKeySequenceEdit; 48 | }; 49 | 50 | #endif // CUSTOMEVENTACTION_H 51 | -------------------------------------------------------------------------------- /Source/deletedialog.h: -------------------------------------------------------------------------------- 1 | #ifndef DELETEDIALOG_H 2 | #define DELETEDIALOG_H 3 | 4 | #include 5 | class EditorWindow; 6 | class QShortcut; 7 | 8 | namespace Ui { 9 | class DeleteDialog; 10 | } 11 | 12 | class DeleteDialog : public QDialog 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | explicit DeleteDialog(EditorWindow *parent = nullptr); 18 | ~DeleteDialog(); 19 | 20 | virtual void accept() override; 21 | 22 | private slots: 23 | void on_deleteIntervalMergeWithFollowingRadioButton_toggled(bool checked); 24 | 25 | void on_deleteIntervalMergeWithPreceedingRadioButton_toggled(bool checked); 26 | 27 | void on_deleteIntervalMergePreceedingWithFollowingRadioButton_toggled(bool checked); 28 | 29 | void on_deleteIntervalMoveLaterIntervalsRadioButton_toggled(bool checked); 30 | 31 | void on_deleteIntervalMoveEarlierIntervalsRadioButton_toggled(bool checked); 32 | 33 | private: 34 | Ui::DeleteDialog *ui; 35 | EditorWindow * editor; 36 | void showEditorDeletePage(); 37 | void setLabels(); 38 | void setShortcuts(); 39 | void setButtonState(); 40 | QShortcut * mergeFollowingShortcut; 41 | QShortcut * mergePrecedingShortcut; 42 | QShortcut * mergeBothShortcut; 43 | QShortcut * moveLaterShortcut; 44 | QShortcut * moveEarlierShortcut; 45 | QShortcut * doneShortcut; 46 | }; 47 | 48 | #endif // DELETEDIALOG_H 49 | -------------------------------------------------------------------------------- /Source/deletedialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | DeleteDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 493 10 | 156 11 | 12 | 13 | 14 | Delete Interval 15 | 16 | 17 | 18 | 19 | 20 | Merge with following interval (remove the beat at the end of interval) 21 | 22 | 23 | true 24 | 25 | 26 | 27 | 28 | 29 | 30 | Merge with preceeding interval (remove the beat at the beginning of interval) 31 | 32 | 33 | 34 | 35 | 36 | 37 | Merge preceeding interval with following interval (remove both ends of interval) 38 | 39 | 40 | 41 | 42 | 43 | 44 | Move later beats to fill the gap (all beats to the right of the selected beat will be moved left) 45 | 46 | 47 | 48 | 49 | 50 | 51 | Move earlier beats to fill the gap (all beats to the left of the selected beat will be moved right) 52 | 53 | 54 | 55 | 56 | 57 | 58 | Qt::Horizontal 59 | 60 | 61 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | buttonBox 71 | accepted() 72 | DeleteDialog 73 | accept() 74 | 75 | 76 | 248 77 | 254 78 | 79 | 80 | 157 81 | 274 82 | 83 | 84 | 85 | 86 | buttonBox 87 | rejected() 88 | DeleteDialog 89 | reject() 90 | 91 | 92 | 316 93 | 260 94 | 95 | 96 | 286 97 | 274 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /Source/enableidentifyintervalsdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef ENABLEIDENTIFYINTERVALSDIALOG_H 2 | #define ENABLEIDENTIFYINTERVALSDIALOG_H 3 | 4 | #include 5 | 6 | class AbstractNewBeatValueWidget; 7 | class NewCustomBeatValueWidget; 8 | class BeatValue; 9 | 10 | namespace Ui { 11 | class EnableIdentifyIntervalsDialog; 12 | } 13 | 14 | class EnableIdentifyIntervalsDialog : public QDialog 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | explicit EnableIdentifyIntervalsDialog(int valueInMs, double currentTempo, QWidget *parent = nullptr); 20 | ~EnableIdentifyIntervalsDialog(); 21 | 22 | public slots: 23 | void accept() override; 24 | 25 | private slots: 26 | void on_tempoSpinBox_valueChanged(double arg1); 27 | void on_intervalNumeratorSpinBox_valueChanged(int); 28 | AbstractNewBeatValueWidget * selectBestMatch(); 29 | void ensureSingleSelection(); 30 | void newValueSelected(AbstractNewBeatValueWidget *); 31 | 32 | void on_intervalNameEdit_textChanged(const QString &arg1); 33 | 34 | void on_originalTempoButton_clicked(); 35 | 36 | void on_perfectMatchTempoButton_clicked(); 37 | 38 | void on_minimumNecessaryChangeTempoButton_clicked(); 39 | 40 | void on_updateTempoCheckBox_stateChanged(int arg1); 41 | 42 | void on_openAdjustDialogCheckBox_stateChanged(int); 43 | 44 | void on_openAdjustDialogCheckBox_clicked(); 45 | 46 | private: 47 | Ui::EnableIdentifyIntervalsDialog *ui; 48 | int valueInMs; 49 | double tempo; 50 | const double originalTempo; 51 | double lastNonOriginalTempo; 52 | double valueInBeats; 53 | QVector valueWidgets; 54 | NewCustomBeatValueWidget * customValueWidget; 55 | AbstractNewBeatValueWidget * currentlySelectedWidget; 56 | void calculateValueInBeats(); 57 | double getSelectedValueInBeats(); 58 | void updateDescriptiveText(AbstractNewBeatValueWidget *); 59 | void updateCheckboxes(); 60 | static BeatValue * getExistingMatchingValue(float value); 61 | static bool hasExistingMatchingValue(float value); 62 | BeatValue getSelectedValue(); 63 | BeatValue createSelectedValue(); 64 | double getPerfectMatchTempo(); 65 | QVector getMaskingValues(); 66 | QString getMaskingValuesString(); 67 | bool selectedValueIsMasked(); 68 | bool tempoChangedLots(); 69 | QString askAboutBadMatch(); 70 | bool askAboutBigTempoChange(); 71 | bool confirmChoiceToDisableMaskingValues(); 72 | QString askAboutMaskingValues(); 73 | }; 74 | 75 | #endif // ENABLEIDENTIFYINTERVALSDIALOG_H 76 | -------------------------------------------------------------------------------- /Source/event.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTSTRUCT_H 2 | #define EVENTSTRUCT_H 3 | 4 | #include "eventmetadata.h" 5 | #include "globals.h" 6 | 7 | enum e_eventType 8 | { 9 | EVENT_RESET_TIMECODE, 10 | EVENT_NORMAL_WAND_PULSE, 11 | EVENT_NORMAL_WAND_PULSE_END, 12 | EVENT_TIMED_WAND_PULSE, 13 | EVENT_TIMED_WAND_PULSE_END, 14 | EVENT_EDGE_BEGIN, 15 | EVENT_EDGE_END, 16 | EVENT_ANAL_VIBRATOR_PULSE, 17 | EVENT_ANAL_VIBRATOR_PULSE_END, 18 | EVENT_INFLATABLE_BUTT_PLUG_INFLATE, 19 | EVENT_INFLATABLE_BUTT_PLUG_DEFLATE, 20 | EVENT_AIR_PUMP_ON, 21 | EVENT_AIR_PUMP_OFF, 22 | EVENT_RECORDING_MISTAKE, 23 | EVENT_UNUSED, 24 | EVENT_STROKER_WAYPOINT, //for things like handy, launch etc. 25 | EVENT_STROKE_UP, //A stroke moving from the base of the penis towards the tip 26 | EVENT_STROKE_DOWN, //A stroke moving from the tip of the penis towards the base 27 | EVENT_ENUM_SIZE 28 | }; 29 | 30 | class Event 31 | { 32 | public: 33 | Event(); 34 | Event(long timestamp, unsigned char type, short value); 35 | Event(long timestamp, unsigned char type, short value, bool optional); 36 | Event(long timestamp, unsigned char type, short value, EventMetadata metadata); 37 | 38 | bool operator==(const Event &) const; 39 | 40 | QString toAsciiString(); 41 | QString toBinary(); 42 | //! used for hashing the events table to detect changes 43 | int toHash(); 44 | 45 | long timestamp; 46 | 47 | static bool writeAllToXml(QString filename); 48 | 49 | private: 50 | char typeData; 51 | public: 52 | unsigned char type() const; 53 | QString typeName() const; 54 | void setType(unsigned char newType); 55 | void setTypeRaw(char newTypeData); 56 | short value; 57 | short maxPossibleValue(); 58 | bool optional() const; 59 | bool isOptional() const; 60 | void setOptional(bool optionality); 61 | EventMetadata * metadata; 62 | }; 63 | 64 | #endif // EVENTSTRUCT_H 65 | -------------------------------------------------------------------------------- /Source/eventdatamodel.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDATAMODEL_H 2 | #define EVENTDATAMODEL_H 3 | 4 | #include 5 | class Event; 6 | 7 | class eventDataModel : public QAbstractTableModel 8 | { 9 | Q_OBJECT 10 | public: 11 | explicit eventDataModel(QObject *parent = 0); 12 | int rowCount ( const QModelIndex & parent = QModelIndex() ) const; 13 | int columnCount ( const QModelIndex & parent = QModelIndex() ) const; 14 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 15 | Qt::ItemFlags flags(const QModelIndex &index) const; 16 | QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; 17 | bool setData(const QModelIndex &index, const QVariant &value, int role); 18 | Event eventFromIndex(const QModelIndex &index); 19 | void setEventAtIndex(const QModelIndex & indexToChange, const Event & event); 20 | void addEvent(const Event eventToAdd); 21 | void removeEvent(int index); 22 | //bool removeRow(); 23 | //bool insertRows(int row, int count, const QModelIndex &parent); 24 | bool removeRows ( int row, int count, const QModelIndex & parent = QModelIndex() ); 25 | 26 | signals: 27 | 28 | public slots: 29 | 30 | }; 31 | 32 | #endif // EVENTDATAMODEL_H 33 | -------------------------------------------------------------------------------- /Source/eventdataproxymodel.cpp: -------------------------------------------------------------------------------- 1 | #include "eventdataproxymodel.h" 2 | #include "eventdatamodel.h" 3 | #include "event.h" 4 | 5 | EventDataProxyModel::EventDataProxyModel(QObject *parent) : 6 | QSortFilterProxyModel(parent) 7 | { 8 | source = static_cast(sourceModel()); 9 | } 10 | 11 | Event EventDataProxyModel::eventFromIndex(const QModelIndex &index) 12 | { 13 | if (!source) 14 | source = static_cast(sourceModel()); 15 | return source->eventFromIndex(mapToSource(index)); 16 | } 17 | 18 | void EventDataProxyModel::setEventAtIndex(const QModelIndex & indexToChange, const Event & event) 19 | { 20 | if (!source) 21 | source = static_cast(sourceModel()); 22 | source->setEventAtIndex(mapToSource(indexToChange),event); 23 | } 24 | 25 | void EventDataProxyModel::addEvent(const Event eventToAdd) 26 | { 27 | if (!source) 28 | source = static_cast(sourceModel()); 29 | source->addEvent(eventToAdd); 30 | } 31 | 32 | void EventDataProxyModel::removeEvent(int indexToDelete) 33 | { 34 | if (!source) 35 | source = static_cast(sourceModel()); 36 | int otherRow = mapToSource(index(indexToDelete,0)).row(); 37 | source->removeEvent(otherRow); 38 | } 39 | -------------------------------------------------------------------------------- /Source/eventdataproxymodel.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDATAPROXYMODEL_H 2 | #define EVENTDATAPROXYMODEL_H 3 | 4 | #include 5 | 6 | class Event; 7 | class eventDataModel; 8 | 9 | class EventDataProxyModel : public QSortFilterProxyModel 10 | { 11 | Q_OBJECT 12 | public: 13 | explicit EventDataProxyModel(QObject *parent = 0); 14 | 15 | Event eventFromIndex(const QModelIndex & index); 16 | void setEventAtIndex(const QModelIndex & indexToChange, const Event & event); 17 | void addEvent(const Event eventToAdd); 18 | void removeEvent(int indexToDelete); 19 | 20 | signals: 21 | 22 | public slots: 23 | 24 | private: 25 | eventDataModel * source; 26 | }; 27 | 28 | #endif // EVENTDATAPROXYMODEL_H 29 | -------------------------------------------------------------------------------- /Source/eventdispatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "eventdispatcher.h" 2 | 3 | EventDispatcher::EventDispatcher(QObject *parent) : QObject(parent) 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /Source/eventdispatcher.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDISPATCHER_H 2 | #define EVENTDISPATCHER_H 3 | 4 | #include "event.h" 5 | 6 | class EventDispatcher : public QObject 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit EventDispatcher(QObject *parent = nullptr); 11 | virtual void dispatch(Event event) = 0; 12 | 13 | signals: 14 | 15 | }; 16 | 17 | #endif // EVENTDISPATCHER_H 18 | -------------------------------------------------------------------------------- /Source/eventmetadata.cpp: -------------------------------------------------------------------------------- 1 | #include "eventmetadata.h" 2 | 3 | EventMetadata::EventMetadata() 4 | : 5 | tempo(0), 6 | valueNumerator(0), 7 | valueDenominator(0), 8 | startsNewPattern(false), 9 | startsNewRound(false), 10 | endsRound(false), 11 | patternName(QString()), 12 | patternIndex(0) 13 | { 14 | //just the LIST above... 15 | } 16 | -------------------------------------------------------------------------------- /Source/eventmetadata.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTMETADATA_H 2 | #define EVENTMETADATA_H 3 | 4 | struct EventMetadata 5 | { 6 | EventMetadata(); 7 | float tempo; 8 | unsigned char valueNumerator; 9 | unsigned char valueDenominator; 10 | bool startsNewPattern; 11 | bool startsNewRound; 12 | bool endsRound; 13 | QString patternName; 14 | unsigned char patternIndex; 15 | }; 16 | 17 | #endif // EVENTMETADATA_H 18 | -------------------------------------------------------------------------------- /Source/eventtabledelegate.cpp: -------------------------------------------------------------------------------- 1 | #include "eventtabledelegate.h" 2 | #include 3 | #include 4 | #include 5 | 6 | EventTableDelegate::EventTableDelegate(QObject *parent) : 7 | QStyledItemDelegate(parent) 8 | { 9 | } 10 | 11 | QWidget * EventTableDelegate::createEditor(QWidget *parent, 12 | const QStyleOptionViewItem & option, 13 | const QModelIndex & index) const 14 | { 15 | if (index.column() == 1 || 16 | index.column() == 2 || 17 | index.column() == 3) 18 | { 19 | QSpinBox * editor = new QSpinBox(parent); 20 | editor->setMinimum(0); 21 | if (index.column() == 1) 22 | editor->setMaximum(INT_MAX); 23 | else if (index.column() == 2) 24 | editor->setMaximum(0xFF); 25 | else if (index.column() == 3) 26 | editor->setMaximum(0xFFFF); 27 | 28 | int value = index.data(Qt::EditRole).toInt(); 29 | editor->setValue(value); 30 | 31 | return editor; 32 | } 33 | else if (index.column() == 4) 34 | { 35 | QCheckBox * editor = new QCheckBox(parent); 36 | return editor; 37 | } 38 | else 39 | return QStyledItemDelegate::createEditor(parent, option, index); 40 | } 41 | 42 | void EventTableDelegate::setEditorData(QWidget *editor, 43 | const QModelIndex &index) const 44 | { 45 | if (index.column() == 1 || 46 | index.column() == 2 || 47 | index.column() == 3 ) 48 | { 49 | int value = index.model()->data(index, Qt::EditRole).toInt(); 50 | QSpinBox * thisEditor = qobject_cast(editor); 51 | thisEditor->setValue(value); 52 | } 53 | else if (index.column() == 4) 54 | { 55 | bool value = index.model()->data(index, Qt::EditRole).toBool(); 56 | QCheckBox * thisEditor = qobject_cast(editor); 57 | thisEditor->setChecked(value); 58 | } 59 | else 60 | QStyledItemDelegate::setEditorData(editor, index); 61 | } 62 | 63 | void EventTableDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, 64 | const QModelIndex &index) const 65 | { 66 | if (index.column() == 1 || 67 | index.column() == 2 || 68 | index.column() == 3 ) 69 | { 70 | QSpinBox *spinBox = static_cast(editor); 71 | spinBox->interpretText(); 72 | int value = spinBox->value(); 73 | 74 | model->setData(index, value, Qt::EditRole); 75 | } 76 | else if (index.column() == 4) 77 | { 78 | QCheckBox *checkBox = static_cast(editor); 79 | bool value = checkBox->isChecked(); 80 | 81 | model->setData(index, value, Qt::EditRole); 82 | } 83 | else 84 | QStyledItemDelegate::setModelData(editor, model, index); 85 | } 86 | 87 | void EventTableDelegate::updateEditorGeometry(QWidget *editor, 88 | const QStyleOptionViewItem &option, const QModelIndex &/* index */) const 89 | { 90 | editor->setGeometry(option.rect); 91 | } 92 | -------------------------------------------------------------------------------- /Source/eventtabledelegate.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTTABLEDELEGATE_H 2 | #define EVENTTABLEDELEGATE_H 3 | 4 | #include 5 | 6 | class EventTableDelegate : public QStyledItemDelegate 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit EventTableDelegate(QObject *parent = 0); 11 | 12 | QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, 13 | const QModelIndex &index) const; 14 | 15 | void setEditorData(QWidget *editor, const QModelIndex &index) const; 16 | void setModelData(QWidget *editor, QAbstractItemModel *model, 17 | const QModelIndex &index) const; 18 | 19 | void updateEditorGeometry(QWidget *editor, 20 | const QStyleOptionViewItem &option, const QModelIndex &index) const; 21 | 22 | signals: 23 | 24 | public slots: 25 | 26 | }; 27 | 28 | #endif // EVENTTABLEDELEGATE_H 29 | -------------------------------------------------------------------------------- /Source/exportbeatmeterdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "exportbeatmeterdialog.h" 2 | #include "ui_exportbeatmeterdialog.h" 3 | #include 4 | #include 5 | #include "event.h" 6 | #include "optionsdialog.h" 7 | #include 8 | 9 | ExportBeatMeterDialog::ExportBeatMeterDialog(QWidget *parent) : 10 | QDialog(parent), 11 | ui(new Ui::ExportBeatMeterDialog), 12 | scene(new QGraphicsScene(this)) 13 | { 14 | ui->setupUi(this); 15 | ui->strokeMeter->setScene(scene); 16 | } 17 | 18 | ExportBeatMeterDialog::~ExportBeatMeterDialog() 19 | { 20 | delete ui; 21 | } 22 | 23 | void ExportBeatMeterDialog::on_updateButton_clicked() 24 | { 25 | updateScene(); 26 | } 27 | 28 | void ExportBeatMeterDialog::updateScene() 29 | { 30 | while (!strokeMarkers.isEmpty()) 31 | { 32 | QAbstractGraphicsShapeItem * removeMe = strokeMarkers.last(); 33 | scene->removeItem(removeMe); 34 | strokeMarkers.removeLast(); 35 | delete removeMe; 36 | } 37 | 38 | int markerHeight = OptionsDialog::getBeatMarkerHeight(); 39 | int markerWidth = OptionsDialog::getBeatMarkerWidth(); 40 | for (Event event : events) { 41 | int hOffset = ((event.timestamp * OptionsDialog::getBeatMeterSpeed()) / 1000) - markerWidth / 2;//offset by half the width so the markers are centered horizontally on their timestamps 42 | int vOffset = 0 - (markerHeight / 2); //offset by half the height so the markers are centered vertically on 0 43 | QRectF rectLocation(hOffset,vOffset,markerWidth,markerHeight); 44 | QAbstractGraphicsShapeItem * marker; 45 | if (OptionsDialog::useSquareBeatMarkers()) 46 | marker = new QGraphicsRectItem(rectLocation); 47 | else 48 | marker = new QGraphicsEllipseItem(rectLocation); 49 | QPen pen; 50 | pen.setWidth(2); 51 | marker->setPen(pen); 52 | QColor color = getColor(event); 53 | QBrush brush(color); 54 | marker->setBrush(brush); 55 | strokeMarkers.append(marker); 56 | scene->addItem(marker); 57 | } 58 | 59 | int width = OptionsDialog::getBeatMeterWidth(); 60 | QRectF bounds = scene->itemsBoundingRect(); 61 | bounds.setLeft(bounds.left() - width); 62 | bounds.setRight(bounds.right() + width); 63 | scene->setSceneRect(bounds); 64 | scene->update(); 65 | } 66 | 67 | QColor ExportBeatMeterDialog::getColor(Event) 68 | { 69 | //TODO: Implement some fun colouring here 70 | return QColor(Qt::lightGray); 71 | } 72 | 73 | void ExportBeatMeterDialog::on_exportButton_clicked() 74 | { 75 | QString exportFilenamePrefix = QFileDialog::getSaveFileName(this, tr("Select image sequence base filename"), 76 | "~/Pictures", 77 | tr("Portable Network Graphics (*.png)")); 78 | if (exportFilenamePrefix.isEmpty()) 79 | return; //save cancelled 80 | if (exportFilenamePrefix.endsWith(".png")) 81 | { 82 | exportFilenamePrefix.chop(4); 83 | } 84 | int height = OptionsDialog::getBeatMeterHeight(); 85 | int width = OptionsDialog::getBeatMeterWidth(); 86 | int pixelsPerSecond = OptionsDialog::getBeatMeterSpeed(); 87 | double fps = OptionsDialog::getBeatMeterFrameRate(); 88 | long frameCounter = 0; 89 | qreal beginning = scene->itemsBoundingRect().left() - width; 90 | qreal end = scene->itemsBoundingRect().right(); 91 | qreal top = 0 - (float) height / 2; 92 | qreal hPos = beginning; 93 | 94 | while (hPos < end) 95 | { 96 | ++frameCounter; 97 | hPos = beginning + (frameCounter * pixelsPerSecond) / fps; 98 | scene->setSceneRect(QRectF(hPos, top, width, height)); 99 | QImage image(scene->sceneRect().size().toSize(), QImage::Format_ARGB32); // Create the image with the exact size of the shrunk scene 100 | image.fill(Qt::transparent); // Start all pixels transparent 101 | 102 | QPainter painter(&image); 103 | scene->render(&painter); 104 | image.save(QString("%1_%2.png").arg(exportFilenamePrefix).arg(frameCounter)); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Source/exportbeatmeterdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef EXPORTBEATMETERDIALOG_H 2 | #define EXPORTBEATMETERDIALOG_H 3 | 4 | #include 5 | #include "event.h" 6 | 7 | class QGraphicsScene; 8 | class QAbstractGraphicsShapeItem; 9 | 10 | namespace Ui { 11 | class ExportBeatMeterDialog; 12 | } 13 | 14 | class ExportBeatMeterDialog : public QDialog 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | explicit ExportBeatMeterDialog(QWidget *parent = nullptr); 20 | ~ExportBeatMeterDialog(); 21 | 22 | private slots: 23 | void on_updateButton_clicked(); 24 | 25 | void on_exportButton_clicked(); 26 | 27 | private: 28 | Ui::ExportBeatMeterDialog *ui; 29 | QGraphicsScene * scene; 30 | QVector strokeMarkers; 31 | QColor getColor(Event event); 32 | 33 | void updateScene(); 34 | }; 35 | 36 | #endif // EXPORTBEATMETERDIALOG_H 37 | -------------------------------------------------------------------------------- /Source/exportbeatmeterdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | ExportBeatMeterDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 879 10 | 141 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Export 23 | 24 | 25 | 26 | 27 | 28 | 29 | Update 30 | 31 | 32 | 33 | 34 | 35 | 36 | Cancel 37 | 38 | 39 | 40 | 41 | 42 | 43 | Qt::Horizontal 44 | 45 | 46 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | QPainter::Antialiasing|QPainter::TextAntialiasing 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | buttonBox 65 | accepted() 66 | ExportBeatMeterDialog 67 | accept() 68 | 69 | 70 | 248 71 | 254 72 | 73 | 74 | 157 75 | 274 76 | 77 | 78 | 79 | 80 | buttonBox 81 | rejected() 82 | ExportBeatMeterDialog 83 | reject() 84 | 85 | 86 | 316 87 | 260 88 | 89 | 90 | 286 91 | 274 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /Source/funscriptwriter.cpp: -------------------------------------------------------------------------------- 1 | #include "funscriptwriter.h" 2 | #include "optionsdialog.h" 3 | #include 4 | 5 | FunscriptWriter::FunscriptWriter(QFile & file, QObject * parent) 6 | : 7 | SyncFileWriter(file, parent) 8 | { 9 | 10 | } 11 | 12 | void FunscriptWriter::writeHeader() 13 | { 14 | //TODO: This whole class could be rewritten using proper JSON handling, but hey, it works... 15 | int topOfRange = OptionsDialog::getRangeTop(); 16 | int bottomOfRange = OptionsDialog::getRangeBottom(); 17 | int rangeAnchor = OptionsDialog::getRangeAnchor(); 18 | int proportionalLength = OptionsDialog::getStrokeLengthProportion(); 19 | QString fileHeader = QString("{\"export_comment\":\"Exported from Cock Heroine using range %1%% to %2%%, %3%% anchor point and %4%% proportional stroke length. " 20 | "Exporting tempo, beat values and non-standard stroke events such as edging and butt plug control events not supported at time of writing.\"," 21 | "\"version\":\"1.0\",\"inverted\":false,\"range\":100,\"actions\":[") 22 | .arg(bottomOfRange) 23 | .arg(topOfRange) 24 | .arg(rangeAnchor) 25 | .arg(proportionalLength); 26 | file.write(fileHeader.toLatin1()); 27 | needPreceedingComma = false; 28 | } 29 | 30 | void FunscriptWriter::addEvent(long timestamp, int position) 31 | { 32 | if (needPreceedingComma) 33 | file.write(","); 34 | QString eventString = funscriptEntryString(timestamp, position); 35 | file.write(eventString.toLatin1()); 36 | needPreceedingComma = true; 37 | } 38 | 39 | void FunscriptWriter::writeFooter() 40 | { 41 | file.write("]}"); 42 | } 43 | 44 | QString FunscriptWriter::funscriptEntryString(long timestamp, int targetLocation) 45 | { 46 | return QString("{\"pos\":%1,\"at\":%2}").arg(targetLocation).arg(timestamp); 47 | } 48 | -------------------------------------------------------------------------------- /Source/funscriptwriter.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNSCRIPTWRITER_H 2 | #define FUNSCRIPTWRITER_H 3 | 4 | #include "syncfilewriter.h" 5 | 6 | class FunscriptWriter : public SyncFileWriter 7 | { 8 | Q_OBJECT 9 | public: 10 | FunscriptWriter(QFile &, QObject * parent = nullptr); 11 | virtual void writeHeader(); 12 | virtual void addEvent(long timestamp, int position); 13 | virtual void writeFooter(); 14 | QString funscriptEntryString(long timestamp, int targetLocation); 15 | private: 16 | bool needPreceedingComma = false; 17 | }; 18 | 19 | #endif // FUNSCRIPTWRITER_H 20 | -------------------------------------------------------------------------------- /Source/globals.h: -------------------------------------------------------------------------------- 1 | #ifndef GLOBALS_H 2 | #define GLOBALS_H 3 | 4 | #include "beatpattern.h" 5 | 6 | class QTime; 7 | class QTimer; 8 | class Event; 9 | class BeatTimestamp; 10 | class BeatValue; 11 | class BeatInterval; 12 | class UniqueBeatInterval; 13 | class QMediaPlayer; 14 | class EditorWindow; 15 | class MainWindow; 16 | class QLCDNumber; 17 | class EventDispatcher; 18 | 19 | extern QVector events; 20 | QVector filteredEvents(const QList & types, const QVector & eventsToFilter = events); 21 | extern QVector beatTimestamps; 22 | //! 23 | //! \brief beatValues a list of all the possible 'types' of interval length we recognise. 24 | //! At time of writing these are populated in the beat analysis code, but they could (also) be 25 | //! loaded from a save file. New types of value are added via the editor (values table). 26 | //! Also can be enabled or created from the Enable/Identify Values Dialog. 27 | //! 28 | extern QVector beatValues; 29 | //! 30 | //! \brief beatIntervals This is just a list of the 'spaces' between the beats (BeatTimestamps). 31 | //! 32 | extern QVector beatIntervals; 33 | //! 34 | //! \brief uniqueBeatIntervals If you were to create a graph for all the intervals, with interval length 35 | //! as the x axis and abundance as the y axis, then the idea is you have one uniqueBeatInterval for 36 | //! each peak on that graph. 37 | //! 38 | extern QList uniqueBeatIntervals; 39 | extern QVector beatPatterns; 40 | extern const char * defaultValueShortcuts[2][10]; 41 | 42 | extern QString loadedVideo; 43 | extern QString loadedScript; 44 | 45 | extern QTimer * refreshTimer; 46 | extern QMediaPlayer * videoPlayer; 47 | extern QMediaPlayer * stimSignalPlayer; 48 | extern MainWindow * mainWindow; 49 | extern EditorWindow * editor; 50 | extern QTime * timecode; 51 | extern long timecodeLastStopped; 52 | extern bool currentlyPlaying; 53 | extern long currentTimecode(); 54 | extern void seekToTimestamp (long seekTo); 55 | QVector listOfTimestampDenominators(); 56 | QVector listOfMetadataDenominators(); 57 | extern QLCDNumber * lcdDisplay; //so seekToTimestamp can update it 58 | void setLcdDisplay(int millis); 59 | 60 | BeatValue * getBeatValueByName(const QString & name); 61 | BeatValue * getBeatValueFromMsLength(int length); 62 | BeatValue * getBeatValueFromLengthInBeats(float length); 63 | BeatValue * getBeatValueFromDropdownEntry(const QString & entry); 64 | QString millisToTimecode(unsigned int millis); 65 | 66 | extern QVector dispatchers; 67 | 68 | double tempoToBeatLength(double tempo); 69 | double beatLengthToTempo(double beatLength); 70 | 71 | #endif // GLOBALS_H 72 | -------------------------------------------------------------------------------- /Source/graphicsscenevideodialog.h: -------------------------------------------------------------------------------- 1 | #ifndef GRAPHICSSCENEVIDEODIALOG_H 2 | #define GRAPHICSSCENEVIDEODIALOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace Ui { 9 | class GraphicsSceneVideoDialog; 10 | } 11 | 12 | class GraphicsSceneVideoDialog : public QDialog 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | explicit GraphicsSceneVideoDialog(QWidget *parent = 0); 18 | ~GraphicsSceneVideoDialog(); 19 | void takeVideoOutput(); 20 | QGraphicsScene * videoScene; 21 | QGraphicsVideoItem * videoItem; 22 | void closeEvent(QCloseEvent *); 23 | void resizeEvent(QResizeEvent*); 24 | void handleResize(); 25 | void mousePressEvent(QMouseEvent *); 26 | void keyPressEvent(QKeyEvent *); 27 | bool eventFilter(QObject *obj, QEvent *event); 28 | void toggleFullscreen(); 29 | void show(); 30 | 31 | public slots: 32 | void maskBeatMeter(bool dark = false); 33 | void setMaskOrNot (bool); 34 | 35 | private: 36 | Ui::GraphicsSceneVideoDialog *ui; 37 | QGraphicsRectItem * maskingRectangle; 38 | QGraphicsView * graphicsView; 39 | }; 40 | 41 | #endif // GRAPHICSSCENEVIDEODIALOG_H 42 | -------------------------------------------------------------------------------- /Source/graphicsscenevideodialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | GraphicsSceneVideoDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 20 | QFrame::NoFrame 21 | 22 | 23 | QFrame::Plain 24 | 25 | 26 | 0 27 | 28 | 29 | Qt::ScrollBarAlwaysOff 30 | 31 | 32 | Qt::ScrollBarAlwaysOff 33 | 34 | 35 | 36 | 37 | 38 | 39 | Qt::Horizontal 40 | 41 | 42 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | buttonBox 52 | accepted() 53 | GraphicsSceneVideoDialog 54 | accept() 55 | 56 | 57 | 248 58 | 254 59 | 60 | 61 | 157 62 | 274 63 | 64 | 65 | 66 | 67 | buttonBox 68 | rejected() 69 | GraphicsSceneVideoDialog 70 | reject() 71 | 72 | 73 | 316 74 | 260 75 | 76 | 77 | 286 78 | 274 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /Source/handycsvwriter.cpp: -------------------------------------------------------------------------------- 1 | #include "handycsvwriter.h" 2 | #include 3 | 4 | HandyCsvWriter::HandyCsvWriter(QFile & file, QObject * parent) 5 | : 6 | SyncFileWriter(file, parent) 7 | { 8 | 9 | } 10 | 11 | void HandyCsvWriter::writeHeader() 12 | { 13 | QString fileHeader = QString("#Handy sync file. Exported from Cock Heroine\n"); 14 | file.write(fileHeader.toLatin1()); 15 | } 16 | 17 | void HandyCsvWriter::addEvent(long timestamp, int position) 18 | { 19 | QString eventString = csvEntryString(timestamp, position); 20 | file.write(eventString.toLatin1()); 21 | } 22 | 23 | void HandyCsvWriter::writeFooter() 24 | { 25 | //A CSV file needs no footer 26 | } 27 | 28 | QString HandyCsvWriter::csvEntryString(long timestamp, int targetLocation) 29 | { 30 | return QString("%1,%2\n").arg(timestamp).arg(targetLocation); 31 | } 32 | -------------------------------------------------------------------------------- /Source/handycsvwriter.h: -------------------------------------------------------------------------------- 1 | #ifndef HANDYCSVWRITER_H 2 | #define HANDYCSVWRITER_H 3 | 4 | #include "syncfilewriter.h" 5 | 6 | class HandyCsvWriter : public SyncFileWriter 7 | { 8 | Q_OBJECT 9 | public: 10 | HandyCsvWriter(QFile &, QObject * parent = nullptr); 11 | virtual void writeHeader(); 12 | virtual void addEvent(long timestamp, int position); 13 | virtual void writeFooter(); 14 | QString csvEntryString(long timestamp, int targetLocation); 15 | }; 16 | 17 | #endif // HANDYCSVWRITER_H 18 | -------------------------------------------------------------------------------- /Source/helperfunctions.h: -------------------------------------------------------------------------------- 1 | #ifndef HELPERFUNCTIONS_H 2 | #define HELPERFUNCTIONS_H 3 | 4 | #include 5 | class QTableView; 6 | class BeatValue; 7 | 8 | bool isWholeNumber(float numberToCheck); 9 | long int roundToInt(double numberToRound); 10 | float absoluteDifferenceBetween(const double initialValue, const double newValue); 11 | float percentageDifferenceBetween(const double initialValue, const double newValue); 12 | bool isWithinXPercentOf(const double initialValue, const double newValue, const short percentageDifference); 13 | int isPowerOfTwo (unsigned int x); 14 | 15 | int greatestCommonDivisor(int a, int b); 16 | int lowestCommonMultiple(int a, int b); 17 | int lowestCommonMultiple(const QVector & listOfNumbers); 18 | int lowestCommonMultiple(const QVector & listOfNumbers); 19 | 20 | void setColumWidthsFromModel(QTableView * table); 21 | bool multipleRowsSelected(const QModelIndexList & indexList); 22 | bool containsNonContiguousRows(const QModelIndexList & indexList); 23 | void identifyFirstAndLastRows(const QModelIndexList & indexList, int & result_first, int & result_last); 24 | BeatValue * calculateShortestActiveBeatValue(); 25 | 26 | bool midiCanUseTempo(); 27 | void reportTempoGaps(); 28 | 29 | #endif // HELPERFUNCTIONS_H 30 | -------------------------------------------------------------------------------- /Source/intervaldatamodel.h: -------------------------------------------------------------------------------- 1 | #ifndef INTERVALDATAMODEL_H 2 | #define INTERVALDATAMODEL_H 3 | 4 | #include 5 | 6 | class BeatInterval; 7 | 8 | class IntervalDataModel : public QAbstractTableModel 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit IntervalDataModel(QObject *parent = 0); 13 | int rowCount ( const QModelIndex & parent = QModelIndex() ) const; 14 | int columnCount ( const QModelIndex & parent = QModelIndex() ) const; 15 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 16 | Qt::ItemFlags flags(const QModelIndex &index) const; 17 | QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; 18 | bool containsUnknownIntervals(); 19 | bool containsPoorlyMatchedIntervals (float threshold = 40); 20 | 21 | signals: 22 | 23 | public slots: 24 | 25 | }; 26 | 27 | #endif // INTERVALDATAMODEL_H 28 | -------------------------------------------------------------------------------- /Source/keyboardshortcutsdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef KEYBOARDSHORTCUTSDIALOG_H 2 | #define KEYBOARDSHORTCUTSDIALOG_H 3 | 4 | #include 5 | class QKeySequenceEdit; 6 | 7 | namespace Ui { 8 | class KeyboardShortcutsDialog; 9 | } 10 | 11 | class KeyboardShortcutsDialog : public QDialog 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit KeyboardShortcutsDialog(QWidget *parent = 0); 17 | ~KeyboardShortcutsDialog(); 18 | void applyShortcutPrefsFromUi(); 19 | static void applyActionShortcutsFromPrefs(); 20 | void accept(); 21 | 22 | public slots: 23 | void on_addCustomShortcutButtonClicked(); 24 | 25 | private: 26 | Ui::KeyboardShortcutsDialog *ui; 27 | QList * allActions; 28 | QList allPrimaryShortcuts; 29 | QList allSecondaryShortcuts; 30 | static QList *createActionsList(); 31 | void createWidgetList(); 32 | static QString getActionID(QAction * action); 33 | static QString getActionContext(QAction * action); 34 | static QString getActionPrimaryShortcutID(QAction * action); 35 | static QString getActionSecondaryShortcutID(QAction * action); 36 | }; 37 | 38 | #endif // KEYBOARDSHORTCUTSDIALOG_H 39 | -------------------------------------------------------------------------------- /Source/keyboardshortcutsdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | KeyboardShortcutsDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 801 10 | 527 11 | 12 | 13 | 14 | Keyboard Shortcuts 15 | 16 | 17 | 18 | 19 | 20 | true 21 | 22 | 23 | 24 | 25 | 0 26 | 0 27 | 781 28 | 478 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | Qt::Horizontal 39 | 40 | 41 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | buttonBox 51 | accepted() 52 | KeyboardShortcutsDialog 53 | accept() 54 | 55 | 56 | 248 57 | 254 58 | 59 | 60 | 157 61 | 274 62 | 63 | 64 | 65 | 66 | buttonBox 67 | rejected() 68 | KeyboardShortcutsDialog 69 | reject() 70 | 71 | 72 | 316 73 | 260 74 | 75 | 76 | 286 77 | 274 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /Source/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | QCoreApplication::setOrganizationName("Flooble"); 8 | QCoreApplication::setOrganizationDomain("unknown"); 9 | QCoreApplication::setApplicationName("Cock Heroine"); 10 | MainWindow w; 11 | w.setWindowIcon(QIcon(":/WandIcon")); 12 | w.setWindowTitle(QString("Cock Heroine")); 13 | w.show(); 14 | 15 | return a.exec(); 16 | } 17 | -------------------------------------------------------------------------------- /Source/midifilereader.h: -------------------------------------------------------------------------------- 1 | #ifndef MIDIFILEREADER_H 2 | #define MIDIFILEREADER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class MidiFileReader : public QObject 9 | { 10 | public: 11 | MidiFileReader(QString filename, QObject *parent); 12 | quint32 readVariableLengthQuantity(); 13 | void printAsVariableLengthQuantity(unsigned int value); 14 | bool readFile(); 15 | bool handleNewTempo(); 16 | void configureIntegerDivisionCompensation(); 17 | bool handleMidiNoteOn(unsigned char statusByte); 18 | bool createEventNow(unsigned char key, unsigned char velocity, unsigned char channel); 19 | bool handleMetaEvent(); 20 | bool ignoreVariableQuantity(); 21 | bool ignoreFixedQuantity(unsigned int bytesToIgnore); 22 | void advanceCurrentTimeByDivisions(unsigned int numberOfDivisions); 23 | 24 | QFile inputMidiFile; 25 | QDataStream inputMidiData; 26 | 27 | long currentMicroseconds; 28 | 29 | bool usingSMTPE; 30 | 31 | int microsecondsPerBeat; 32 | int microsecondsPerDivision; 33 | 34 | quint8 framesPerSecond; 35 | quint8 ticksPerFrame; 36 | unsigned int ticksPerSecond; 37 | unsigned int framesPerBeat; 38 | 39 | int extraMicroseconds; 40 | int extraMicrosecondsCounter; 41 | int addExtraMicrosecondsEvery; 42 | 43 | quint8 runningStatus; 44 | 45 | static const int firstTrackSizePos; 46 | }; 47 | 48 | #endif // MIDIFILEREADER_H 49 | -------------------------------------------------------------------------------- /Source/midifilewriter.h: -------------------------------------------------------------------------------- 1 | #ifndef MIDIFILEWRITER_H 2 | #define MIDIFILEWRITER_H 3 | 4 | #include 5 | #include 6 | 7 | class MidiFileWriter : public QObject 8 | { 9 | Q_OBJECT 10 | public: 11 | explicit MidiFileWriter(QString filename, QObject *parent = 0); 12 | 13 | bool writeHeader(bool useSMPTE, qint16 framesPerBeat = 0); 14 | quint8 writeVariableLengthQuantity(quint32 value); 15 | quint16 writeTitle(QString title); 16 | bool writeTimeSignature(quint8 numerator, quint8 denominator, quint8 clocksPerMetronomeClick = 24, quint8 demisemiquaversPerCrochet = 8); 17 | bool writeTempo(quint16 BPM, int afterFrames = 0); 18 | bool writeTempo(double BPM, int afterFrames = 0); 19 | bool writeTempo_usPerBeat(quint32 microsecondsPerBeat, int afterFrames = 0); 20 | bool writePatchChange(quint8 channel, quint8 patchNo); 21 | void writeBeat(quint32 timestampInMiliseconds, quint8 note = defaultDrumNote, quint8 channel = 9 /*drums*/, quint8 velocity = 127 /*full whack*/); 22 | void writeBeat_frameDelta(quint32 framesSinceLastBeat, quint8 note = defaultDrumNote, quint8 channel = 9 /*drums*/, quint8 velocity = 127 /*full whack*/); 23 | void writeBeatNow(quint8 note = defaultDrumNote, quint8 channel = 9 /*drums*/, quint8 velocity = 127 /*full whack*/); 24 | void writeEndOfTrack(); 25 | 26 | signals: 27 | 28 | public slots: 29 | 30 | private: 31 | QFile outputMidiFile; 32 | QDataStream outputMidiData; 33 | 34 | long lastPositionWritten; 35 | quint8 runningStatus; 36 | 37 | static const char * const headerTag; 38 | static const char * const trackTag; 39 | static const int firstTrackSizePos; 40 | static quint8 defaultDrumNote; 41 | }; 42 | 43 | #define BASS_DRUM_1 36 44 | #define SIDE_STICK 37 45 | #define ACOUSTIC_SNARE 38 46 | #define ELECTRIC_SNARE 40 47 | #define CLOSED_HIHAT 41 48 | #define OPEN_HIHAT 46 49 | 50 | #endif // MIDIFILEWRITER_H 51 | -------------------------------------------------------------------------------- /Source/newbeatvaluewidget.cpp: -------------------------------------------------------------------------------- 1 | #include "newbeatvaluewidget.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "globals.h" 8 | #include "uniquebeatinterval.h" 9 | #include "beatanalysis.h" 10 | 11 | NewBeatValueWidget::NewBeatValueWidget(QWidget *parent, float valueInBeats, int divisor) 12 | : 13 | AbstractNewBeatValueWidget(valueInBeats, parent), 14 | numerator(1), 15 | divisor(divisor) 16 | { 17 | valueNumeratorLabel = new QLabel(this); 18 | valueNumeratorLabel->setObjectName(QString::fromUtf8("valueNumeratorLabel")); 19 | valueNumeratorLabel->setAlignment(Qt::AlignCenter); 20 | 21 | valueFractionLayout->addWidget(valueNumeratorLabel); 22 | 23 | QLabel * valueSlashLabel = new QLabel(this); 24 | valueSlashLabel->setObjectName(QString::fromUtf8("valueSlashLabel")); 25 | valueSlashLabel->setAlignment(Qt::AlignCenter); 26 | valueSlashLabel->setText(QApplication::translate("EnableIdentifyIntervalsDialog", "/", nullptr)); 27 | 28 | valueFractionLayout->addWidget(valueSlashLabel); 29 | 30 | valueDivisorLabel = new QLabel(this); 31 | valueDivisorLabel->setObjectName(QString::fromUtf8("valueDivisorLabel")); 32 | valueDivisorLabel->setAlignment(Qt::AlignCenter); 33 | valueDivisorLabel->setText(QString::number(divisor)); 34 | 35 | valueFractionLayout->addWidget(valueDivisorLabel); 36 | 37 | 38 | updateForValue(valueInBeats); 39 | } 40 | 41 | void NewBeatValueWidget::updateForValue(float newValueInBeats) 42 | { 43 | valueInBeats = newValueInBeats; 44 | calculateNumerator(); 45 | updateName(); 46 | updateBackground(); 47 | } 48 | 49 | QString NewBeatValueWidget::getName() 50 | { 51 | if (hasExistingMatchingValue()) 52 | { 53 | return AbstractNewBeatValueWidget::getName(); 54 | } 55 | return QString(); 56 | } 57 | 58 | float NewBeatValueWidget::getMatchResistance() 59 | { 60 | return poopness() * divisor; 61 | } 62 | 63 | int NewBeatValueWidget::getNumerator() 64 | { 65 | return numerator; 66 | } 67 | 68 | int NewBeatValueWidget::getDivisor() 69 | { 70 | return divisor; 71 | } 72 | 73 | void NewBeatValueWidget::calculateNumerator() 74 | { 75 | while(true) 76 | { 77 | float currentValue = getCurrentValue(); 78 | float currentDiff = abs(valueInBeats - currentValue); 79 | int bigger = numerator + 1; 80 | float biggerValue = (float) bigger / divisor; 81 | float biggerDiff = abs(valueInBeats - biggerValue); 82 | if (biggerDiff < currentDiff) 83 | { 84 | ++numerator; 85 | continue; 86 | } 87 | if (numerator > 1) 88 | { 89 | int smaller = numerator - 1; 90 | float smallerValue = (float) smaller / divisor; 91 | float smallerDiff = abs(valueInBeats - smallerValue); 92 | if (smallerDiff < currentDiff) 93 | { 94 | --numerator; 95 | continue; 96 | } 97 | } 98 | //the numerator can't be improved by increasing or decreasing - we're done 99 | break; 100 | } 101 | valueNumeratorLabel->setText(QString::number(numerator)); 102 | } 103 | 104 | bool NewBeatValueWidget::hasExistingMatchingValue() 105 | { 106 | for (auto value : beatValues) 107 | { 108 | float thisValue = value.value(); 109 | float myValue = getCurrentValue(); 110 | if (thisValue == myValue) 111 | { 112 | return true; 113 | } 114 | } 115 | return false; 116 | } 117 | 118 | void NewBeatValueWidget::updateName() 119 | { 120 | for (auto value : beatValues) 121 | { 122 | float thisValue = value.value(); 123 | float myValue = getCurrentValue(); 124 | if (thisValue == myValue) 125 | { 126 | valueNameRadioButton->setText(value.name); 127 | return; 128 | } 129 | } 130 | //no matches found - must be new 131 | valueNameRadioButton->setText(QApplication::translate("EnableIdentifyIntervalsDialog", "New value", nullptr)); 132 | } 133 | -------------------------------------------------------------------------------- /Source/newbeatvaluewidget.h: -------------------------------------------------------------------------------- 1 | #ifndef NEWBEATVALUEWIDGET_H 2 | #define NEWBEATVALUEWIDGET_H 3 | 4 | #include "abstractnewbeatvaluewidget.h" 5 | 6 | class QLabel; 7 | 8 | class NewBeatValueWidget : public AbstractNewBeatValueWidget 9 | { 10 | Q_OBJECT 11 | public: 12 | NewBeatValueWidget(QWidget * parent, float valueInBeats, int divisor); 13 | void updateForValue(float newValueInBeats) override; 14 | QString getName() override; 15 | float getMatchResistance() override; 16 | int getNumerator() override; 17 | int getDivisor() override; 18 | private: 19 | int numerator; 20 | int divisor; 21 | QLabel * valueNumeratorLabel; 22 | QLabel * valueDivisorLabel; 23 | void calculateNumerator(); 24 | bool hasExistingMatchingValue(); 25 | void updateName(); 26 | }; 27 | 28 | #endif // NEWBEATVALUEWIDGET_H 29 | -------------------------------------------------------------------------------- /Source/newcustombeatvaluewidget.cpp: -------------------------------------------------------------------------------- 1 | #include "newcustombeatvaluewidget.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "globals.h" 9 | #include "uniquebeatinterval.h" 10 | #include "beatanalysis.h" 11 | #include 12 | 13 | NewCustomBeatValueWidget::NewCustomBeatValueWidget(float valueInBeats, QWidget *parent) 14 | : 15 | AbstractNewBeatValueWidget(valueInBeats, parent) 16 | { 17 | valueNumeratorSpinBox = new QSpinBox(this); 18 | valueNumeratorSpinBox->setObjectName(QString::fromUtf8("valueNumeratorSpinBox")); 19 | valueNumeratorSpinBox->setMinimum(1); 20 | valueNumeratorSpinBox->setMaximum(9999); 21 | valueNumeratorSpinBox->setValue(3); 22 | 23 | valueFractionLayout->addWidget(valueNumeratorSpinBox); 24 | connect(valueNumeratorSpinBox, SIGNAL(valueChanged(int)), this, SLOT(on_numeratorValueChanged(int))); 25 | 26 | QLabel * valueSlashLabel = new QLabel(this); 27 | valueSlashLabel->setObjectName(QString::fromUtf8("valueSlashLabel")); 28 | valueSlashLabel->setAlignment(Qt::AlignCenter); 29 | valueSlashLabel->setText(QApplication::translate("EnableIdentifyIntervalsDialog", "/", nullptr)); 30 | 31 | valueFractionLayout->addWidget(valueSlashLabel); 32 | 33 | valueDivisorSpinBox = new QSpinBox(this); 34 | valueDivisorSpinBox->setObjectName(QString::fromUtf8("valueDivisorSpinBox")); 35 | valueDivisorSpinBox->setEnabled(false); 36 | valueDivisorSpinBox->setMinimum(1); 37 | valueDivisorSpinBox->setMaximum(9999); 38 | 39 | valueFractionLayout->addWidget(valueDivisorSpinBox); 40 | 41 | 42 | valueNameRadioButton->setText(QApplication::translate("EnableIdentifyIntervalsDialog", "Custom", nullptr)); 43 | 44 | 45 | updateBackground(); 46 | } 47 | 48 | void NewCustomBeatValueWidget::setValue(int numerator, int divisor) 49 | { 50 | const QSignalBlocker blocker(valueNumeratorSpinBox); 51 | valueNumeratorSpinBox->setValue(numerator); 52 | valueDivisorSpinBox->setValue(divisor); 53 | updateBackground(); 54 | } 55 | 56 | QString NewCustomBeatValueWidget::getName() 57 | { 58 | return QString(); 59 | } 60 | 61 | int NewCustomBeatValueWidget::getNumerator() 62 | { 63 | return valueNumeratorSpinBox->value(); 64 | } 65 | 66 | int NewCustomBeatValueWidget::getDivisor() 67 | { 68 | return valueDivisorSpinBox->value(); 69 | } 70 | 71 | void NewCustomBeatValueWidget::on_numeratorValueChanged(int) 72 | { 73 | if (!valueNameRadioButton->isChecked()) 74 | valueNameRadioButton->setChecked(true); 75 | updateBackground(); 76 | emit selected(this); 77 | } 78 | 79 | float NewCustomBeatValueWidget::getMatchResistance() 80 | { 81 | if (valueNameRadioButton->isChecked()) 82 | { 83 | return 0; 84 | } 85 | return std::numeric_limits::max(); 86 | } 87 | 88 | #include "helperfunctions.h" 89 | void NewCustomBeatValueWidget::updateBackground() 90 | { 91 | QColor color; 92 | bool fail = false; 93 | float deviation = percentageDifferenceBetween(getCurrentValue(), valueInBeats); 94 | bool acceptableProportion = (deviation <= BeatAnalysis::Configuration::maxPercentAcceptableBeatError); 95 | if (!acceptableProportion) 96 | { 97 | color = QColor("Dark Red"); 98 | fail = true; 99 | } 100 | else 101 | { 102 | float beatsDifference = abs(valueInBeats - getCurrentValue()); 103 | if (beatsDifference > 0.25 && BeatAnalysis::Configuration::allowHalfBeatsInBreaks) 104 | { 105 | color = QColor("Dark Red"); 106 | fail = true; 107 | } 108 | else 109 | { 110 | float offness = deviation / BeatAnalysis::Configuration::maxPercentAcceptableBeatError; 111 | short hue = 120 - (offness * 120); 112 | color = QColor::fromHsl(hue,255,191); 113 | } 114 | } 115 | 116 | QString textColor; 117 | if (fail) 118 | { 119 | textColor = "white"; 120 | } 121 | else 122 | { 123 | textColor = "black"; 124 | } 125 | int r, g, b; 126 | color.getRgb(&r, &g, &b); 127 | QString rgbRep = QString("background-color: rgb(%1,%2,%3); color: %4").arg(r).arg(g).arg(b).arg(textColor); 128 | setStyleSheet(rgbRep); 129 | } 130 | -------------------------------------------------------------------------------- /Source/newcustombeatvaluewidget.h: -------------------------------------------------------------------------------- 1 | #ifndef NEWCUSTOMBEATVALUEWIDGET_H 2 | #define NEWCUSTOMBEATVALUEWIDGET_H 3 | 4 | #include "abstractnewbeatvaluewidget.h" 5 | 6 | class QSpinBox; 7 | 8 | class NewCustomBeatValueWidget : public AbstractNewBeatValueWidget 9 | { 10 | Q_OBJECT 11 | public: 12 | NewCustomBeatValueWidget(float valueInBeats, QWidget * parent = nullptr); 13 | void setValue(int numerator, int divisor); 14 | QString getName() override; 15 | int getNumerator() override; 16 | int getDivisor() override; 17 | private slots: 18 | void on_numeratorValueChanged(int); 19 | private: 20 | QSpinBox * valueNumeratorSpinBox; 21 | QSpinBox * valueDivisorSpinBox; 22 | float getMatchResistance() override; 23 | void updateBackground() override; 24 | }; 25 | 26 | #endif // NEWCUSTOMBEATVALUEWIDGET_H 27 | -------------------------------------------------------------------------------- /Source/optimisationoptionsdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef OPTIMISATIONOPTIONSDIALOG_H 2 | #define OPTIMISATIONOPTIONSDIALOG_H 3 | 4 | #include 5 | 6 | #define BACKUP_WHEN_OPTIMISING_PREF "createBackupWhenOptimising" 7 | 8 | class EditorWindow; 9 | 10 | namespace Ui { 11 | class OptimisationOptionsDialog; 12 | } 13 | 14 | class OptimisationOptionsDialog : public QDialog 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | explicit OptimisationOptionsDialog(EditorWindow *parent); 20 | ~OptimisationOptionsDialog(); 21 | 22 | private slots: 23 | void on_automaticOptimisationRadioButton_clicked(); 24 | 25 | void on_useProvidedOutputTempoRadioButton_clicked(); 26 | 27 | void on_manualStartTimestampCheckBox_toggled(bool checked); 28 | 29 | void on_buttons_accepted(); 30 | 31 | void on_roundBPMCheckBox_clicked(bool checked); 32 | 33 | private: 34 | Ui::OptimisationOptionsDialog *ui; 35 | QSettings settings; 36 | }; 37 | 38 | #endif // OPTIMISATIONOPTIONSDIALOG_H 39 | -------------------------------------------------------------------------------- /Source/patterndatamodel.h: -------------------------------------------------------------------------------- 1 | #ifndef PATTERNDATAMODEL_H 2 | #define PATTERNDATAMODEL_H 3 | 4 | #include 5 | 6 | class PatternDataModel : public QAbstractTableModel 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit PatternDataModel(QObject *parent = 0); 11 | int rowCount ( const QModelIndex & parent = QModelIndex() ) const; 12 | int columnCount ( const QModelIndex & parent = QModelIndex() ) const; 13 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 14 | Qt::ItemFlags flags(const QModelIndex &index) const; 15 | QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; 16 | 17 | signals: 18 | 19 | public slots: 20 | 21 | }; 22 | 23 | #endif // PATTERNDATAMODEL_H 24 | -------------------------------------------------------------------------------- /Source/pch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | -------------------------------------------------------------------------------- /Source/playbackassociatedaction.cpp: -------------------------------------------------------------------------------- 1 | #include "playbackassociatedaction.h" 2 | 3 | PlaybackAssociatedAction::PlaybackAssociatedAction(QString name, QObject *parent) 4 | : 5 | QObject(parent), 6 | name(name) 7 | { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /Source/playbackassociatedaction.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYBACKASSOCIATEDACTION_H 2 | #define PLAYBACKASSOCIATEDACTION_H 3 | 4 | class PlaybackAssociatedAction : public QObject 5 | { 6 | Q_OBJECT 7 | public: 8 | explicit PlaybackAssociatedAction(QString name, QObject *parent = nullptr); 9 | QString getName() {return name;}; 10 | 11 | signals: 12 | void started(QString uniqueActionName); 13 | void completed(QString uniqueActionName); 14 | void failed(QString uniqueActionName, QString errorMessage); 15 | 16 | public slots: 17 | virtual void kickOff() = 0; 18 | virtual void abort() = 0; 19 | 20 | protected: 21 | QString name; 22 | }; 23 | 24 | #endif // PLAYBACKASSOCIATEDACTION_H 25 | -------------------------------------------------------------------------------- /Source/playbackassociatedactions/pregenerateestimsignalaction.cpp: -------------------------------------------------------------------------------- 1 | #include "pregenerateestimsignalaction.h" 2 | #include "mainwindow.h" 3 | #include 4 | #include "stimsignal/stimsignalsource.h" 5 | 6 | PregenerateEstimSignalAction::PregenerateEstimSignalAction() 7 | : 8 | PlaybackAssociatedAction(tr("Pre-generating E-stim Signal")), 9 | cancelRequested(false) 10 | { 11 | 12 | } 13 | 14 | PregenerateEstimSignalAction::~PregenerateEstimSignalAction() 15 | { 16 | writer->deleteLater(); 17 | } 18 | 19 | void PregenerateEstimSignalAction::kickOff() 20 | { 21 | emit started(name); 22 | QString filename = calculateFilename(); 23 | if (filename.isEmpty()) 24 | { 25 | emit failed(name, QString(tr("Could not calculate a temporary filename to store the pre-generated estim signal. Please save the current project data, or load a video."))); 26 | return; 27 | } 28 | else if (QFileInfo(filename).exists()) 29 | { 30 | //hooray - we can reuse this file, so nothing to do here. 31 | emit completed(name); 32 | return; 33 | } 34 | //OK, let's do this... 35 | 36 | writer = new EstimWavFileWriter(filename); 37 | if (writer->writeFile() || cancelRequested) 38 | { 39 | emit completed(name); 40 | return; 41 | } 42 | emit failed(name, "Something went wrong while writing the e-stim file. Sorry! :-("); 43 | } 44 | 45 | void PregenerateEstimSignalAction::abort() 46 | { 47 | cancelRequested = true; 48 | writer->requestCancel(); 49 | } 50 | 51 | QString PregenerateEstimSignalAction::calculateFilename() 52 | { 53 | // if (!mainWindow->getLoadedScript().isEmpty()) 54 | // { 55 | // QFileInfo fileInfo(mainWindow->getLoadedScript()); 56 | //// QString textToRemove = fileInfo.completeSuffix(); 57 | // QString textToRemove = fileInfo.suffix(); 58 | // QString newBaseName = fileInfo.absoluteFilePath(); 59 | // newBaseName.chop(textToRemove.length()); 60 | // return newBaseName + "wav"; 61 | // } 62 | // else if (!mainWindow->getLoadedVideo().isEmpty()) 63 | // { 64 | // QFileInfo fileInfo(mainWindow->getLoadedVideo()); 65 | // QString textToRemove = fileInfo.completeSuffix(); 66 | //// QString textToRemove = fileInfo.suffix(); 67 | // QString newBaseName = fileInfo.absoluteFilePath(); 68 | // newBaseName.chop(textToRemove.length()); 69 | // return newBaseName + "wav"; 70 | // } 71 | // return ""; 72 | return StimSignalSource::calculatePregeneratedStimFilename(); 73 | } 74 | 75 | -------------------------------------------------------------------------------- /Source/playbackassociatedactions/pregenerateestimsignalaction.h: -------------------------------------------------------------------------------- 1 | #ifndef PREGENERATEESTIMSIGNALACTION_H 2 | #define PREGENERATEESTIMSIGNALACTION_H 3 | #include "playbackassociatedaction.h" 4 | #include "stimsignal/estimwavfilewriter.h" 5 | 6 | class PregenerateEstimSignalAction : public PlaybackAssociatedAction 7 | { 8 | public: 9 | PregenerateEstimSignalAction(); 10 | ~PregenerateEstimSignalAction(); 11 | 12 | void kickOff() override; 13 | void abort() override; 14 | 15 | private: 16 | QString calculateFilename(); 17 | EstimWavFileWriter * writer; 18 | bool cancelRequested; 19 | }; 20 | 21 | #endif // PREGENERATEESTIMSIGNALACTION_H 22 | -------------------------------------------------------------------------------- /Source/preplaybackactionmanager.h: -------------------------------------------------------------------------------- 1 | #ifndef PREPLAYBACKACTIONMANAGER_H 2 | #define PREPLAYBACKACTIONMANAGER_H 3 | 4 | #include 5 | class QAbstractButton; 6 | class PlaybackAssociatedAction; 7 | class PrePlaybackInfoDialog; 8 | 9 | class PrePlaybackActionManager : public QObject 10 | { 11 | Q_OBJECT 12 | public: 13 | explicit PrePlaybackActionManager(QObject *parent = nullptr); 14 | ~PrePlaybackActionManager(); 15 | bool waitForCompletion(); 16 | 17 | signals: 18 | 19 | public slots: 20 | void startAll(); 21 | void handleActionStarted(QString uniqueActionName); 22 | void handleActionCompleted(QString uniqueActionName); 23 | void handleActionFailed(QString uniqueActionName, QString errorMessage); 24 | void dialogCancelled(); 25 | 26 | signals: 27 | void startAllJobs(); 28 | void abortAllRemainingJobs(); 29 | 30 | private: 31 | QMap actionMap; 32 | QList currentlyRunning; 33 | void connectUp(PlaybackAssociatedAction * action); 34 | bool someFailed = false; 35 | bool userRequestedAbort = false; 36 | bool allDone = false; 37 | void updateDialogContent(); 38 | QString createDialogMessage(); 39 | PrePlaybackInfoDialog * progressInfoDialog; 40 | QAbstractButton * cancelButton; 41 | }; 42 | 43 | #endif // PREPLAYBACKACTIONMANAGER_H 44 | -------------------------------------------------------------------------------- /Source/preplaybackinfodialog.cpp: -------------------------------------------------------------------------------- 1 | #include "preplaybackinfodialog.h" 2 | #include "ui_preplaybackinfodialog.h" 3 | 4 | PrePlaybackInfoDialog::PrePlaybackInfoDialog(QWidget *parent) : 5 | QDialog(parent), 6 | ui(new Ui::PrePlaybackInfoDialog) 7 | { 8 | ui->setupUi(this); 9 | } 10 | 11 | PrePlaybackInfoDialog::~PrePlaybackInfoDialog() 12 | { 13 | delete ui; 14 | } 15 | 16 | void PrePlaybackInfoDialog::displayList(QList list) 17 | { 18 | QString labelText; 19 | for (int i = 0; i < list.length(); i++) 20 | { 21 | if (i) 22 | labelText += "\n"; 23 | labelText += list[i]; 24 | } 25 | ui->listLabel->setText(labelText); 26 | } 27 | -------------------------------------------------------------------------------- /Source/preplaybackinfodialog.h: -------------------------------------------------------------------------------- 1 | #ifndef PREPLAYBACKINFODIALOG_H 2 | #define PREPLAYBACKINFODIALOG_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class PrePlaybackInfoDialog; 8 | } 9 | 10 | class PrePlaybackInfoDialog : public QDialog 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit PrePlaybackInfoDialog(QWidget *parent = nullptr); 16 | ~PrePlaybackInfoDialog(); 17 | void displayList(QList list); 18 | 19 | private: 20 | Ui::PrePlaybackInfoDialog *ui; 21 | }; 22 | 23 | #endif // PREPLAYBACKINFODIALOG_H 24 | -------------------------------------------------------------------------------- /Source/preplaybackinfodialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | PrePlaybackInfoDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 221 10 | 200 11 | 12 | 13 | 14 | 15 | 200 16 | 200 17 | 18 | 19 | 20 | Preparing for Playback 21 | 22 | 23 | 24 | 25 | 26 | 27 | 0 28 | 0 29 | 30 | 31 | 32 | Waiting for the following jobs to complete: 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 0 42 | 1 43 | 44 | 45 | 46 | 47 | 48 | 49 | Qt::AlignCenter 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | Skip 59 | 60 | 61 | 62 | 63 | 64 | 65 | Cancel 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | cancelButton 77 | clicked() 78 | PrePlaybackInfoDialog 79 | reject() 80 | 81 | 82 | 162 83 | 131 84 | 85 | 86 | 110 87 | 76 88 | 89 | 90 | 91 | 92 | skipButton 93 | clicked() 94 | PrePlaybackInfoDialog 95 | accept() 96 | 97 | 98 | 58 99 | 131 100 | 101 | 102 | 110 103 | 76 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /Source/seektotimecodedialog.cpp: -------------------------------------------------------------------------------- 1 | #include "seektotimecodedialog.h" 2 | #include "ui_seektotimecodedialog.h" 3 | 4 | SeekToTimecodeDialog::SeekToTimecodeDialog(QWidget *parent) : 5 | QDialog(parent), 6 | ui(new Ui::SeekToTimecodeDialog) 7 | { 8 | ui->setupUi(this); 9 | } 10 | 11 | SeekToTimecodeDialog::~SeekToTimecodeDialog() 12 | { 13 | delete ui; 14 | } 15 | 16 | void SeekToTimecodeDialog::updateTimecode(int newTimecode) 17 | { 18 | QTime tempTime = QTime(0,0,0,0).addMSecs(newTimecode); 19 | ui->timeEdit->setTime(tempTime); 20 | } 21 | 22 | void SeekToTimecodeDialog::on_buttonBox_accepted() 23 | { 24 | QDialog::done( - ui->timeEdit->time().msecsTo(QTime(0,0,0,0))); 25 | } 26 | 27 | void SeekToTimecodeDialog::on_buttonBox_rejected() 28 | { 29 | QDialog::done(-1); 30 | } 31 | -------------------------------------------------------------------------------- /Source/seektotimecodedialog.h: -------------------------------------------------------------------------------- 1 | #ifndef SEEKTOTIMECODEDIALOG_H 2 | #define SEEKTOTIMECODEDIALOG_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class SeekToTimecodeDialog; 8 | } 9 | 10 | class SeekToTimecodeDialog : public QDialog 11 | { 12 | Q_OBJECT 13 | 14 | friend class BetterLCDNumber; 15 | public: 16 | explicit SeekToTimecodeDialog(QWidget *parent = 0); 17 | ~SeekToTimecodeDialog(); 18 | 19 | void updateTimecode(int newTimecode); 20 | 21 | private slots: 22 | void on_buttonBox_accepted(); 23 | 24 | void on_buttonBox_rejected(); 25 | 26 | private: 27 | Ui::SeekToTimecodeDialog *ui; 28 | }; 29 | 30 | #endif // SEEKTOTIMECODEDIALOG_H 31 | -------------------------------------------------------------------------------- /Source/seektotimecodedialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | SeekToTimecodeDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 221 10 | 68 11 | 12 | 13 | 14 | Jump To Time 15 | 16 | 17 | true 18 | 19 | 20 | 21 | 22 | 7 23 | 3 24 | 208 25 | 61 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | Jump to time: 35 | 36 | 37 | 38 | 39 | 40 | 41 | QDateTimeEdit::HourSection 42 | 43 | 44 | h:mm:ss.zzz 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Qt::Horizontal 54 | 55 | 56 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 57 | 58 | 59 | true 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | buttonBox 70 | accepted() 71 | SeekToTimecodeDialog 72 | accept() 73 | 74 | 75 | 248 76 | 254 77 | 78 | 79 | 157 80 | 274 81 | 82 | 83 | 84 | 85 | buttonBox 86 | rejected() 87 | SeekToTimecodeDialog 88 | reject() 89 | 90 | 91 | 316 92 | 260 93 | 94 | 95 | 286 96 | 274 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Source/splitdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef SPLITDIALOG_H 2 | #define SPLITDIALOG_H 3 | 4 | #define SHORTCUT_ADD_INSERT_BEFORE_SELECTION "SHORTCUT_ADD_INSERT_BEFORE_SELECTION" 5 | #define SHORTCUT_ADD_INSERT_AFTER_SELECTION "SHORTCUT_ADD_INSERT_AFTER_SELECTION" 6 | #define SHORTCUT_ADD_MOVE_OTHERS_BACK "SHORTCUT_ADD_MOVE_OTHERS_BACK" 7 | #define SHORTCUT_ADD_MOVE_OTHERS_FORWARD "SHORTCUT_ADD_MOVE_OTHERS_FORWARD" 8 | 9 | #include 10 | class QShortcut; 11 | class BeatValue; 12 | class BeatPattern; 13 | 14 | namespace Ui { 15 | class SplitDialog; 16 | } 17 | 18 | class SplitDialog : public QDialog 19 | { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit SplitDialog(QWidget *parent = nullptr); 24 | ~SplitDialog(); 25 | 26 | virtual void accept() override; 27 | 28 | private slots: 29 | void newValueSelected(BeatValue * newValue); 30 | 31 | void on_deleteButton_clicked(); 32 | 33 | private: 34 | Ui::SplitDialog *ui; 35 | void setShortcuts(); 36 | void setLabels(); 37 | void showEditorSplitPage(); 38 | void setButtonState(); 39 | void refreshPatternDisplay(); 40 | QShortcut * addBeforeShortcut; 41 | QShortcut * addAfterShortcut; 42 | QShortcut * moveBackShortcut; 43 | QShortcut * moveForwardShortcut; 44 | QShortcut * doneShortcut; 45 | BeatPattern * pattern; 46 | }; 47 | 48 | #endif // SPLITDIALOG_H 49 | -------------------------------------------------------------------------------- /Source/stimsignal/dualchannelsignalgenerator.cpp: -------------------------------------------------------------------------------- 1 | #include "dualchannelsignalgenerator.h" 2 | #include "optionsdialog.h" 3 | #include "modifiers/phasesettermodifier.h" 4 | //#include "modifiers/singlechannelbeatproximitymodifier.h" 5 | #include "modifiers/waypointfollowermodifier.h" 6 | #include "modifiers/progressincreasemodifier.h" 7 | //#include "modifiers/boostfaststrokesmodifier.h" 8 | #include "modifiers/fadefromcoldmodifier.h" 9 | #include "modifiers/breaksoftenermodifier.h" 10 | #include "modifiers/channelbalancemodifier.h" 11 | #include "mainwindow.h" 12 | #include "stereostimsignalsample.h" 13 | #include "globals.h" 14 | 15 | #define LEFT_CHANNEL 0 16 | #define RIGHT_CHANNEL 1 17 | 18 | DualChannelSignalGenerator::DualChannelSignalGenerator(QAudioFormat audioFormat, QObject *parent) 19 | : 20 | StimSignalGenerator(audioFormat, parent) 21 | { 22 | 23 | } 24 | 25 | void DualChannelSignalGenerator::setModifiers() 26 | { 27 | modifiers.append(new PhaseSetterModifier(OptionsDialog::getEstimLeftChannelStartingFrequency(), OptionsDialog::getEstimLeftChannelEndingFrequency(), LEFT_CHANNEL)); 28 | modifiers.append(new PhaseSetterModifier(OptionsDialog::getEstimRightChannelStartingFrequency(), OptionsDialog::getEstimRightChannelEndingFrequency(), RIGHT_CHANNEL)); 29 | WaypointList * leftVolumeWaypoints = createWaypointList(OptionsDialog::getEstimLeftChannelStrokeStyle() == PREF_ESTIM_ENDS_ON_BEAT_STYLE, 30 | OptionsDialog::getEstimLeftChannelPeakPositionInCycle(), 31 | OptionsDialog::getEstimLeftChannelTroughLevel()); 32 | modifiers.append(new WaypointFollowerModifier(leftVolumeWaypoints, LEFT_CHANNEL)); 33 | WaypointList * rightVolumeWaypoints = createWaypointList(OptionsDialog::getEstimRightChannelStrokeStyle() == PREF_ESTIM_ENDS_ON_BEAT_STYLE, 34 | OptionsDialog::getEstimRightChannelPeakPositionInCycle(), 35 | OptionsDialog::getEstimRightChannelTroughLevel()); 36 | modifiers.append(new WaypointFollowerModifier(rightVolumeWaypoints, RIGHT_CHANNEL)); 37 | modifiers.append(new ProgressIncreaseModifier()); 38 | modifiers.append(new FadeFromColdModifier()); 39 | modifiers.append(new BreakSoftenerModifier()); 40 | if (OptionsDialog::getEstimSignalPan()) 41 | modifiers.append(new ChannelBalanceModifier()); 42 | } 43 | 44 | long DualChannelSignalGenerator::getStopTimestamp() 45 | { 46 | return mainWindow->totalPlayTime() + std::max(OptionsDialog::getEstimLeftChannelFadeTime(), OptionsDialog::getEstimRightChannelFadeTime()); 47 | } 48 | 49 | StimSignalSample *DualChannelSignalGenerator::createSample(qlonglong wholeTimestamp, qreal fractionalTimestamp) 50 | { 51 | return new StereoStimSignalSample(wholeTimestamp, fractionalTimestamp); 52 | } 53 | -------------------------------------------------------------------------------- /Source/stimsignal/dualchannelsignalgenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef DUALCHANNELSIGNALGENERATOR_H 2 | #define DUALCHANNELSIGNALGENERATOR_H 3 | 4 | #include "stimsignalgenerator.h" 5 | 6 | class DualChannelSignalGenerator : public StimSignalGenerator 7 | { 8 | public: 9 | explicit DualChannelSignalGenerator(QAudioFormat audioFormat, QObject *parent = nullptr); 10 | protected: 11 | void setModifiers() override; 12 | long getStopTimestamp() override; 13 | StimSignalSample * createSample(qlonglong wholeTimestamp, qreal fractionalTimestamp) override; 14 | }; 15 | 16 | #endif // DUALCHANNELSIGNALGENERATOR_H 17 | -------------------------------------------------------------------------------- /Source/stimsignal/estimwavfilewriter.h: -------------------------------------------------------------------------------- 1 | #ifndef ESTIMWAVFILEWRITER_H 2 | #define ESTIMWAVFILEWRITER_H 3 | 4 | #include 5 | class QProgressDialog; 6 | 7 | class EstimWavFileWriter : public QObject 8 | { 9 | Q_OBJECT 10 | public: 11 | explicit EstimWavFileWriter(QString filePath, QObject *parent = nullptr); 12 | bool writeFile(); 13 | signals: 14 | 15 | public slots: 16 | void requestCancel(); 17 | 18 | private: 19 | QFile file; 20 | bool cancelRequested = false; 21 | }; 22 | 23 | #endif // ESTIMWAVFILEWRITER_H 24 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/boostfaststrokesmodifier.cpp: -------------------------------------------------------------------------------- 1 | #include "boostfaststrokesmodifier.h" 2 | #include "optionsdialog.h" 3 | #include "globals.h" 4 | #include "mainwindow.h" 5 | #include "stimsignal/stimsignalsample.h" 6 | 7 | BoostFastStrokesModifier::BoostFastStrokesModifier() 8 | { 9 | maxBoostAmount = 0.01 * OptionsDialog::getEstimBoostShortStrokes(); 10 | normalStrokeLength = OptionsDialog::getEstimMaxStrokeLength(); 11 | } 12 | 13 | void BoostFastStrokesModifier::modify(StimSignalSample &sample) 14 | { 15 | qreal timestamp = sample.totalTimestamp(); 16 | Event * before = mainWindow->getLastEventBefore(timestamp); 17 | Event * after = mainWindow->getNextEventAfter(timestamp); 18 | if (before == nullptr || after == nullptr) 19 | //can't (or shouldn't) help with this 20 | return; 21 | long length = after->timestamp - before->timestamp; 22 | if (length <= 0 || length > normalStrokeLength) 23 | //can't (or shouldn't) help with this 24 | return; 25 | qreal boostAmount = ((qreal) (normalStrokeLength - length) / normalStrokeLength) * maxBoostAmount; 26 | // XXX 27 | // Note that this will cause distortion if not combined with something which reduces the overall volume below 100% 28 | // At the time of writing, this role is done by the 'ProgressIncreaseModifier' 29 | for (int i = 0; i < sample.numberOfChannels(); ++i) 30 | sample.setAmplitude(i, sample.getAmplitude(i) * (1 + boostAmount)); 31 | } 32 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/boostfaststrokesmodifier.h: -------------------------------------------------------------------------------- 1 | #ifndef BOOSTFASTSTROKESMODIFIER_H 2 | #define BOOSTFASTSTROKESMODIFIER_H 3 | 4 | #include "stimsignal/stimsignalmodifier.h" 5 | 6 | //! 7 | //! \brief The BoostFastStrokesModifier class 8 | //! Note that this will cause distortion if not combined with something which reduces the overall volume below 100% 9 | //! At the time of writing, this role is done by the 'ProgressIncreaseModifier' 10 | //! 11 | class BoostFastStrokesModifier : public StimSignalModifier 12 | { 13 | public: 14 | BoostFastStrokesModifier(); 15 | void modify(StimSignalSample &sample) override; 16 | qreal maxBoostAmount; 17 | int normalStrokeLength; 18 | }; 19 | 20 | #endif // BOOSTFASTSTROKESMODIFIER_H 21 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/breaksoftenermodifier.cpp: -------------------------------------------------------------------------------- 1 | #include "breaksoftenermodifier.h" 2 | #include "optionsdialog.h" 3 | #include "stimsignal/stimsignalsample.h" 4 | 5 | BreakSoftenerModifier::BreakSoftenerModifier(int checkThisChannel) 6 | : 7 | currentLevel(1), 8 | channel(checkThisChannel) 9 | { 10 | maxQuietening = qreal(OptionsDialog::getEstimCompressorStrength()) / 100; 11 | long samplesInQuieteningPeriod = OptionsDialog::getEstimSamplingRate() * OptionsDialog::getEstimCompressorBiteTime() + 0.5; 12 | long samplesInLoudeningPeriod = OptionsDialog::getEstimSamplingRate() * OptionsDialog::getEstimCompressorRelease() + 0.5; 13 | quieteningPerSilentSample = maxQuietening / samplesInQuieteningPeriod; 14 | loudeningPerActiveSample = maxQuietening / samplesInLoudeningPeriod; 15 | 16 | if (channel == -1) 17 | { 18 | if (OptionsDialog::getEstimChannelCount() == 1) 19 | channel = 0; 20 | else 21 | { 22 | if (OptionsDialog::getEstimLeftChannelFadeTime() > OptionsDialog::getEstimRightChannelFadeTime()) 23 | channel = 1; 24 | else 25 | channel = 0; 26 | } 27 | } 28 | } 29 | 30 | void BreakSoftenerModifier::modify(StimSignalSample &sample) 31 | { 32 | if (sample.getAmplitude(channel) == 0) 33 | { 34 | if (!atMaxQuietening()) 35 | currentLevel -= quieteningPerSilentSample; 36 | } 37 | else 38 | { 39 | if (!atMaxVolume()) 40 | { 41 | currentLevel += loudeningPerActiveSample; 42 | } 43 | } 44 | for (int i = 0; i < sample.numberOfChannels(); ++i) 45 | sample.setAmplitude(i, sample.getAmplitude(i) * currentLevel); 46 | } 47 | 48 | bool BreakSoftenerModifier::atMaxQuietening() 49 | { 50 | return currentLevel <= maxQuietening; 51 | } 52 | 53 | bool BreakSoftenerModifier::atMaxVolume() 54 | { 55 | return currentLevel >= 1; 56 | } 57 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/breaksoftenermodifier.h: -------------------------------------------------------------------------------- 1 | #ifndef BREAKSOFTENERMODIFIER_H 2 | #define BREAKSOFTENERMODIFIER_H 3 | 4 | #include "stimsignal/stimsignalmodifier.h" 5 | 6 | class BreakSoftenerModifier : public StimSignalModifier 7 | { 8 | public: 9 | //! 10 | //! \brief BreakSoftenerModifier this modifier will apply quietning to the beginning of signals following a long break. 11 | //! It will always quieten all channels equally, but you can set the channel parameter to dictate which channel 12 | //! will be used to decide whether a break is happening (whether that channel is currently silent) 13 | //! \param channel set which channel will be checked to see whether the signal is currently silent (which 14 | //! is taken to mean now is a break). If omitted, a channel will be chosen based on which is most often 15 | //! silent (that is - the one with the shorter decay time) 16 | //! 17 | BreakSoftenerModifier(int channel = -1); 18 | void modify(StimSignalSample &sample) override; 19 | 20 | private: 21 | qreal currentLevel; 22 | qreal maxQuietening; 23 | qreal quieteningPerSilentSample; 24 | qreal loudeningPerActiveSample; 25 | int channel; 26 | 27 | bool atMaxQuietening(); 28 | bool atMaxVolume(); 29 | }; 30 | 31 | #endif // BREAKSOFTENERMODIFIER_H 32 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/channelbalancemodifier.cpp: -------------------------------------------------------------------------------- 1 | #include "channelbalancemodifier.h" 2 | #include "optionsdialog.h" 3 | #include "stimsignal/stimsignalsample.h" 4 | 5 | ChannelBalanceModifier::ChannelBalanceModifier() 6 | { 7 | int pan = OptionsDialog::getEstimSignalPan(); 8 | panLeft = (pan < 0); 9 | attenuation = 1 - (0.01 * abs(pan)); 10 | } 11 | 12 | void ChannelBalanceModifier::modify(StimSignalSample &sample) 13 | { 14 | if (panLeft) 15 | { 16 | sample.setAmplitude(1, sample.getAmplitude(1) * attenuation); 17 | } 18 | else 19 | { 20 | sample.setPrimaryAmplitude(sample.getPrimaryAmplitude() * attenuation); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/channelbalancemodifier.h: -------------------------------------------------------------------------------- 1 | #ifndef CHANNELBALANCEMODIFIER_H 2 | #define CHANNELBALANCEMODIFIER_H 3 | 4 | #include "stimsignal/stimsignalmodifier.h" 5 | 6 | class ChannelBalanceModifier : public StimSignalModifier 7 | { 8 | public: 9 | ChannelBalanceModifier(); 10 | void modify(StimSignalSample &sample) override; 11 | 12 | private: 13 | bool panLeft; 14 | float attenuation; 15 | 16 | }; 17 | 18 | #endif // CHANNELBALANCEMODIFIER_H 19 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/fadefromcoldmodifier.cpp: -------------------------------------------------------------------------------- 1 | #include "fadefromcoldmodifier.h" 2 | #include "optionsdialog.h" 3 | #include "stimsignal/stimsignalsample.h" 4 | 5 | FadeFromColdModifier::FadeFromColdModifier() 6 | { 7 | int sampleRate = OptionsDialog::getEstimSamplingRate(); 8 | totalSamples = (OptionsDialog::getEstimStartPlaybackFadeInTime() * sampleRate) / 1000; 9 | increment = 1.0 / totalSamples; 10 | } 11 | 12 | void FadeFromColdModifier::modify(StimSignalSample &sample) 13 | { 14 | if (sampleCounter < totalSamples) 15 | { 16 | runningTotal = sampleCounter * increment; 17 | for (int i = 0; i < sample.numberOfChannels(); ++i) 18 | sample.setAmplitude(i, sample.getAmplitude(i) * runningTotal); 19 | ++sampleCounter; 20 | } 21 | else 22 | { 23 | //remove self to save processing? 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/fadefromcoldmodifier.h: -------------------------------------------------------------------------------- 1 | #ifndef FADEFROMCOLDMODIFIER_H 2 | #define FADEFROMCOLDMODIFIER_H 3 | 4 | #include "stimsignal/stimsignalmodifier.h" 5 | 6 | class FadeFromColdModifier : public StimSignalModifier 7 | { 8 | public: 9 | FadeFromColdModifier(); 10 | void modify(StimSignalSample &sample) override; 11 | 12 | private: 13 | long totalSamples; 14 | long sampleCounter = 0; 15 | qreal runningTotal = 0; 16 | qreal increment = 0; 17 | }; 18 | 19 | #endif // FADEFROMCOLDMODIFIER_H 20 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/phaseinvertermodifier.cpp: -------------------------------------------------------------------------------- 1 | #include "phaseinvertermodifier.h" 2 | #include "stimsignal/stimsignalsample.h" 3 | 4 | PhaseInverterModifier::PhaseInverterModifier() 5 | { 6 | 7 | } 8 | 9 | void PhaseInverterModifier::modify(StimSignalSample &sample) 10 | { 11 | sample.setPhase(1, sample.getPhase(1) + 0.5); 12 | } 13 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/phaseinvertermodifier.h: -------------------------------------------------------------------------------- 1 | #ifndef PHASEINVERTERMODIFIER_H 2 | #define PHASEINVERTERMODIFIER_H 3 | 4 | #include "stimsignal/stimsignalmodifier.h" 5 | 6 | class PhaseInverterModifier : public StimSignalModifier 7 | { 8 | public: 9 | PhaseInverterModifier(); 10 | void modify(StimSignalSample &sample) override; 11 | }; 12 | 13 | #endif // PHASEINVERTERMODIFIER_H 14 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/phasesettermodifier.cpp: -------------------------------------------------------------------------------- 1 | #include "phasesettermodifier.h" 2 | #include "optionsdialog.h" 3 | #include "mainwindow.h" 4 | #include "stimsignal/stimsignalsample.h" 5 | 6 | PhaseSetterModifier::PhaseSetterModifier(int startFrequency, int endFrequency, int channel) 7 | : 8 | phase(0), 9 | step(0), 10 | startingFrequency(startFrequency), 11 | endingFrequency(endFrequency), 12 | updateCounter(OptionsDialog::getEstimSamplingRate()), 13 | frequencyUpdateInterval(OptionsDialog::getEstimSamplingRate()), 14 | channel(channel) 15 | { 16 | 17 | } 18 | 19 | void PhaseSetterModifier::modify(StimSignalSample &sample) 20 | { 21 | if (updateCounter >= frequencyUpdateInterval) 22 | setStep(sample.totalTimestamp()); 23 | if (channel == -1) 24 | { 25 | for (int i = 0; i < sample.numberOfChannels(); ++i) 26 | sample.setPhase(i, phase); 27 | } 28 | else 29 | { 30 | sample.setPhase(channel, phase); 31 | } 32 | phase += step; 33 | } 34 | 35 | void PhaseSetterModifier::setStep(long timestamp) 36 | { 37 | qreal endPortion = mainWindow->progressThroughGame(timestamp); 38 | qreal beginningPortion = 1 - endPortion; 39 | int currentFrequency = (beginningPortion * startingFrequency) + (endPortion * endingFrequency); 40 | step = (qreal) currentFrequency / OptionsDialog::getEstimSamplingRate(); 41 | phase -= (int) phase; 42 | updateCounter = 0; 43 | } 44 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/phasesettermodifier.h: -------------------------------------------------------------------------------- 1 | #ifndef PHASESETTERMODIFIER_H 2 | #define PHASESETTERMODIFIER_H 3 | 4 | #include "stimsignal/stimsignalmodifier.h" 5 | 6 | class PhaseSetterModifier : public StimSignalModifier 7 | { 8 | public: 9 | //! 10 | //! \brief PhaseSetterModifier 11 | //! \param startFrequency 12 | //! \param endFrequency 13 | //! \param channel by deafult this is 0, and will apply to the first channel. Set to -1 to apply to all channels 14 | //! 15 | PhaseSetterModifier(int startFrequency, int endFrequency, int channel = 0); 16 | void modify(StimSignalSample &sample) override; 17 | 18 | private: 19 | void setStep(long timestamp); 20 | qreal phase; 21 | qreal step; 22 | int startingFrequency; 23 | int endingFrequency; 24 | int updateCounter; 25 | int frequencyUpdateInterval; 26 | int channel; 27 | }; 28 | 29 | #endif // PHASESETTERMODIFIER_H 30 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/progressincreasemodifier.cpp: -------------------------------------------------------------------------------- 1 | #include "progressincreasemodifier.h" 2 | #include "optionsdialog.h" 3 | #include "mainwindow.h" 4 | #include "globals.h" 5 | #include "stimsignal/stimsignalsample.h" 6 | 7 | ProgressIncreaseModifier::ProgressIncreaseModifier() 8 | { 9 | int increasePercent = OptionsDialog::getEstimTotalSignalGrowth(); 10 | //leave space for 'boosting' fast strokes 11 | qreal endingVolume = (qreal) 100 / (100 + OptionsDialog::getEstimBoostShortStrokes()); 12 | initialValue = (endingVolume * 100) / (100 + increasePercent); 13 | totalIncrease = endingVolume - initialValue; 14 | } 15 | 16 | void ProgressIncreaseModifier::modify(StimSignalSample &sample) 17 | { 18 | qreal progress = mainWindow->progressThroughGame(sample.totalTimestamp()); 19 | qreal scaleValue = initialValue + (progress * totalIncrease); 20 | for (int i = 0; i < sample.numberOfChannels(); ++i) 21 | sample.setAmplitude(i, sample.getAmplitude(i) * scaleValue); 22 | } 23 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/progressincreasemodifier.h: -------------------------------------------------------------------------------- 1 | #ifndef PROGRESSINCREASEMODIFIER_H 2 | #define PROGRESSINCREASEMODIFIER_H 3 | 4 | #include "stimsignal/stimsignalmodifier.h" 5 | 6 | class ProgressIncreaseModifier : public StimSignalModifier 7 | { 8 | public: 9 | ProgressIncreaseModifier(); 10 | void modify(StimSignalSample &sample) override; 11 | qreal initialValue = 1; 12 | qreal totalIncrease = 0; 13 | }; 14 | 15 | #endif // PROGRESSINCREASEMODIFIER_H 16 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/singlechannelbeatproximitymodifier.h: -------------------------------------------------------------------------------- 1 | #ifndef SINGLECHANNELBEATPROXIMITYMODIFIER_H 2 | #define SINGLECHANNELBEATPROXIMITYMODIFIER_H 3 | 4 | #include "../stimsignalmodifier.h" 5 | 6 | 7 | class SingleChannelBeatProximityModifier : public StimSignalModifier 8 | { 9 | public: 10 | SingleChannelBeatProximityModifier(bool cycleStartsOnBeat, qreal peakPositionWithinCycle, qreal troughLevel, qreal fadeTime); 11 | void modify(StimSignalSample &sample) override; 12 | 13 | private: 14 | bool cycleStartsOnBeat; 15 | qreal peakPositionInCycle; 16 | qreal troughLevel; 17 | qreal fadeTime; 18 | int getFadeTime(); 19 | int maxStrokeLength; 20 | qreal amountFadedInOneBeat; 21 | }; 22 | 23 | #endif // SINGLECHANNELBEATPROXIMITYMODIFIER_H 24 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/triphasebeatproximitymodifier.cpp: -------------------------------------------------------------------------------- 1 | #include "triphasebeatproximitymodifier.h" 2 | #include "mainwindow.h" 3 | #include "globals.h" 4 | #include "../stimsignalgenerator.h" 5 | #include "optionsdialog.h" 6 | #include "stimsignal/stimsignalsample.h" 7 | 8 | int TriphaseBeatProximityModifier::getFadeInTime() 9 | { 10 | return fadeInTime; 11 | } 12 | 13 | int TriphaseBeatProximityModifier::getFadeInAnticipation() 14 | { 15 | return fadeInAnticipation; 16 | } 17 | 18 | int TriphaseBeatProximityModifier::getFadeOutTime() 19 | { 20 | return fadeOutTime; 21 | } 22 | 23 | int TriphaseBeatProximityModifier::getFadeOutDelay() 24 | { 25 | return fadeOutDelay; 26 | } 27 | 28 | TriphaseBeatProximityModifier::TriphaseBeatProximityModifier() 29 | { 30 | fadeInTime = OptionsDialog::getEstimBeatFadeInTime(); 31 | fadeInAnticipation = OptionsDialog::getEstimBeatFadeInAnticipationTime(); 32 | fadeOutTime = OptionsDialog::getEstimBeatFadeOutTime(); 33 | fadeOutDelay = OptionsDialog::getEstimBeatFadeOutDelay(); 34 | } 35 | 36 | void TriphaseBeatProximityModifier::modify(StimSignalSample &sample) 37 | { 38 | //how close are we to the last event? 39 | Event * eventBefore = mainWindow->getLastEventBefore(sample.totalTimestamp()); 40 | qreal volumeBefore = 0; 41 | if (eventBefore != nullptr) 42 | { 43 | qreal spaceBefore = abs(sample.distanceToTimestamp(eventBefore->timestamp)); 44 | if (spaceBefore < getFadeOutDelay()) 45 | volumeBefore = 1; 46 | else 47 | { 48 | spaceBefore -= getFadeOutDelay(); 49 | if (spaceBefore <= getFadeOutTime()) 50 | volumeBefore = (getFadeOutTime() - spaceBefore) / getFadeOutTime(); 51 | } 52 | } 53 | //and how about the next event? 54 | Event * eventAfter = mainWindow->getNextEventAfter(sample.totalTimestamp()); 55 | qreal volumeAfter = 0; 56 | if (eventAfter != nullptr) 57 | { 58 | qreal spaceAfter = abs(sample.distanceToTimestamp(eventAfter->timestamp)); 59 | if (spaceAfter <= getFadeInAnticipation()) 60 | volumeAfter = 1; 61 | else 62 | { 63 | spaceAfter -= getFadeInAnticipation(); 64 | if (spaceAfter <= getFadeInTime()) 65 | volumeAfter = (getFadeInTime() - spaceAfter) / getFadeInTime(); 66 | } 67 | } 68 | 69 | qreal volume = qMax(volumeBefore, volumeAfter); 70 | 71 | for (int i = 0; i < sample.numberOfChannels(); ++i) 72 | sample.setAmplitude(i, sample.getAmplitude(i) * volume); 73 | } 74 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/triphasebeatproximitymodifier.h: -------------------------------------------------------------------------------- 1 | #ifndef BEATPROXIMITYMODIFIER_H 2 | #define BEATPROXIMITYMODIFIER_H 3 | 4 | #include "../stimsignalmodifier.h" 5 | 6 | #define DEFAULT_STIM_FADE_IN_LENGTH 4000 7 | #define DEFAULT_STIM_FADE_OUT_LENGTH 4000 8 | 9 | class TriphaseBeatProximityModifier : public StimSignalModifier 10 | { 11 | public: 12 | TriphaseBeatProximityModifier(); 13 | void modify(StimSignalSample &sample) override; 14 | private: 15 | int fadeInTime; 16 | int fadeInAnticipation; 17 | int fadeOutTime; 18 | int fadeOutDelay; 19 | int getFadeInTime(); 20 | int getFadeInAnticipation(); 21 | int getFadeOutTime(); 22 | int getFadeOutDelay(); 23 | }; 24 | 25 | #endif // BEATPROXIMITYMODIFIER_H 26 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/triphasemodifier.cpp: -------------------------------------------------------------------------------- 1 | #include "triphasemodifier.h" 2 | #include "mainwindow.h" 3 | #include "globals.h" 4 | #include "optionsdialog.h" 5 | #include "beatinterval.h" 6 | #include "stimsignal/stimsignalsample.h" 7 | 8 | TriphaseModifier::TriphaseModifier() 9 | { 10 | strokeLength = OptionsDialog::getEstimMaxStrokeLength(); 11 | if (OptionsDialog::getEstimTriphaseStrokeStyle() == PREF_ESTIM_UP_DOWN_BEAT_STROKE_STYLE) 12 | style = UP_DOWN_BEAT; 13 | else 14 | style = DOWN_BEAT_UP; 15 | } 16 | 17 | int TriphaseModifier::getMaxTriphaseStrokeLength() 18 | { 19 | return strokeLength; 20 | } 21 | 22 | void TriphaseModifier::modify(StimSignalSample &sample) 23 | { 24 | qreal phaseDifference = 0; 25 | 26 | long nextEventTimestamp = 0; 27 | long previousEventTimestamp = 0; 28 | bool previousEventFound = false; 29 | bool nextEventFound = false; 30 | Event * nextEvent = mainWindow->getNextEventAfter(sample.totalTimestamp()); 31 | if (nextEvent != nullptr) 32 | { 33 | nextEventFound = true; 34 | nextEventTimestamp = nextEvent->timestamp; 35 | } 36 | Event * previousEvent = mainWindow->getLastEventBefore(sample.totalTimestamp()); 37 | if (previousEvent != nullptr) 38 | { 39 | previousEventFound = true; 40 | previousEventTimestamp = previousEvent->timestamp; 41 | } 42 | switch (style) 43 | { 44 | case UP_DOWN_BEAT: 45 | // mimic the behaviour of the handy and funscript output. 46 | // pros: it matches the stroker and funscript actions 47 | // cons: not sure if this is the best way to make stimulation 48 | // feel 'on the beat' as all action is before the beat. ///////////////////////// 49 | // we primarily care about when the next beat is. 50 | if (nextEventFound && sample.distanceToTimestamp(nextEventTimestamp) <= getMaxTriphaseStrokeLength()) 51 | { 52 | //now we're part way through a 'stroke', so calculate how far through we are. 53 | qlonglong startStrokeAt = 0; 54 | if ( !previousEventFound || std::abs(sample.distanceToTimestamp(previousEventTimestamp)) > getMaxTriphaseStrokeLength()) 55 | { 56 | startStrokeAt = nextEventTimestamp - getMaxTriphaseStrokeLength(); 57 | if (startStrokeAt < 0) 58 | { 59 | startStrokeAt = 0; //can't start a stroke before the beginning 60 | } 61 | } 62 | else 63 | { 64 | startStrokeAt = previousEventTimestamp; 65 | } 66 | int strokeLength = nextEventTimestamp - startStrokeAt; 67 | //distance to timestamp in the past is negative, so invert it 68 | phaseDifference = -sample.distanceToTimestamp(startStrokeAt) / strokeLength; 69 | } 70 | break; //case 71 | case DOWN_BEAT_UP: 72 | //this style defaults to 'up' rather than 'down' when not stroking 73 | qreal distance = 1; //i.e. nowhere near the beat (will get converted to 0.5, which is max distance from any integer) 74 | int strokeLength = getMaxTriphaseStrokeLength(); 75 | if (nextEventFound && previousEventFound) 76 | strokeLength = nextEventTimestamp - previousEventTimestamp; 77 | int halfOfStrokeLength = strokeLength / 2; 78 | 79 | if (nextEventFound && sample.distanceToTimestamp(nextEventTimestamp) <= halfOfStrokeLength) 80 | { 81 | //now we're on a 'down stroke', calculate how far from the beat we are 82 | distance = sample.distanceToTimestamp(nextEventTimestamp) / halfOfStrokeLength; 83 | //distance is between 0 and 1 84 | } 85 | else if (previousEventFound && std::abs(sample.distanceToTimestamp(previousEventTimestamp)) <= halfOfStrokeLength) 86 | { 87 | //we're on an 'up stroke', calculate distance from beat, distance will be negative 88 | distance = sample.distanceToTimestamp(previousEventTimestamp) / halfOfStrokeLength; 89 | //distance is between 0 and -1 90 | } 91 | //constrain to between -0.5 and +0.5: phase 'wraps around' below 0 as well as above 1 92 | phaseDifference = 0.5 * distance; 93 | break; //case 94 | } 95 | sample.setPhase(1, sample.getPrimaryPhase() + phaseDifference); 96 | } 97 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/triphasemodifier.h: -------------------------------------------------------------------------------- 1 | #ifndef TRIPHASEMODIFIER_H 2 | #define TRIPHASEMODIFIER_H 3 | 4 | #include "../stimsignalmodifier.h" 5 | 6 | enum StrokeStyle { 7 | UP_DOWN_BEAT, 8 | DOWN_BEAT_UP 9 | }; 10 | 11 | class TriphaseModifier : public StimSignalModifier 12 | { 13 | public: 14 | TriphaseModifier(); 15 | void modify(StimSignalSample &sample) override; 16 | private: 17 | int strokeLength; 18 | int getMaxTriphaseStrokeLength(); 19 | StrokeStyle style; 20 | }; 21 | 22 | #endif // TRIPHASEMODIFIER_H 23 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/waypoint.cpp: -------------------------------------------------------------------------------- 1 | #include "waypoint.h" 2 | 3 | Waypoint::Waypoint(long timestamp, qreal multiplier) 4 | : 5 | timestamp(timestamp), 6 | multiplier(multiplier), 7 | next(nullptr), 8 | previous(nullptr) 9 | { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/waypoint.h: -------------------------------------------------------------------------------- 1 | #ifndef WAYPOINT_H 2 | #define WAYPOINT_H 3 | 4 | #include 5 | 6 | class Waypoint 7 | { 8 | public: 9 | Waypoint(long timestamp, qreal multiplier); 10 | 11 | long timestamp; 12 | qreal multiplier; 13 | Waypoint * next; 14 | Waypoint * previous; 15 | }; 16 | 17 | #endif // WAYPOINT_H 18 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/waypointfollowermodifier.cpp: -------------------------------------------------------------------------------- 1 | #include "waypointfollowermodifier.h" 2 | #include "waypointlist.h" 3 | #include 4 | #include "stimsignal/stimsignalsample.h" 5 | #include "waypoint.h" 6 | 7 | WaypointFollowerModifier::WaypointFollowerModifier(WaypointList *waypoints, int channel) 8 | : 9 | waypoints(waypoints), 10 | nextWaypoint(waypoints->first()), 11 | channel(channel) 12 | { 13 | } 14 | 15 | WaypointFollowerModifier::~WaypointFollowerModifier() 16 | { 17 | delete waypoints; 18 | } 19 | 20 | void WaypointFollowerModifier::modify(StimSignalSample &sample) 21 | { 22 | if (sample.totalTimestamp() < waypoints->first()->timestamp || sample.totalTimestamp() >= waypoints->last()->timestamp) 23 | { 24 | if (channel == -1) 25 | { 26 | for (int i = 0; i < sample.numberOfChannels(); ++i) 27 | sample.setAmplitude(i, 0); 28 | } 29 | else 30 | { 31 | sample.setAmplitude(channel, 0); 32 | } 33 | return; 34 | } 35 | if (sample.totalTimestamp() >= nextWaypoint->timestamp) 36 | { 37 | lastWaypoint = nextWaypoint; 38 | nextWaypoint = lastWaypoint->next; 39 | } 40 | qreal distanceToLast = sample.totalTimestamp() - lastWaypoint->timestamp; 41 | qreal distanceToNext = nextWaypoint->timestamp - sample.totalTimestamp(); 42 | qreal nextWeight = distanceToLast / (distanceToLast + distanceToNext); 43 | qreal totalMultiplier = calculateMultiplier(lastWaypoint->multiplier, nextWaypoint->multiplier, nextWeight); 44 | if (channel == -1) 45 | { 46 | for (int i = 0; i < sample.numberOfChannels(); ++i) 47 | sample.setAmplitude(i, totalMultiplier); 48 | } 49 | else 50 | { 51 | sample.setAmplitude(channel, totalMultiplier); 52 | } 53 | } 54 | 55 | qreal WaypointFollowerModifier::calculateMultiplier(qreal lastValue, qreal nextValue, qreal progress) 56 | { 57 | // qreal zeroToOne = (progress * nextValue) + ((1 - progress) * lastValue); 58 | //simple implementation: 59 | // return zeroToOne; 60 | 61 | //SINple implementation 62 | qreal difference = nextValue - lastValue; 63 | return lastValue + difference * (0.5 + (0.5 * qCos(M_PI + M_PI * progress))); 64 | } 65 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/waypointfollowermodifier.h: -------------------------------------------------------------------------------- 1 | #ifndef WAYPOINTFOLLOWERMODIFIER_H 2 | #define WAYPOINTFOLLOWERMODIFIER_H 3 | 4 | #include "stimsignal/stimsignalmodifier.h" 5 | 6 | class WaypointList; 7 | class Waypoint; 8 | 9 | class WaypointFollowerModifier : public StimSignalModifier 10 | { 11 | public: 12 | WaypointFollowerModifier(WaypointList * waypoints, int channel = -1); 13 | ~WaypointFollowerModifier(); 14 | void modify(StimSignalSample &sample) override; 15 | 16 | private: 17 | WaypointList * waypoints; 18 | Waypoint * lastWaypoint; 19 | Waypoint * nextWaypoint; 20 | int channel; 21 | qreal calculateMultiplier(qreal lastValue, qreal nextValue, qreal progress); 22 | }; 23 | 24 | #endif // WAYPOINTFOLLOWERMODIFIER_H 25 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/waypointlist.cpp: -------------------------------------------------------------------------------- 1 | #include "waypointlist.h" 2 | #include "optionsdialog.h" 3 | #include "waypoint.h" 4 | 5 | WaypointList::WaypointList() 6 | : 7 | QList() 8 | { 9 | 10 | } 11 | 12 | WaypointList::~WaypointList() 13 | { 14 | while (!isEmpty()) 15 | { 16 | delete takeLast(); 17 | } 18 | } 19 | 20 | void WaypointList::plonkOnTheEnd(Waypoint * newValue) 21 | { 22 | Waypoint * oldEnd = nullptr; 23 | if (!isEmpty()) 24 | { 25 | oldEnd = last(); 26 | oldEnd->next = newValue; 27 | } 28 | newValue->previous = oldEnd; 29 | append(newValue); 30 | } 31 | 32 | void WaypointList::insertTroughs(qreal troughLevel) 33 | { 34 | double amountFadedInHalfAStrokeTime = 1 - troughLevel; 35 | double halfAStrokeTime = (double) OptionsDialog::getEstimMaxStrokeLength() / 2; 36 | double fadeTime = halfAStrokeTime / amountFadedInHalfAStrokeTime; 37 | int i = length() - 1; 38 | // Don't crash here by handling last waypoint 39 | plonkOnTheEnd(new Waypoint(at(i)->timestamp + fadeTime, 0)); 40 | //iterate backwards because it means we don't have to skip values of i 41 | for (--i /* step back once, we did length - 1 already*/; i >= 0 ; --i) 42 | { 43 | //insert a trough _after_ this peak 44 | qreal lengthBetweenPeaks = (at(i+1)->timestamp - at(i)->timestamp); 45 | if (lengthBetweenPeaks > (fadeTime * 2)) 46 | { 47 | //we've got time to completely fade out and back in, so we'll need a waypoint for the end of the fade-out and another for the beginning of the fade-in. 48 | qreal endOfFadeout = at(i)->timestamp + fadeTime; 49 | qreal beginningOfFadein = at(i+1)->timestamp - fadeTime; 50 | squeezeInBetween(i + 1, new Waypoint(beginningOfFadein, 0)); 51 | //use i+1 again 52 | squeezeInBetween(i + 1, new Waypoint(endOfFadeout, 0)); 53 | } 54 | else 55 | { 56 | //we just need a trough halfway between the peaks 57 | qreal troughPosition = (at(i)->timestamp + at(i+1)->timestamp) / 2; 58 | qreal troughValue = troughLevel; 59 | if (lengthBetweenPeaks < OptionsDialog::getEstimMaxStrokeLength()) 60 | { 61 | troughValue = std::max(at(i)->multiplier, at(i+1)->multiplier) * troughLevel; 62 | } 63 | else 64 | { 65 | troughValue = (fadeTime*2 - lengthBetweenPeaks) / (fadeTime*2); 66 | } 67 | squeezeInBetween(i+1, new Waypoint(troughPosition, troughValue)); 68 | } 69 | } 70 | //i is now -1. Insert the beginning of the fade-in that comes before the first waypoint 71 | if (at(0)->timestamp > 0) 72 | { 73 | qreal startOfFadeIn = 0; 74 | if (at(0)->timestamp - fadeTime > 0) 75 | startOfFadeIn = at(0)->timestamp - fadeTime; 76 | insert(0, new Waypoint(startOfFadeIn, 0)); 77 | at(0)->next = at(1); 78 | at(1)->previous = at(0); 79 | } 80 | } 81 | 82 | void WaypointList::squeezeInBetween(int insertionIndex, Waypoint *newValue) 83 | { 84 | Q_ASSERT(insertionIndex < length() - 1); 85 | insert(insertionIndex, newValue); 86 | at(insertionIndex-1)->next = at(insertionIndex); 87 | at(insertionIndex)->previous = at(insertionIndex-1); 88 | at(insertionIndex)->next = at(insertionIndex+1); 89 | at(insertionIndex+1)->previous = at(insertionIndex); 90 | } 91 | -------------------------------------------------------------------------------- /Source/stimsignal/modifiers/waypointlist.h: -------------------------------------------------------------------------------- 1 | #ifndef WAYPOINTLIST_H 2 | #define WAYPOINTLIST_H 3 | 4 | class Waypoint; 5 | 6 | class WaypointList : public QList 7 | { 8 | public: 9 | WaypointList(); 10 | ~WaypointList(); 11 | 12 | void plonkOnTheEnd(Waypoint *newValue); 13 | void insertTroughs(qreal troughLevel); 14 | void squeezeInBetween(int insertionIndex, Waypoint * newValue); 15 | }; 16 | 17 | #endif // WAYPOINTLIST_H 18 | -------------------------------------------------------------------------------- /Source/stimsignal/monostimsignalsample.cpp: -------------------------------------------------------------------------------- 1 | #include "monostimsignalsample.h" 2 | 3 | MonoStimSignalSample::MonoStimSignalSample(qlonglong wholeTimestamp, qreal fractionalTimestamp, QObject *parent) 4 | : 5 | StimSignalSample(1, wholeTimestamp, fractionalTimestamp, parent) 6 | { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /Source/stimsignal/monostimsignalsample.h: -------------------------------------------------------------------------------- 1 | #ifndef MONOSTIMSIGNALSAMPLE_H 2 | #define MONOSTIMSIGNALSAMPLE_H 3 | 4 | #include "stimsignalsample.h" 5 | 6 | class MonoStimSignalSample : public StimSignalSample 7 | { 8 | public: 9 | explicit MonoStimSignalSample(qlonglong wholeTimestamp, qreal fractionalTimestamp, QObject *parent = nullptr); 10 | }; 11 | 12 | #endif // MONOSTIMSIGNALSAMPLE_H 13 | -------------------------------------------------------------------------------- /Source/stimsignal/multithreadedsamplepipelineprocessor.h: -------------------------------------------------------------------------------- 1 | #ifndef MULTITHREADEDSAMPLEPIPELINEPROCESSOR_H 2 | #define MULTITHREADEDSAMPLEPIPELINEPROCESSOR_H 3 | 4 | #include "stereostimsignalsample.h" 5 | //class StimSignalWorkPackage; 6 | //#include 7 | 8 | class StimSignalWorker; 9 | class StimSignalModifier; 10 | 11 | class MultithreadedSamplePipelineProcessor : public QObject 12 | { 13 | Q_OBJECT 14 | public: 15 | explicit MultithreadedSamplePipelineProcessor(QList * modifiers, QObject *parent = nullptr); 16 | ~MultithreadedSamplePipelineProcessor(); 17 | //! 18 | //! \brief processAll will run all the samples through all the modifiers and then return 19 | //! 20 | void processAll(QList * vector ); 21 | 22 | signals: 23 | void runThroughAllWorkers(int index); 24 | 25 | public slots: 26 | void registerWorkComplete(int index); 27 | 28 | private: 29 | void setUpWorkers(); 30 | QList * sampleVector; 31 | QList * modifiers; 32 | //belongs to old, slow method 33 | // QList workQueue; 34 | // int workAssigned; 35 | // QAtomicInt workComplete; 36 | QList workers; 37 | QList workerThreads; 38 | bool allDone = false; 39 | }; 40 | 41 | #endif // MULTITHREADEDSAMPLEPIPELINEPROCESSOR_H 42 | -------------------------------------------------------------------------------- /Source/stimsignal/separatelnrsignalgenerator.cpp: -------------------------------------------------------------------------------- 1 | #include "separatelnrsignalgenerator.h" 2 | #include "optionsdialog.h" 3 | #include "modifiers/phasesettermodifier.h" 4 | //#include "modifiers/singlechannelbeatproximitymodifier.h" 5 | #include "modifiers/waypointfollowermodifier.h" 6 | #include "modifiers/progressincreasemodifier.h" 7 | //#include "modifiers/boostfaststrokesmodifier.h" 8 | #include "modifiers/fadefromcoldmodifier.h" 9 | #include "modifiers/breaksoftenermodifier.h" 10 | #include "modifiers/channelbalancemodifier.h" 11 | #include "mainwindow.h" 12 | #include "stereostimsignalsample.h" 13 | 14 | #define LEFT_CHANNEL 0 15 | #define RIGHT_CHANNEL 1 16 | 17 | SeparateLnRSignalGenerator::SeparateLnRSignalGenerator(QAudioFormat audioFormat, QObject *parent) 18 | : 19 | StimSignalGenerator(audioFormat, parent) 20 | { 21 | 22 | } 23 | 24 | void SeparateLnRSignalGenerator::setModifiers() 25 | { 26 | modifiers.append(new PhaseSetterModifier(OptionsDialog::getEstimLeftChannelStartingFrequency(), OptionsDialog::getEstimLeftChannelEndingFrequency(), LEFT_CHANNEL)); 27 | modifiers.append(new PhaseSetterModifier(OptionsDialog::getEstimRightChannelStartingFrequency(), OptionsDialog::getEstimRightChannelEndingFrequency(), RIGHT_CHANNEL)); 28 | 29 | QList upstrokes{e_eventType::EVENT_STROKE_UP}; 30 | QVector upEvents = filteredEvents(upstrokes); 31 | WaypointList * leftVolumeWaypoints = createWaypointList(false, 32 | 0, 33 | OptionsDialog::getEstimLeftChannelTroughLevel(), 34 | upEvents); 35 | modifiers.append(new WaypointFollowerModifier(leftVolumeWaypoints, LEFT_CHANNEL)); 36 | 37 | QList downstrokes{e_eventType::EVENT_STROKE_DOWN}; 38 | QVector downEvents = filteredEvents(downstrokes); 39 | WaypointList * rightVolumeWaypoints = createWaypointList(false, 40 | 0, 41 | OptionsDialog::getEstimRightChannelTroughLevel(), 42 | downEvents); 43 | modifiers.append(new WaypointFollowerModifier(rightVolumeWaypoints, RIGHT_CHANNEL)); 44 | 45 | modifiers.append(new ProgressIncreaseModifier()); 46 | modifiers.append(new FadeFromColdModifier()); 47 | modifiers.append(new BreakSoftenerModifier()); 48 | if (OptionsDialog::getEstimSignalPan()) 49 | modifiers.append(new ChannelBalanceModifier()); 50 | } 51 | 52 | long SeparateLnRSignalGenerator::getStopTimestamp() 53 | { 54 | return mainWindow->totalPlayTime() + std::max(OptionsDialog::getEstimLeftChannelFadeTime(), OptionsDialog::getEstimRightChannelFadeTime()); 55 | } 56 | 57 | StimSignalSample *SeparateLnRSignalGenerator::createSample(qlonglong wholeTimestamp, qreal fractionalTimestamp) 58 | { 59 | return new StereoStimSignalSample(wholeTimestamp, fractionalTimestamp); 60 | } 61 | -------------------------------------------------------------------------------- /Source/stimsignal/separatelnrsignalgenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef SEPARATELNRSIGNALGENERATOR_H 2 | #define SEPARATELNRSIGNALGENERATOR_H 3 | 4 | #include "stimsignalgenerator.h" 5 | 6 | class SeparateLnRSignalGenerator : public StimSignalGenerator 7 | { 8 | public: 9 | explicit SeparateLnRSignalGenerator(QAudioFormat audioFormat, QObject *parent = nullptr); 10 | protected: 11 | void setModifiers() override; 12 | long getStopTimestamp() override; 13 | StimSignalSample * createSample(qlonglong wholeTimestamp, qreal fractionalTimestamp) override; 14 | }; 15 | 16 | #endif // SEPARATELNRSIGNALGENERATOR_H 17 | -------------------------------------------------------------------------------- /Source/stimsignal/singlechannelsignalgenerator.cpp: -------------------------------------------------------------------------------- 1 | #include "singlechannelsignalgenerator.h" 2 | #include "optionsdialog.h" 3 | #include "modifiers/phasesettermodifier.h" 4 | //#include "modifiers/singlechannelbeatproximitymodifier.h" 5 | #include "modifiers/waypointfollowermodifier.h" 6 | #include "modifiers/progressincreasemodifier.h" 7 | //#include "modifiers/boostfaststrokesmodifier.h" 8 | #include "modifiers/fadefromcoldmodifier.h" 9 | #include "modifiers/breaksoftenermodifier.h" 10 | #include "modifiers/channelbalancemodifier.h" 11 | #include "mainwindow.h" 12 | #include "monostimsignalsample.h" 13 | 14 | SingleChannelSignalGenerator::SingleChannelSignalGenerator(QAudioFormat audioFormat, QObject *parent) 15 | : 16 | StimSignalGenerator(audioFormat, parent) 17 | { 18 | 19 | } 20 | 21 | void SingleChannelSignalGenerator::setModifiers() 22 | { 23 | modifiers.append(new PhaseSetterModifier(OptionsDialog::getEstimLeftChannelStartingFrequency(), OptionsDialog::getEstimLeftChannelEndingFrequency())); 24 | WaypointList * volumeWaypoints = createWaypointList(OptionsDialog::getEstimLeftChannelStrokeStyle() == PREF_ESTIM_ENDS_ON_BEAT_STYLE, 25 | OptionsDialog::getEstimLeftChannelPeakPositionInCycle(), 26 | OptionsDialog::getEstimLeftChannelTroughLevel()); 27 | modifiers.append(new WaypointFollowerModifier(volumeWaypoints)); 28 | modifiers.append(new ProgressIncreaseModifier()); 29 | modifiers.append(new FadeFromColdModifier()); 30 | modifiers.append(new BreakSoftenerModifier()); 31 | if (OptionsDialog::getEstimSignalPan()) 32 | modifiers.append(new ChannelBalanceModifier()); 33 | } 34 | 35 | long SingleChannelSignalGenerator::getStopTimestamp() 36 | { 37 | return mainWindow->totalPlayTime() + OptionsDialog::getEstimLeftChannelFadeTime(); 38 | } 39 | 40 | StimSignalSample *SingleChannelSignalGenerator::createSample(qlonglong wholeTimestamp, qreal fractionalTimestamp) 41 | { 42 | return new MonoStimSignalSample(wholeTimestamp, fractionalTimestamp); 43 | } 44 | -------------------------------------------------------------------------------- /Source/stimsignal/singlechannelsignalgenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef SINGLECHANNELSIGNALGENERATOR_H 2 | #define SINGLECHANNELSIGNALGENERATOR_H 3 | 4 | #include "stimsignalgenerator.h" 5 | 6 | class SingleChannelSignalGenerator : public StimSignalGenerator 7 | { 8 | public: 9 | explicit SingleChannelSignalGenerator(QAudioFormat audioFormat, QObject *parent = nullptr); 10 | protected: 11 | void setModifiers() override; 12 | long getStopTimestamp() override; 13 | StimSignalSample * createSample(qlonglong wholeTimestamp, qreal fractionalTimestamp) override; 14 | }; 15 | 16 | #endif // SINGLECHANNELSIGNALGENERATOR_H 17 | -------------------------------------------------------------------------------- /Source/stimsignal/stereostimsignalsample.cpp: -------------------------------------------------------------------------------- 1 | #include "stereostimsignalsample.h" 2 | #include 3 | 4 | StereoStimSignalSample::StereoStimSignalSample(qlonglong wholeTimestamp, qreal fractionalTimestamp, QObject *parent) 5 | : 6 | StimSignalSample(2, wholeTimestamp, fractionalTimestamp, parent) 7 | { 8 | 9 | } 10 | 11 | qreal StereoStimSignalSample::getSecondaryPhase() const 12 | { 13 | return getPhase(1); 14 | } 15 | 16 | void StereoStimSignalSample::setSecondaryPhase(const qreal &value) 17 | { 18 | setPhase(1, value); 19 | } 20 | 21 | qreal StereoStimSignalSample::secondaryValue() 22 | { 23 | return value(1); 24 | } 25 | 26 | qint16 StereoStimSignalSample::secondaryPcm() 27 | { 28 | return realToPcm(secondaryValue()); 29 | } 30 | 31 | qreal StereoStimSignalSample::getSecondaryAmplitude() const 32 | { 33 | return getAmplitude(1); 34 | } 35 | 36 | void StereoStimSignalSample::setSecondaryAmplitude(const qreal &value) 37 | { 38 | setAmplitude(1, value); 39 | } 40 | -------------------------------------------------------------------------------- /Source/stimsignal/stereostimsignalsample.h: -------------------------------------------------------------------------------- 1 | #ifndef STIMSIGNALFRAME_H 2 | #define STIMSIGNALFRAME_H 3 | 4 | #include "stimsignalsample.h" 5 | 6 | class StereoStimSignalSample : public StimSignalSample 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit StereoStimSignalSample(qlonglong wholeTimestamp, qreal fractionalTimestamp, QObject *parent = nullptr); 11 | 12 | qreal secondaryValue(); 13 | 14 | qint16 secondaryPcm(); 15 | 16 | qreal getSecondaryAmplitude() const; 17 | void setSecondaryAmplitude(const qreal &value); 18 | 19 | qreal getSecondaryPhase() const; 20 | void setSecondaryPhase(const qreal &value); 21 | 22 | signals: 23 | 24 | }; 25 | 26 | #endif // STIMSIGNALFRAME_H 27 | -------------------------------------------------------------------------------- /Source/stimsignal/stimsignalfile.cpp: -------------------------------------------------------------------------------- 1 | #include "stimsignalfile.h" 2 | #include 3 | #include 4 | 5 | StimSignalFile::StimSignalFile(QString filename, QObject *parent) 6 | : 7 | QFile(filename, parent) 8 | { 9 | } 10 | 11 | bool StimSignalFile::open(QIODevice::OpenMode mode) 12 | { 13 | bool success = QIODevice::open(mode); 14 | if (success && fileName().endsWith(".wav")) 15 | { 16 | char sampleRateData[4]; 17 | seek(24); //that's where the sample rate is stored 18 | read(sampleRateData, 4); 19 | sampleRate = qFromLittleEndian(sampleRateData); 20 | 21 | char bytesPerSecData[4]; 22 | seek(28); //bytes per second 23 | read(bytesPerSecData, 4); 24 | bytesPerSecond = qFromLittleEndian(bytesPerSecData); 25 | 26 | char frameSizeData[4]; 27 | seek(32); //that's where the size of a 'frame' is stored (mono sample size (2 bytes) * num channels (1 or 2)) 28 | read(frameSizeData, 4); 29 | frameSize = qFromLittleEndian(frameSizeData); 30 | 31 | seek(WAV_HEADER_SIZE); //beginning of data section 32 | } 33 | return success; 34 | } 35 | 36 | void StimSignalFile::close() 37 | { 38 | return QIODevice::close(); 39 | } 40 | 41 | void StimSignalFile::setPlayFrom(long timestamp) 42 | { 43 | //calculate the frame number we need. there are sampleRate frames per second 44 | long frameNumber = timestamp * sampleRate; 45 | //but timestamp is in miliseconds, so we've gone 1000 times too far... 46 | frameNumber /= 1000; 47 | //this is the number of the frame we need 48 | //the frame will be this many bytes into the file: 49 | long seekLocation = frameNumber * frameSize; 50 | //plus space for the header 51 | seekLocation += WAV_HEADER_SIZE; 52 | 53 | seek(seekLocation); 54 | } 55 | 56 | QAudioFormat StimSignalFile::deriveFormatFromFile(QFile * file) 57 | { 58 | if (!file->fileName().endsWith(".wav")) 59 | { 60 | qDebug() << "This isn't a wav file. We don't support that yet (consider supporting me?)"; 61 | return QAudioFormat(); 62 | } 63 | bool fileWasOpen = file->isOpen(); 64 | if (!fileWasOpen && !file->open(QIODevice::ReadOnly)) 65 | { 66 | qDebug() << "Could not open file to fild audio format."; 67 | return QAudioFormat(); 68 | } 69 | 70 | QAudioFormat format; 71 | format.setByteOrder(QAudioFormat::LittleEndian); //wav is little-endian 72 | format.setSampleType(QAudioFormat::SignedInt); //or at least that's the way the generator creates them 73 | format.setCodec("audio/pcm"); 74 | 75 | char sampleRateData[4]; 76 | file->seek(SAMPLE_RATE_LOCATION); 77 | file->read(sampleRateData, 4); 78 | int sampleRate = qFromLittleEndian(sampleRateData); 79 | format.setSampleRate(sampleRate); 80 | 81 | char channelCountData[2]; 82 | file->seek(CHANNEL_COUNT_LOCATION); 83 | file->read(channelCountData, 2); 84 | qint16 channelCount = qFromLittleEndian(channelCountData); 85 | format.setChannelCount(channelCount); 86 | 87 | char bitsPerSampleData[2]; 88 | file->seek(BITS_PER_SAMPLE_LOCATION); 89 | file->read(bitsPerSampleData, 2); 90 | qint16 bitsPerSample = qFromLittleEndian(bitsPerSampleData); 91 | format.setSampleSize(bitsPerSample); 92 | 93 | if (!fileWasOpen) 94 | file->close(); 95 | return format; 96 | } 97 | 98 | void StimSignalFile::seekToTimestamp(QFile * file, long timestamp, int sampleRate, int frameSize) 99 | { 100 | //calculate the frame number we need. there are sampleRate frames per second 101 | long frameNumber = timestamp * sampleRate; 102 | //but timestamp is in miliseconds, so we've gone 1000 times too far... 103 | frameNumber /= 1000; 104 | //this is the number of the frame we need 105 | //the frame will be this many bytes into the file: 106 | long seekLocation = frameNumber * frameSize; 107 | //plus space for the header 108 | seekLocation += WAV_HEADER_SIZE; 109 | 110 | file->seek(seekLocation); 111 | } 112 | 113 | 114 | //qint64 StimSignalFile::readData(char *data, qint64 maxlen) 115 | //{ 116 | // return file.readData(data, maxlen); 117 | //} 118 | -------------------------------------------------------------------------------- /Source/stimsignal/stimsignalfile.h: -------------------------------------------------------------------------------- 1 | #ifndef STIMSIGNALFILE_H 2 | #define STIMSIGNALFILE_H 3 | 4 | #include "stimsignalsource.h" 5 | #include 6 | #include 7 | 8 | class StimSignalFile : public QFile, public StimSignalSource 9 | { 10 | public: 11 | StimSignalFile(QString filename, QObject * parent); 12 | bool open(QIODevice::OpenMode mode) override; 13 | void close() override; 14 | void setPlayFrom(long timestamp) override; 15 | static QAudioFormat deriveFormatFromFile(QFile *); 16 | static void seekToTimestamp(QFile *file, long timestamp, int sampleRate, int frameSize); 17 | // qint64 readData(char *data, qint64 maxlen) override; 18 | // qint64 writeData(const char *data, qint64 len) override; 19 | 20 | private: 21 | qint32 sampleRate; 22 | qint32 bytesPerSecond; 23 | qint32 frameSize; 24 | 25 | static const int SAMPLE_RATE_LOCATION = 24; //4 bytes 26 | static const int CHANNEL_COUNT_LOCATION = 22; //2 bytes 27 | // static const int BYTES_PER_FRAME_LOCATION = 32; //2 bytes 28 | static const int BITS_PER_SAMPLE_LOCATION = 34; //2 bytes 29 | static const int WAV_HEADER_SIZE = 44; 30 | }; 31 | 32 | #endif // STIMSIGNALFILE_H 33 | -------------------------------------------------------------------------------- /Source/stimsignal/stimsignalgenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef STIMSIGNALGENERATOR_H 2 | #define STIMSIGNALGENERATOR_H 3 | 4 | #include 5 | #include "stimsignalsource.h" 6 | class StimSignalModifier; 7 | class StimSignalSample; 8 | class MultithreadedSamplePipelineProcessor; 9 | class WaypointList; 10 | struct Event; 11 | extern QVector events; 12 | 13 | //DONE: Add prefs for: starting frequ, - Used 14 | // ending freq, - Used 15 | // percentage vol increase over duration, - Used 16 | // max stroke duration, - Used 17 | // (slightly?) increase volume of faster strokes - Used 18 | // stroke type (up-down-beat or down-beat-up) - Used 19 | // invert phase - Used 20 | // initial fade-in time from cold - Used 21 | // lower volume upto X% after breaks upto Yseconds - Used 22 | // lift volume reduction over period of... - Used 23 | // panning left or right - Used 24 | //DONE: Use above prefs 25 | //DONE: Allow saving of signal to a file 26 | //DONE: Allow selection of audio device (stim signal) 27 | //DONE: Start and stop signal with video 28 | 29 | class StimSignalGenerator : public QIODevice, public StimSignalSource 30 | { 31 | Q_OBJECT 32 | public: 33 | explicit StimSignalGenerator(QAudioFormat audioFormat, QObject *parent = nullptr); 34 | ~StimSignalGenerator(); 35 | //TODO: destructor to delete modifiers 36 | 37 | qint64 generate(char *data, qint64 maxlen); 38 | 39 | bool open(OpenMode mode) override; 40 | void close() override; 41 | qint64 readData(char *data, qint64 maxlen) override; 42 | qint64 writeData(const char *data, qint64 len) override; 43 | bool seek(qint64 pos) override; //don't do this 44 | bool isSequential() const override; 45 | void setGenerateFrom(long from); 46 | void setPlayFrom(long timestamp) override; 47 | 48 | static StimSignalGenerator * createFromPrefs(QObject * parent); 49 | 50 | protected: 51 | virtual void setModifiers() = 0; 52 | virtual long getStopTimestamp() = 0; 53 | int sampleCounter; 54 | QAudioFormat audioFormat; 55 | long generateFromTimestamp = 0; 56 | int startingFrequency; 57 | int endingFrequency; 58 | QList modifiers; 59 | WaypointList * createWaypointList(bool waypointsComeOnOrBeforeBeat, qreal peakPositionInCycle, qreal troughLevel, QVector eventsToUse = events); 60 | 61 | private: 62 | virtual StimSignalSample * createSample(qlonglong wholeTimestamp, qreal fractionalTimestamp) = 0; 63 | MultithreadedSamplePipelineProcessor * sampleProcessor; 64 | 65 | signals: 66 | void progressed(int progress, int outOf); 67 | 68 | }; 69 | 70 | #endif // STIMSIGNALGENERATOR_H 71 | -------------------------------------------------------------------------------- /Source/stimsignal/stimsignalmodifier.cpp: -------------------------------------------------------------------------------- 1 | #include "stimsignalmodifier.h" 2 | 3 | StimSignalModifier::StimSignalModifier(QObject *parent) : QObject(parent) 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /Source/stimsignal/stimsignalmodifier.h: -------------------------------------------------------------------------------- 1 | #ifndef STIMSIGNALMODIFIER_H 2 | #define STIMSIGNALMODIFIER_H 3 | 4 | class StimSignalSample; 5 | 6 | class StimSignalModifier : public QObject 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit StimSignalModifier(QObject *parent = nullptr); 11 | 12 | virtual void modify(StimSignalSample & sample) = 0; 13 | 14 | signals: 15 | 16 | }; 17 | 18 | #endif // STIMSIGNALMODIFIER_H 19 | -------------------------------------------------------------------------------- /Source/stimsignal/stimsignalsample.cpp: -------------------------------------------------------------------------------- 1 | #include "stimsignalsample.h" 2 | #include 3 | 4 | StimSignalSample::StimSignalSample(int numChannels, qlonglong wholeTimestamp, qreal fractionalTimestamp, QObject *parent) 5 | : 6 | QObject(parent), 7 | wholeTimestamp(wholeTimestamp), 8 | fractionalTimestamp(fractionalTimestamp), 9 | numChannels(numChannels) 10 | { 11 | for (int i = 0; i < numChannels; ++i) { 12 | amplitudes.append(1); 13 | phases.append(0); 14 | } 15 | } 16 | 17 | //! 18 | //! \brief StimSignalSample::distanceToTimestamp 19 | //! \param timestamp the timestamp (in ms) we're working the distance to 20 | //! \return the number of milliseconds to the given timestamp. Positive if it's in the future. Negative if the given timestamp is before this one. 21 | //! 22 | qreal StimSignalSample::distanceToTimestamp(qlonglong timestamp) 23 | { 24 | qlonglong wholeDistance = timestamp - wholeTimestamp; 25 | return wholeDistance - fractionalTimestamp; 26 | } 27 | 28 | qreal StimSignalSample::getAmplitude(int channel) const 29 | { 30 | return amplitudes[channel]; 31 | } 32 | 33 | void StimSignalSample::setAmplitude(int channel, const qreal &value) 34 | { 35 | amplitudes[channel] = value; 36 | } 37 | 38 | qreal StimSignalSample::getPrimaryAmplitude() const 39 | { 40 | return getAmplitude(0); 41 | } 42 | 43 | void StimSignalSample::setPrimaryAmplitude(const qreal &value) 44 | { 45 | setAmplitude(0, value); 46 | } 47 | 48 | qreal StimSignalSample::getPhase(int channel) const 49 | { 50 | return phases[channel]; 51 | } 52 | 53 | void StimSignalSample::setPhase(int channel, const qreal &value) 54 | { 55 | phases[channel] = value; 56 | phases[channel] -= (int) phases[channel]; 57 | } 58 | 59 | qreal StimSignalSample::getPrimaryPhase() const 60 | { 61 | return getPhase(0); 62 | } 63 | 64 | void StimSignalSample::setPrimaryPhase(const qreal &value) 65 | { 66 | setPhase(0, value); 67 | } 68 | 69 | qreal StimSignalSample::value(int channel) 70 | { 71 | return amplitudes[channel] * qSin(2 * M_PI * phases[channel]); 72 | } 73 | 74 | qreal StimSignalSample::primaryValue() 75 | { 76 | return value(0); 77 | } 78 | 79 | qint16 StimSignalSample::pcm(int channel) 80 | { 81 | return realToPcm(value(channel)); 82 | } 83 | 84 | qint16 StimSignalSample::primaryPcm() 85 | { 86 | return pcm(0); 87 | } 88 | 89 | const qint16 PCMS16MaxValue = 32767; 90 | //const quint16 PCMS16MaxAmplitude = 32768; // because minimum is -32768 91 | 92 | qint16 StimSignalSample::realToPcm(qreal real) 93 | { 94 | return real * PCMS16MaxValue; 95 | } 96 | -------------------------------------------------------------------------------- /Source/stimsignal/stimsignalsample.h: -------------------------------------------------------------------------------- 1 | #ifndef STIMSIGNALSAMPLE_H 2 | #define STIMSIGNALSAMPLE_H 3 | 4 | class StimSignalSample : public QObject 5 | { 6 | Q_OBJECT 7 | public: 8 | explicit StimSignalSample(int numChannels, qlonglong wholeTimestamp, qreal fractionalTimestamp, QObject *parent = nullptr); 9 | 10 | qreal totalTimestamp() const {return wholeTimestamp + fractionalTimestamp;} 11 | qreal distanceToTimestamp(qlonglong timestamp); 12 | 13 | char numberOfChannels(){return numChannels;} 14 | qreal getAmplitude(int channel) const; 15 | void setAmplitude(int channel, const qreal &value); 16 | qreal getPrimaryAmplitude() const; 17 | void setPrimaryAmplitude(const qreal &value); 18 | 19 | qreal getPhase(int channel) const; 20 | void setPhase(int channel, const qreal &value); 21 | qreal getPrimaryPhase() const; 22 | void setPrimaryPhase(const qreal &value); 23 | 24 | qreal value(int channel); 25 | qreal primaryValue(); 26 | 27 | qint16 pcm(int channel); 28 | qint16 primaryPcm(); 29 | 30 | protected: 31 | qlonglong wholeTimestamp; //in ms 32 | qreal fractionalTimestamp; //also in ms (may be larger than 1) 33 | char numChannels; 34 | QList amplitudes; 35 | QList phases; 36 | qint16 realToPcm(qreal real); 37 | }; 38 | 39 | #endif // STIMSIGNALSAMPLE_H 40 | -------------------------------------------------------------------------------- /Source/stimsignal/stimsignalsamplefactory.cpp: -------------------------------------------------------------------------------- 1 | #include "stimsignalsamplefactory.h" 2 | 3 | StimSignalSampleFactory::StimSignalSampleFactory(QObject *parent) : QObject(parent) 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /Source/stimsignal/stimsignalsamplefactory.h: -------------------------------------------------------------------------------- 1 | #ifndef STIMSIGNALSAMPLEFACTORY_H 2 | #define STIMSIGNALSAMPLEFACTORY_H 3 | 4 | class StimSignalSampleFactory : public QObject 5 | { 6 | Q_OBJECT 7 | public: 8 | explicit StimSignalSampleFactory(QObject *parent = nullptr); 9 | 10 | signals: 11 | 12 | }; 13 | 14 | #endif // STIMSIGNALSAMPLEFACTORY_H 15 | -------------------------------------------------------------------------------- /Source/stimsignal/stimsignalsource.cpp: -------------------------------------------------------------------------------- 1 | #include "stimsignalsource.h" 2 | #include "optionsdialog.h" 3 | #include "stimsignalgenerator.h" 4 | #include "stimsignalfile.h" 5 | #include "mainwindow.h" 6 | #include 7 | #include 8 | 9 | StimSignalSource::StimSignalSource() 10 | { 11 | 12 | } 13 | 14 | QIODevice *StimSignalSource::createFromPrefs(QObject *parent) 15 | { 16 | QString userSpecifiedFilename = OptionsDialog::getEstimSourceFilename(); 17 | 18 | QString pregeneratedFilename = calculatePregeneratedStimFilename(); 19 | 20 | switch (OptionsDialog::getEstimSourceMode()) 21 | { 22 | case FROM_FILE: 23 | return new QFile(userSpecifiedFilename, parent); 24 | case PREGENERATED: 25 | return new QFile(pregeneratedFilename, parent); 26 | case ON_THE_FLY: 27 | return StimSignalGenerator::createFromPrefs(parent); 28 | } 29 | return nullptr; 30 | } 31 | 32 | QString StimSignalSource::calculatePregeneratedStimFilename() 33 | { 34 | QString basePath = OptionsDialog::getLastOpenedLocation(); 35 | if (basePath.isEmpty()) 36 | basePath = QDir::toNativeSeparators(QDir::homePath()); 37 | 38 | QString fullPath = loadedScript; 39 | if (fullPath.isEmpty()) 40 | fullPath = loadedVideo; 41 | if (fullPath.isEmpty()) 42 | fullPath = QDir::cleanPath(basePath + QDir::separator() + "Untitled.wav"); 43 | QFileInfo pathInfo(fullPath); 44 | QString baseName = pathInfo.completeBaseName(); //baseName or completeBaseName? 45 | QString generatedFilename = QString("%1-%2-%3.wav") 46 | .arg(baseName) 47 | .arg(OptionsDialog::getEstimSettingsFilenameSuffix()) 48 | .arg(currentEventsHash(),0,16,QLatin1Char('0')); 49 | return QDir::cleanPath(pathInfo.path() + QDir::separator() + generatedFilename); 50 | } 51 | 52 | #define A 54059 /* a prime */ 53 | #define B 76963 /* another prime */ 54 | #define C 86969 /* yet another prime */ 55 | #define FIRSTH 37 /* also prime */ 56 | unsigned int StimSignalSource::currentEventsHash() 57 | { 58 | unsigned h = FIRSTH; 59 | for (auto event : events) 60 | { 61 | h = (h * A) ^ (event.toHash() * B); 62 | } 63 | return h; // or return h % C; 64 | } 65 | -------------------------------------------------------------------------------- /Source/stimsignal/stimsignalsource.h: -------------------------------------------------------------------------------- 1 | #ifndef STIMSIGNALSOURCE_H 2 | #define STIMSIGNALSOURCE_H 3 | 4 | #include 5 | 6 | class StimSignalSource 7 | { 8 | public: 9 | StimSignalSource(); 10 | virtual bool open(QIODevice::OpenMode mode) = 0; 11 | virtual void close() = 0; 12 | virtual void setPlayFrom(long timestamp) = 0; 13 | 14 | static QIODevice * createFromPrefs(QObject * parent); 15 | static QString calculatePregeneratedStimFilename(); 16 | 17 | private: 18 | static unsigned int currentEventsHash(); 19 | }; 20 | 21 | #endif // STIMSIGNALSOURCE_H 22 | -------------------------------------------------------------------------------- /Source/stimsignal/stimsignalworker.cpp: -------------------------------------------------------------------------------- 1 | #include "stimsignalworker.h" 2 | #include "stimsignalmodifier.h" 3 | 4 | StimSignalWorker::StimSignalWorker(QList *sampleVector, StimSignalModifier *modifier, QObject *parent) 5 | : 6 | QObject(parent), 7 | sampleVector(sampleVector), 8 | modifier(modifier) 9 | { 10 | 11 | } 12 | 13 | void StimSignalWorker::setVector(QList *newVector) 14 | { 15 | sampleVector = newVector; 16 | } 17 | 18 | void StimSignalWorker::processIndex(int index) 19 | { 20 | modifier->modify(*((*sampleVector)[index])); 21 | emit indexProcessed(index); 22 | } 23 | -------------------------------------------------------------------------------- /Source/stimsignal/stimsignalworker.h: -------------------------------------------------------------------------------- 1 | #ifndef STIMSIGNALWORKER_H 2 | #define STIMSIGNALWORKER_H 3 | 4 | class StimSignalSample; 5 | class StimSignalModifier; 6 | template class QList; 7 | 8 | class StimSignalWorker : public QObject 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit StimSignalWorker(QList * sampleVector, StimSignalModifier * modifier, QObject *parent = nullptr); 13 | void setVector(QList * newVector); 14 | 15 | signals: 16 | void indexProcessed(int index); 17 | 18 | public slots: 19 | void processIndex(int index); 20 | 21 | private: 22 | QList * sampleVector; 23 | StimSignalModifier * modifier; 24 | }; 25 | 26 | #endif // STIMSIGNALWORKER_H 27 | -------------------------------------------------------------------------------- /Source/stimsignal/triphasesignalgenerator.cpp: -------------------------------------------------------------------------------- 1 | #include "triphasesignalgenerator.h" 2 | #include "modifiers/phasesettermodifier.h" 3 | #include "modifiers/triphasemodifier.h" 4 | #include "modifiers/triphasebeatproximitymodifier.h" 5 | #include "modifiers/progressincreasemodifier.h" 6 | #include "modifiers/boostfaststrokesmodifier.h" 7 | #include "modifiers/phaseinvertermodifier.h" 8 | #include "modifiers/fadefromcoldmodifier.h" 9 | #include "modifiers/breaksoftenermodifier.h" 10 | #include "modifiers/channelbalancemodifier.h" 11 | #include "mainwindow.h" 12 | #include "optionsdialog.h" 13 | #include "stereostimsignalsample.h" 14 | 15 | 16 | TriphaseSignalGenerator::TriphaseSignalGenerator(QAudioFormat audioFormat, QObject *parent) 17 | : 18 | StimSignalGenerator(audioFormat, parent) 19 | { 20 | } 21 | 22 | void TriphaseSignalGenerator::setModifiers() 23 | { 24 | modifiers.append(new PhaseSetterModifier(OptionsDialog::getEstimTriphaseStartingFrequency(), OptionsDialog::getEstimTriphaseEndingFrequency())); 25 | modifiers.append(new TriphaseModifier()); 26 | modifiers.append(new TriphaseBeatProximityModifier()); 27 | modifiers.append(new ProgressIncreaseModifier()); 28 | modifiers.append(new BoostFastStrokesModifier()); 29 | if (OptionsDialog::getEstimInvertStrokes()) 30 | modifiers.append(new PhaseInverterModifier()); 31 | modifiers.append(new FadeFromColdModifier()); 32 | modifiers.append(new BreakSoftenerModifier()); 33 | if (OptionsDialog::getEstimSignalPan()) 34 | modifiers.append(new ChannelBalanceModifier()); 35 | } 36 | 37 | long TriphaseSignalGenerator::getStopTimestamp() 38 | { 39 | return mainWindow->totalPlayTime() + OptionsDialog::getEstimBeatFadeOutDelay() + OptionsDialog::getEstimBeatFadeOutTime(); 40 | } 41 | 42 | StimSignalSample *TriphaseSignalGenerator::createSample(qlonglong wholeTimestamp, qreal fractionalTimestamp) 43 | { 44 | return new StereoStimSignalSample(wholeTimestamp, fractionalTimestamp); 45 | } 46 | -------------------------------------------------------------------------------- /Source/stimsignal/triphasesignalgenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef TRIPHASESIGNALGENERATOR_H 2 | #define TRIPHASESIGNALGENERATOR_H 3 | 4 | #include "stimsignalgenerator.h" 5 | 6 | class TriphaseSignalGenerator : public StimSignalGenerator 7 | { 8 | public: 9 | explicit TriphaseSignalGenerator(QAudioFormat audioFormat, QObject *parent = nullptr); 10 | protected: 11 | void setModifiers() override; 12 | long getStopTimestamp() override; 13 | StimSignalSample * createSample(qlonglong wholeTimestamp, qreal fractionalTimestamp) override; 14 | }; 15 | 16 | #endif // TRIPHASESIGNALGENERATOR_H 17 | -------------------------------------------------------------------------------- /Source/syncfilewriter.cpp: -------------------------------------------------------------------------------- 1 | #include "syncfilewriter.h" 2 | 3 | SyncFileWriter::SyncFileWriter(QFile & file, QObject *parent) 4 | : 5 | QObject(parent), 6 | file(file) 7 | { 8 | //nothing 9 | } 10 | -------------------------------------------------------------------------------- /Source/syncfilewriter.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCFILEWRITER_H 2 | #define SYNCFILEWRITER_H 3 | 4 | class QFile; 5 | 6 | class SyncFileWriter : public QObject 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit SyncFileWriter(QFile &, QObject *parent = nullptr); 11 | virtual void writeHeader() = 0; 12 | virtual void addEvent(long timestamp, int position) = 0; 13 | virtual void writeFooter() = 0; 14 | 15 | protected: 16 | QFile & file; 17 | signals: 18 | 19 | }; 20 | 21 | #endif // SYNCFILEWRITER_H 22 | -------------------------------------------------------------------------------- /Source/uniquebeatinterval.cpp: -------------------------------------------------------------------------------- 1 | #include "uniquebeatinterval.h" 2 | #include "config.h" 3 | #include "helperfunctions.h" 4 | #include "globals.h" 5 | 6 | UniqueBeatInterval::UniqueBeatInterval() 7 | : 8 | length(0), 9 | count(0) 10 | { 11 | //nothing here 12 | } 13 | 14 | UniqueBeatInterval::UniqueBeatInterval(float len) 15 | : 16 | length(len), 17 | count(1) 18 | { 19 | //nothing here 20 | } 21 | 22 | UniqueBeatInterval::UniqueBeatInterval(float len, short cnt) 23 | : 24 | length(len), 25 | count(cnt) 26 | { 27 | //nothing here 28 | } 29 | 30 | UniqueBeatInterval::~UniqueBeatInterval() 31 | { 32 | } 33 | 34 | int UniqueBeatInterval::registerInterval(float interval) 35 | { 36 | if (matchesThisInterval(interval)) 37 | { 38 | double workingValue = (count * length) + interval; 39 | ++count; 40 | length = workingValue / count; 41 | } 42 | else 43 | { 44 | length = interval; 45 | count = 1; 46 | } 47 | return length; 48 | } 49 | -------------------------------------------------------------------------------- /Source/uniquebeatinterval.h: -------------------------------------------------------------------------------- 1 | #ifndef UNIQUEBEATINTERVAL_H 2 | #define UNIQUEBEATINTERVAL_H 3 | 4 | #include "abstractbeatinterval.h" 5 | 6 | class UniqueBeatInterval : public AbstractBeatInterval 7 | { 8 | public: 9 | UniqueBeatInterval(); 10 | UniqueBeatInterval(float len); 11 | UniqueBeatInterval(float len, short cnt); 12 | ~UniqueBeatInterval(); 13 | 14 | float length; 15 | short count; 16 | 17 | int registerInterval(float interval); 18 | 19 | float getLength() const {return length;} 20 | }; 21 | 22 | void reorderUniqueBeatIntervals(); 23 | 24 | #endif // UNIQUEBEATINTERVAL_H 25 | -------------------------------------------------------------------------------- /Source/valuedatamodel.h: -------------------------------------------------------------------------------- 1 | #ifndef VALUEDATAMODEL_H 2 | #define VALUEDATAMODEL_H 3 | 4 | #include 5 | 6 | class BeatValue; 7 | 8 | class ValueDataModel : public QAbstractTableModel 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit ValueDataModel(QObject *parent = 0); 13 | int rowCount ( const QModelIndex & parent = QModelIndex() ) const; 14 | int columnCount ( const QModelIndex & parent = QModelIndex() ) const; 15 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 16 | Qt::ItemFlags flags(const QModelIndex &index) const; 17 | QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; 18 | bool setData(const QModelIndex &index, const QVariant &value, int role); 19 | void addValue(const BeatValue & valueToAdd); 20 | bool removeRow (int row); 21 | void recalculateCounts(); 22 | 23 | signals: 24 | 25 | public slots: 26 | 27 | private: 28 | mutable QVector counts; 29 | 30 | }; 31 | 32 | #endif // VALUEDATAMODEL_H 33 | -------------------------------------------------------------------------------- /Source/valuetablekeyboardeventhandler.cpp: -------------------------------------------------------------------------------- 1 | #include "valuetablekeyboardeventhandler.h" 2 | #include "globals.h" 3 | #include "editorwindow.h" 4 | #include "ui_editorwindow.h" 5 | #include "valuedatamodel.h" 6 | 7 | ValueTableKeyboardEventHandler::ValueTableKeyboardEventHandler(QObject *parent) : 8 | QObject(parent) 9 | { 10 | } 11 | 12 | bool ValueTableKeyboardEventHandler::eventFilter(QObject *obj, QEvent *event) 13 | { 14 | if (event->type() == QEvent::KeyPress) 15 | { 16 | QKeyEvent *keyEvent = static_cast(event); 17 | if (keyEvent->key() == Qt::Key_Delete) 18 | { 19 | keyPressEvent(keyEvent); 20 | return true; 21 | } 22 | } 23 | return QObject::eventFilter(obj, event); 24 | } 25 | 26 | void ValueTableKeyboardEventHandler::keyPressEvent(QKeyEvent *event) 27 | { 28 | if ( event->key() == Qt::Key_Delete ) 29 | editor->valueModel->removeRow(editor->ui->beatValuesTable->selectionModel()->selectedRows(1).first().row()); 30 | else 31 | event->ignore(); 32 | } 33 | -------------------------------------------------------------------------------- /Source/valuetablekeyboardeventhandler.h: -------------------------------------------------------------------------------- 1 | #ifndef VALUETABLEKEYBOARDEVENTHANDLER_H 2 | #define VALUETABLEKEYBOARDEVENTHANDLER_H 3 | 4 | #include 5 | 6 | class ValueTableKeyboardEventHandler : public QObject 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit ValueTableKeyboardEventHandler(QObject *parent = 0); 11 | bool eventFilter(QObject *obj, QEvent *event); 12 | void keyPressEvent(QKeyEvent *event); 13 | 14 | signals: 15 | 16 | public slots: 17 | 18 | }; 19 | 20 | #endif // VALUETABLEKEYBOARDEVENTHANDLER_H 21 | -------------------------------------------------------------------------------- /Source/wavefileexporter.h: -------------------------------------------------------------------------------- 1 | #ifndef WAVEFILEEXPORTER_H 2 | #define WAVEFILEEXPORTER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class WaveFileExporter : public QObject 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit WaveFileExporter(QString inputWavFilename, QString outputWavFilename, QObject *parent = 0); 13 | bool inputFileIsValid(); 14 | void writeOutputHeader(); 15 | void writeSoundAt(unsigned int timestamp); 16 | void printDebugInformation(); 17 | void closeFile(); 18 | 19 | private: 20 | QFile inputFile; 21 | QFile outputFile; 22 | QByteArray inputHeaderData; 23 | 24 | QDataStream inputData; 25 | QDataStream outputData; 26 | 27 | quint16 inputDataChunkStartPos; 28 | 29 | void processInputHeader(); 30 | bool inputHeaderProcessed; 31 | 32 | qint16 numChannels; 33 | qint32 sampleRate; 34 | qint32 bitRate; 35 | qint16 bytesPerFrame; 36 | qint16 sampleResolution; 37 | quint32 inputAudioSize; 38 | 39 | quint32 sizeOfOutputDataSectionInBytes; 40 | 41 | qint32 intFromBytes(QByteArray bytes); 42 | 43 | static const char * const riffTag; 44 | static const char * const waveTag; 45 | static const char * const dataTag; 46 | 47 | signals: 48 | 49 | public slots: 50 | 51 | }; 52 | 53 | #endif // WAVEFILEEXPORTER_H 54 | -------------------------------------------------------------------------------- /notes_on_deploying.txt: -------------------------------------------------------------------------------- 1 | REM Looks like this file used to be a batch script in a former life! 2 | 3 | REM Step 1: Build Cock Heroine (from within Qt Creator, or however. Make note of the location where the .exe is produced) 4 | REM Step 2: Run this script (make sure the path to the exe is the one you just built) 5 | REM Step 3: Clear up unwanted files from the build directory, collect 'em all up and put somewhere for people to use. 6 | 7 | REM Not sure why this doesn't work. :-/ 8 | REM set PATH="C:\Qt\Qt5.12.12\5.12.12\mingw73_32\bin;%PATH%" 9 | REM windeployqt.exe C:\Users\mm\Programming\Cpp\build-CockHeroine-Desktop_Qt_5_12_12_MinGW_32_bit-Release\release\CockHeroine.exe 10 | 11 | REM Use the full path instead 12 | REM C:\Qt\Qt5.12.12\5.12.12\mingw73_32\bin\windeployqt.exe C:\Users\mm\Programming\Cpp\build-CockHeroine-Desktop_Qt_5_12_12_MinGW_32_bit-Release\release\CockHeroine.exe 13 | REM The above doesn't work either!! 14 | 15 | REM Actual solution: 16 | REM Open the Qt command prompt (C:\Windows\System32\cmd.exe /A /Q /K C:\Qt\Qt5.12.12\5.12.12\mingw73_32\bin\qtenv2.bat) 17 | REM Then cd to the release directory: cd C:\Users\mm\Programming\Cpp\build-CockHeroine-Desktop_Qt_5_12_12_MinGW_32_bit-Release\release 18 | REM Now run: windeployqt CockHeroine.exe 19 | REM for 64-bit, change mingw73_32 to mingw73_64: 20 | REM C:\Qt\Qt5.12.12\5.12.12\mingw73_64\bin\qtenv2.bat 21 | REM cd C:\Users\mm\Programming\Cpp\build-CockHeroine-Desktop_Qt_5_12_12_MinGW_64_bit-Release\release 22 | REM windeployqt CockHeroine.exe 23 | 24 | C:\Qt\Qt5.12.12\5.12.12\mingw73_32\bin\qtenv2.bat 25 | cd C:\Users\mm\Programming\Cpp\build-CockHeroine-Desktop_Qt_5_12_12_MinGW_32_bit-Release\release 26 | windeployqt CockHeroine.exe 27 | -------------------------------------------------------------------------------- /release64.bat: -------------------------------------------------------------------------------- 1 | echo Set up the environment for building Qt things 2 | set PATH=C:\Qt\Qt5.12.12\5.12.12\mingw73_64\bin;C:/Qt/Qt5.12.12/Tools/mingw730_64\bin;%PATH% 3 | echo Go to where we want to build 4 | mkdir C:\Users\mm\Programming\Cpp\cock-heroine-release 5 | cd /d C:\Users\mm\Programming\Cpp\cock-heroine-release 6 | echo Build the project 7 | qmake ..\cock-heroine\CockHeroine.pro 8 | make release 9 | cd release 10 | echo remove the bits we don't need 11 | rm *.o *.cpp *.h 12 | echo pull in the libraries needed 13 | windeployqt CockHeroine.exe 14 | pause --------------------------------------------------------------------------------