1 /* -----------------------------------------------------------------------------
2 *
3 * Giada - Your Hardcore Loopmachine
4 *
5 * -----------------------------------------------------------------------------
6 *
7 * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
8 *
9 * This file is part of Giada - Your Hardcore Loopmachine.
10 *
11 * Giada - Your Hardcore Loopmachine is free software: you can
12 * redistribute it and/or modify it under the terms of the GNU General
13 * Public License as published by the Free Software Foundation, either
14 * version 3 of the License, or (at your option) any later version.
15 *
16 * Giada - Your Hardcore Loopmachine is distributed in the hope that it
17 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
18 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 * See the GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with Giada - Your Hardcore Loopmachine. If not, see
23 * <http://www.gnu.org/licenses/>.
24 *
25 * -------------------------------------------------------------------------- */
26
27
28 #include <functional>
29 #include <cmath>
30 #include <cassert>
31 #include <FL/Fl.H>
32 #include "gui/dialogs/mainWindow.h"
33 #include "gui/dialogs/sampleEditor.h"
34 #include "gui/dialogs/warnings.h"
35 #include "gui/elems/basics/input.h"
36 #include "gui/elems/basics/dial.h"
37 #include "gui/elems/sampleEditor/waveTools.h"
38 #include "gui/elems/sampleEditor/volumeTool.h"
39 #include "gui/elems/sampleEditor/boostTool.h"
40 #include "gui/elems/sampleEditor/panTool.h"
41 #include "gui/elems/sampleEditor/pitchTool.h"
42 #include "gui/elems/sampleEditor/rangeTool.h"
43 #include "gui/elems/sampleEditor/waveform.h"
44 #include "gui/elems/mainWindow/keyboard/keyboard.h"
45 #include "gui/elems/mainWindow/keyboard/channel.h"
46 #include "gui/elems/mainWindow/keyboard/sampleChannel.h"
47 #include "gui/elems/mainWindow/keyboard/channelButton.h"
48 #include "utils/gui.h"
49 #include "utils/fs.h"
50 #include "utils/log.h"
51 #include "core/model/model.h"
52 #include "core/kernelAudio.h"
53 #include "core/mixerHandler.h"
54 #include "core/mixer.h"
55 #include "core/clock.h"
56 #include "core/plugins/pluginHost.h"
57 #include "core/conf.h"
58 #include "core/wave.h"
59 #include "core/recorder.h"
60 #include "core/recManager.h"
61 #include "core/plugins/plugin.h"
62 #include "core/waveManager.h"
63 #include "main.h"
64 #include "channel.h"
65
66
67 extern giada::v::gdMainWindow* G_MainWin;
68
69
70 namespace giada {
71 namespace c {
72 namespace channel
73 {
74 namespace
75 {
printLoadError_(int res)76 void printLoadError_(int res)
77 {
78 if (res == G_RES_ERR_WRONG_DATA)
79 v::gdAlert("Multichannel samples not supported.");
80 else if (res == G_RES_ERR_IO)
81 v::gdAlert("Unable to read this sample.");
82 else if (res == G_RES_ERR_PATH_TOO_LONG)
83 v::gdAlert("File path too long.");
84 else if (res == G_RES_ERR_NO_DATA)
85 v::gdAlert("No file specified.");
86 }
87 } // {anonymous}
88
89
90 /* -------------------------------------------------------------------------- */
91 /* -------------------------------------------------------------------------- */
92 /* -------------------------------------------------------------------------- */
93
94
SampleData(const m::SamplePlayer & s,const m::AudioReceiver & a)95 SampleData::SampleData(const m::SamplePlayer& s, const m::AudioReceiver& a)
96 : waveId (s.getWaveId())
97 , mode (s.state->mode.load())
98 , isLoop (s.state->isAnyLoopMode())
99 , pitch (s.state->pitch.load())
100 , m_samplePlayer (&s)
101 , m_audioReceiver(&a)
102 {
103 }
104
105
a_getTracker() const106 Frame SampleData::a_getTracker() const { return a_get(m_samplePlayer->state->tracker); }
a_getBegin() const107 Frame SampleData::a_getBegin() const { return a_get(m_samplePlayer->state->begin); }
a_getEnd() const108 Frame SampleData::a_getEnd() const { return a_get(m_samplePlayer->state->end); }
a_getInputMonitor() const109 bool SampleData::a_getInputMonitor() const { return a_get(m_audioReceiver->state->inputMonitor); }
a_getOverdubProtection() const110 bool SampleData::a_getOverdubProtection() const { return a_get(m_audioReceiver->state->overdubProtection); }
111
112
113 /* -------------------------------------------------------------------------- */
114
115
MidiData(const m::MidiSender & m)116 MidiData::MidiData(const m::MidiSender& m)
117 : m_midiSender(&m)
118 {
119 }
120
a_isOutputEnabled() const121 bool MidiData::a_isOutputEnabled() const { return a_get(m_midiSender->state->enabled); }
a_getFilter() const122 int MidiData::a_getFilter() const { return a_get(m_midiSender->state->filter); }
123
124
125 /* -------------------------------------------------------------------------- */
126
127
Data(const m::Channel & c)128 Data::Data(const m::Channel& c)
129 : id (c.id)
130 , columnId (c.getColumnId())
131 #ifdef WITH_VST
132 , pluginIds (c.pluginIds)
133 #endif
134 , type (c.getType())
135 , height (c.state->height)
136 , name (c.state->name)
137 , volume (c.state->volume.load())
138 , pan (c.state->pan.load())
139 , key (c.state->key.load())
140 , hasActions (c.state->hasActions)
141 , m_channel (c)
142 {
143 if (c.getType() == ChannelType::SAMPLE)
144 sample = std::make_optional<SampleData>(*c.samplePlayer, *c.audioReceiver);
145 else
146 if (c.getType() == ChannelType::MIDI)
147 midi = std::make_optional<MidiData>(*c.midiSender);
148 }
149
150
a_getSolo() const151 bool Data::a_getSolo() const { return a_get(m_channel.state->solo); }
a_getMute() const152 bool Data::a_getMute() const { return a_get(m_channel.state->mute); }
a_getPlayStatus() const153 ChannelStatus Data::a_getPlayStatus() const { return a_get(m_channel.state->playStatus); }
a_getRecStatus() const154 ChannelStatus Data::a_getRecStatus() const { return a_get(m_channel.state->recStatus); }
a_getReadActions() const155 bool Data::a_getReadActions() const { return a_get(m_channel.state->readActions); }
a_isArmed() const156 bool Data::a_isArmed() const { return a_get(m_channel.state->armed); }
a_isRecordingInput() const157 bool Data::a_isRecordingInput() const { return m::recManager::isRecordingInput(); }
a_isRecordingAction() const158 bool Data::a_isRecordingAction() const { return m::recManager::isRecordingAction(); }
159
160
161 /* -------------------------------------------------------------------------- */
162 /* -------------------------------------------------------------------------- */
163 /* -------------------------------------------------------------------------- */
164
165
getData(ID channelId)166 Data getData(ID channelId)
167 {
168 namespace mm = m::model;
169
170 mm::ChannelsLock cl(mm::channels);
171 return Data(mm::get(mm::channels, channelId));
172 }
173
174
getChannels()175 std::vector<Data> getChannels()
176 {
177 namespace mm = m::model;
178 mm::ChannelsLock cl(mm::channels);
179
180 std::vector<Data> out;
181 for (const m::Channel* ch : mm::channels)
182 if (!ch->isInternal())
183 out.push_back(Data(*ch));
184
185 return out;
186 }
187
188
189 /* -------------------------------------------------------------------------- */
190
191
loadChannel(ID channelId,const std::string & fname)192 int loadChannel(ID channelId, const std::string& fname)
193 {
194 /* Save the patch and take the last browser's dir in order to re-use it the
195 next time. */
196
197 m::conf::conf.samplePath = u::fs::dirname(fname);
198
199 int res = m::mh::loadChannel(channelId, fname);
200 if (res != G_RES_OK)
201 printLoadError_(res);
202
203 return res;
204 }
205
206
207 /* -------------------------------------------------------------------------- */
208
209
addChannel(ID columnId,ChannelType type)210 void addChannel(ID columnId, ChannelType type)
211 {
212 m::mh::addChannel(type, columnId);
213 }
214
215
216 /* -------------------------------------------------------------------------- */
217
218
addAndLoadChannel(ID columnId,const std::string & fpath)219 void addAndLoadChannel(ID columnId, const std::string& fpath)
220 {
221 int res = m::mh::addAndLoadChannel(columnId, fpath);
222 if (res != G_RES_OK)
223 printLoadError_(res);
224 }
225
226
addAndLoadChannels(ID columnId,const std::vector<std::string> & fpaths)227 void addAndLoadChannels(ID columnId, const std::vector<std::string>& fpaths)
228 {
229 if (fpaths.size() == 1)
230 return addAndLoadChannel(columnId, fpaths[0]);
231
232 bool errors = false;
233 for (const std::string& f : fpaths)
234 if (m::mh::addAndLoadChannel(columnId, f) != G_RES_OK)
235 errors = true;
236
237 if (errors)
238 v::gdAlert("Some files weren't loaded sucessfully.");
239 }
240
241
242 /* -------------------------------------------------------------------------- */
243
244
deleteChannel(ID channelId)245 void deleteChannel(ID channelId)
246 {
247 if (!v::gdConfirmWin("Warning", "Delete channel: are you sure?"))
248 return;
249 u::gui::closeAllSubwindows();
250 m::recorder::clearChannel(channelId);
251 m::mh::deleteChannel(channelId);
252 }
253
254
255 /* -------------------------------------------------------------------------- */
256
257
freeChannel(ID channelId)258 void freeChannel(ID channelId)
259 {
260 if (!v::gdConfirmWin("Warning", "Free channel: are you sure?"))
261 return;
262 u::gui::closeAllSubwindows();
263 m::recorder::clearChannel(channelId);
264 m::mh::freeChannel(channelId);
265 }
266
267
268 /* -------------------------------------------------------------------------- */
269
270
setInputMonitor(ID channelId,bool value)271 void setInputMonitor(ID channelId, bool value)
272 {
273 m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
274 {
275 c.audioReceiver->state->inputMonitor.store(value);
276 });
277 }
278
279
280 /* -------------------------------------------------------------------------- */
281
282
setOverdubProtection(ID channelId,bool value)283 void setOverdubProtection(ID channelId, bool value)
284 {
285 m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
286 {
287 c.audioReceiver->state->overdubProtection.store(value);
288 if (value == true && c.state->armed.load() == true)
289 c.state->armed.store(false);
290 });
291 }
292
293
294 /* -------------------------------------------------------------------------- */
295
296
cloneChannel(ID channelId)297 void cloneChannel(ID channelId)
298 {
299 m::mh::cloneChannel(channelId);
300 }
301
302
303 /* -------------------------------------------------------------------------- */
304
305
setSamplePlayerMode(ID channelId,SamplePlayerMode m)306 void setSamplePlayerMode(ID channelId, SamplePlayerMode m)
307 {
308 m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
309 {
310 c.samplePlayer->state->mode.store(m);
311 });
312
313 /* TODO - brutal rebuild! Just rebuild the specific channel instead */
314 G_MainWin->keyboard->rebuild();
315
316 u::gui::refreshActionEditor();
317 }
318
319
320 /* -------------------------------------------------------------------------- */
321
322
setHeight(ID channelId,Pixel p)323 void setHeight(ID channelId, Pixel p)
324 {
325 m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
326 {
327 c.state->height = p;
328 });
329 }
330
331
332 /* -------------------------------------------------------------------------- */
333
334
setName(ID channelId,const std::string & name)335 void setName(ID channelId, const std::string& name)
336 {
337 m::mh::renameChannel(channelId, name);
338 }
339 }}} // giada::c::channel::
340