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