1 /*!
2  * @file midilfo.h
3  * @brief Member definitions for the MidiLfo MIDI worker class.
4  *
5  *
6  *      Copyright 2009 - 2017 <qmidiarp-devel@lists.sourceforge.net>
7  *
8  *      This program is free software; you can redistribute it and/or modify
9  *      it under the terms of the GNU General Public License as published by
10  *      the Free Software Foundation; either version 2 of the License, or
11  *      (at your option) any later version.
12  *
13  *      This program is distributed in the hope that it will be useful,
14  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *      GNU General Public License for more details.
17  *
18  *      You should have received a copy of the GNU General Public License
19  *      along with this program; if not, write to the Free Software
20  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21  *      MA 02110-1301, USA.
22  *
23  */
24 
25 #ifndef MIDILFO_H
26 #define MIDILFO_H
27 
28 #include <vector>
29 #include "midiworker.h"
30 
31 
32 /*! @brief MIDI worker class for the LFO Module. Implements a sequencer
33  * for controller data as a QObject.
34  *
35  * The parameters of MidiLfo are controlled by the LfoWidget class.
36  * The backend driver thread calls the Engine::echoCallback(), which will
37  * query each module, in this case via
38  * the MidiLfo::getNextFrame() method. MidiLfo will fill a frame from
39  * its internal MidiLfo::data buffer as a function of the position of
40  * the driver's transport. MidiLfo::frame is then accessed by Engine. It
41  * has size 1 except for resolution higher than 16th notes.
42  * The MidiLfo::data buffer is populated by the getData() function
43  * at each modification done via the LfoWidget. It can consist of
44  * a classic waveform calculation or a hand-drawn waveform. In all cases
45  * the waveform has resolution, offset and size attributes and single
46  * points can be tagged as muted, which will avoid data output at the
47  * corresponding position.
48  */
49 class MidiLfo : public MidiWorker  {
50 
51   private:
52     int lastMouseLoc;   /*!< The X location of the last modification of the wave, used for interpolation*/
53     int lastMouseY;     /*!< The Y location at the last modification of the wave, used for interpolation*/
54     int recValue;
55     int lastSampleValue;
56 /*! @brief  recalculates the MidiLfo::customWave as a function
57  * of a new offset value.
58  *
59  * It is called by MidiLfo::updateOffset() in case a custom wave is active.
60  * @param cwoffs New offset value
61  */
62     void updateCustomWaveOffset(int cwoffs);
63 
64   public:
65     bool recordMode, isRecording;
66     bool lastMute;              /**< Contains the mute state of the last waveForm point modified by mouse click*/
67     int old_res;
68     int freq, amp, offs;
69     int size;       /*!< Size of the waveform in quarter notes */
70     int res;        /*!< Resolution of the waveform in ticks per quarter note */
71     int frameSize;  /*!< Current size of a vector returned by MidiLfo::getNextFrame() */
72     int maxNPoints;        /*!< Maximum number of steps that have been used in the session */
73     int waveFormIndex;          /*!< Index of the waveform to produce
74                                     @par 0: Sine
75                                     @par 1: Sawtooth Up
76                                     @par 2: Triangle
77                                     @par 3: Sawtooth Down
78                                     @par 4: Square
79                                     @par 5: Use Custom Wave */
80     int cwmin;                  /*!< The minimum of MidiLfo::customWave */
81     std::vector<Sample> customWave; /*!< Vector of Sample points holding the custom drawn wave */
82     std::vector<bool> muteMask;     /*!< Vector of booleans with mute state information for each wave point */
83     std::vector<Sample> frame; /*!< Vector of Sample points holding the current frame for transfer */
84     std::vector<Sample> data;
85 
86   public:
87     MidiLfo();
~MidiLfo()88     virtual ~MidiLfo() {}
89     void updateWaveForm(int val);
90     void updateFrequency(int);
91     void updateAmplitude(int);
92     void updateOffset(int);
93     void updateResolution(int);
94     void updateSize(int);
95     void updateLoop(int);
96     void record(int value);
97     void setRecordMode(bool on);
98 /*! @brief  Called by LfoWidget::mouseEvent()
99  */
100     int mouseEvent(double mouseX, double mouseY, int buttons, int pressed);
101 /*!
102 * @brief  determines the minimum of the current waveform and
103 * sets the LfoWidget::offset slider accordingly.
104 *
105 * It also sets MidiLfo::cwmin. When a new waveform is drawn, its minimum
106 * offset from 0 changes and the offset controller has to be adapted in range.
107 *
108 */
109     void newCustomOffset();
110 
111 /*! @brief  sets the (controller) value of one point of the
112  * MidiLfo::customWave array. It is used for handling drawing functionality.
113  *
114  * It is called by the mouseEvent() function.
115  * The normalized mouse coordinates are scaled to the waveform size and
116  * resolution and to the controller range (0 ... 127). The function
117  * interpolates potentially missing waveform points between two events
118  * if the mouse buttons were not released.
119  *
120  * @returns index in the wave vector that has been set
121  * @param mouseX Normalized horizontal location of the mouse on the
122  * LfoScreen (0.0 ... 1.0)
123  * @param mouseY Normalized verical location of the mouse on the
124  * LfoScreen (0.0 ... 1.0)
125  * @param newpt Set to true if the mouse button was newly clicked before
126  * the move
127  *
128  * @see MidiLfo::toggleMutePoint(), MidiLfo::setMutePoint()
129  */
130     int setCustomWavePoint(double mouseX, double mouseY, bool newpt);
131 /*! @brief  sets the mute state of one point of the
132  * MidiLfo::muteMask array to the given state.
133  *
134  * The method is called when the right mouse button is clicked on the
135  * LfoScreen via the mouseEvent() function.
136  * If calculated waveforms are active, only the MidiLfo::muteMask is
137  * changed. If a custom waveform is active, the Sample.mute status
138  * at the given position is changed as well.
139  *
140  * @returns index in the wave vector that has been set
141  * @param mouseX Normalized Horizontal location of the mouse on the
142  * LfoScreen (0.0 ... 1.0)
143  * @param muted mute state to set for the given position
144  *
145  * @see MidiLfo::toggleMutePoint()
146  */
147     int setMutePoint(double mouseX, bool muted);
148 /*! @brief  recalculates the MidiLfo::customWave as a
149  * function of the current MidiLfo::res and MidiLfo::size values.
150  *
151  * It is called upon every change of MidiLfo::size and MidiLfo::res. It
152  * repeats the current MidiLfo::customWave periodically if the new values
153  * lead to a bigger size data array.
154  */
155     void resizeAll();
156 /*! @brief  copies the current MidiLfo::data array into
157  * MidiLfo::customWave.
158  *
159  * It is called when a waveform modification by the user is attempted
160  * while in calculated waveform mode. (MidiLfo::waveFormIndex 1 ... 4).
161  */
162     void copyToCustom();
163 /*! @brief  flips the MidiLfo::customWave array about its middle value
164  *
165  * It is called by LfoWidget when the vertical flip button is pressed.
166  */
167     void flipWaveVertical();
168 /*! @brief  sets the MidiLfo::framePtr to the given value.
169  *
170  * It is called when the Transport starts.
171  * @param idx Index to which the framePtr is set
172  */
173     void setFramePtr(int idx);
174 /**
175  * @brief  does the actions related to a newly received event.
176  *
177  * It is called by Engine when a new event is received on the MIDI input port.
178 
179  * @param inEv MidiEvent to check and process or not
180  * @param tick The time the event was received in internal ticks
181  * @return True if inEv is in not the input range of the module (event is unmatched)
182  */
183     bool handleEvent(MidiEvent inEv, int tick);
184 /*! @brief  is the main calculator for the data contained
185  * in a waveform.
186  *
187  * It is called upon every change of parameters in LfoWidget or upon
188  * input by mouse clicks on the LfoScreen. It fills the
189  * MidiLfo::data buffer with Sample points, which it either calculates
190  * or which it copies from the MidiLfo::customWave data.
191  *
192  * @param *data reference to an array the waveform is copied to
193  */
194     void getData(std::vector<Sample> *data);
195 /*! @brief fills the MidiLfo::frame with Sample data points taken from
196  * the currently active waveform MidiLfo::data.
197  *
198  * MidiLfo::frame is then accessed by Engine::echoCallback() and sequenced
199  * to the driver backend.
200  *
201  * @param tick current tick
202  */
203     void getNextFrame(int tick);
204 /*! @brief  toggles the mute state of one point of the
205  * MidiLfo::muteMask array.
206  *
207  * The function is called when the right mouse button is clicked on the
208  * LfoScreen.
209  * If calculated waveforms are active, only the MidiLfo::muteMask is
210  * changed. If a custom waveform is active, the Sample.mute status
211  * at the given position is changed as well.
212  *
213  * @param mouseX Normalized Horizontal location of the mouse on the
214  * LfoScreen (0.0 ... 1.0)
215  * @see MidiLfo::setMutePoint
216  */
217     bool toggleMutePoint(double mouseX);
218 
219 /*! @brief Checks if deferred parameter changes are pending and applies
220  * them if so
221  */
222     void applyPendingParChanges();
223 /**
224  * @brief sets MidiLfo::nextTick and MidiLfo::framePtr position
225  * according to the specified tick.
226  *
227  * @param tick The current tick to which the module position should be
228  * aligned.
229  */
230     void setNextTick(int tick);
231 };
232 
233 #endif
234