1 /*
2 * Copyright (C) 2002 - David W. Durham
3 *
4 * This file is part of ReZound, an audio editing application.
5 *
6 * ReZound is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 2 of the License,
9 * or (at your option) any later version.
10 *
11 * ReZound is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21 #include "main_controls.h"
22
23 #include <exception>
24
25 #include "settings.h"
26 #include "CSound.h"
27 #include "AStatusComm.h"
28 #include "ASoundFileManager.h"
29 #include "CSoundPlayerChannel.h"
30 #include "CLoadedSound.h"
31 #include "AAction.h"
32 #include "AFrontendHooks.h"
33
34
35
36
37 // -----------------------------------------------------------------------------------
38 // --- File Operations ---------------------------------------------------------------
39 // -----------------------------------------------------------------------------------
40
openSound(ASoundFileManager * soundFileManager,const string filename)41 bool openSound(ASoundFileManager *soundFileManager,const string filename)
42 {
43 try
44 {
45 return soundFileManager->open(filename);
46 }
47 catch(exception &e)
48 {
49 Error(e.what());
50 return false;
51 }
52 }
53
newSound(ASoundFileManager * soundFileManager)54 void newSound(ASoundFileManager *soundFileManager)
55 {
56 try
57 {
58 soundFileManager->createNew();
59 }
60 catch(exception &e)
61 {
62 Error(e.what());
63 }
64 }
65
closeSound(ASoundFileManager * soundFileManager)66 void closeSound(ASoundFileManager *soundFileManager)
67 {
68 try
69 {
70 soundFileManager->close(ASoundFileManager::ctSaveYesNoCancel);
71 }
72 catch(exception &e)
73 {
74 Error(e.what());
75 }
76 }
77
saveSound(ASoundFileManager * soundFileManager)78 void saveSound(ASoundFileManager *soundFileManager)
79 {
80 try
81 {
82 soundFileManager->save();
83 }
84 catch(exception &e)
85 {
86 Error(e.what());
87 }
88 }
89
saveAsSound(ASoundFileManager * soundFileManager)90 void saveAsSound(ASoundFileManager *soundFileManager)
91 {
92 try
93 {
94 soundFileManager->saveAs();
95 }
96 catch(exception &e)
97 {
98 Error(e.what());
99 }
100 }
101
revertSound(ASoundFileManager * soundFileManager)102 void revertSound(ASoundFileManager *soundFileManager)
103 {
104 try
105 {
106 soundFileManager->revert();
107 }
108 catch(exception &e)
109 {
110 Error(e.what());
111 }
112 }
113
recordSound(ASoundFileManager * soundFileManager)114 void recordSound(ASoundFileManager *soundFileManager)
115 {
116 try
117 {
118 soundFileManager->recordToNew();
119 }
120 catch(exception &e)
121 {
122 Error(e.what());
123 }
124 }
125
recordMacro()126 void recordMacro()
127 {
128 try
129 {
130 if(AActionFactory::macroRecorder.isRecording())
131 {
132 AActionFactory::macroRecorder.stopRecording();
133 }
134 else
135 {
136 string macroName; // initialize to something that doesn't exist "untitled1"..
137 if(gFrontendHooks->showRecordMacroDialog(macroName))
138 AActionFactory::macroRecorder.startRecording(gUserMacroStore,macroName);
139 }
140 }
141 catch(exception &e)
142 {
143 Error(e.what());
144 }
145 }
146
147 //---------------------------------------------------------------------------
148
exitReZound(ASoundFileManager * soundFileManager)149 const bool exitReZound(ASoundFileManager *soundFileManager)
150 {
151 try
152 {
153 // make sure the user wants to quit
154 // >1 instead of >0 because it's easy to open the 1 (unsaved) file again, rather than several (because if the one is unsaved, you get a chance to cancel)
155 if(soundFileManager->getOpenedCount()>1)
156 {
157 if(Question(_("Are you sure you want to quit?"),yesnoQues)!=yesAns)
158 return false;
159 }
160
161 // stop recording a macro if there is one
162 // we do this prior to closing files since they could cancel the quit while files are closing
163 AActionFactory::macroRecorder.stopRecording();
164
165 // close all opened files
166 while(soundFileManager->getActive()!=NULL)
167 {
168 if(!soundFileManager->close(ASoundFileManager::ctSaveYesNoStop))
169 throw EStopClosing();
170 }
171 return true;
172 }
173 catch(EStopClosing &e)
174 {
175 return false;
176 }
177 catch(exception &e)
178 {
179 Error(e.what());
180 return false;
181 }
182 }
183
184
185
186
187 // -----------------------------------------------------------------------------------
188 // --- Play Control Operations -------------------------------------------------------
189 // -----------------------------------------------------------------------------------
190
play(ASoundFileManager * soundFileManager,CSoundPlayerChannel::LoopTypes loopType,bool selectionOnly)191 void play(ASoundFileManager *soundFileManager,CSoundPlayerChannel::LoopTypes loopType,bool selectionOnly)
192 {
193 try
194 {
195 CLoadedSound *loaded=soundFileManager->getActive();
196 if(loaded!=NULL)
197 loaded->channel->play(0,loopType,selectionOnly);
198 }
199 catch(exception &e)
200 {
201 Error(e.what());
202 }
203 }
204
play(ASoundFileManager * soundFileManager,sample_pos_t position)205 void play(ASoundFileManager *soundFileManager,sample_pos_t position)
206 {
207 try
208 {
209 CLoadedSound *loaded=soundFileManager->getActive();
210 if(loaded!=NULL)
211 loaded->channel->play(position);
212 }
213 catch(exception &e)
214 {
215 Error(e.what());
216 }
217 }
218
pause(ASoundFileManager * soundFileManager)219 void pause(ASoundFileManager *soundFileManager)
220 {
221 try
222 {
223 CLoadedSound *loaded=soundFileManager->getActive();
224 if(loaded!=NULL)
225 loaded->channel->pause();
226 }
227 catch(exception &e)
228 {
229 Error(e.what());
230 }
231 }
232
stop(ASoundFileManager * soundFileManager)233 void stop(ASoundFileManager *soundFileManager)
234 {
235 try
236 {
237 CLoadedSound *loaded=soundFileManager->getActive();
238 if(loaded!=NULL)
239 loaded->channel->stop();
240 }
241 catch(exception &e)
242 {
243 Error(e.what());
244 }
245 }
246
jumpToBeginning(ASoundFileManager * soundFileManager)247 void jumpToBeginning(ASoundFileManager *soundFileManager)
248 {
249 try
250 {
251 CLoadedSound *loaded=soundFileManager->getActive();
252 if(loaded!=NULL && loaded->channel->isPlaying())
253 {
254 CSoundPlayerChannel *channel=loaded->channel;
255 // this doesn't however, clear the data that's already been queued for playing
256 channel->setPosition(0);
257 }
258 }
259 catch(exception &e)
260 {
261 Error(e.what());
262 }
263 }
264
jumpToStartPosition(ASoundFileManager * soundFileManager)265 void jumpToStartPosition(ASoundFileManager *soundFileManager)
266 {
267 try
268 {
269 CLoadedSound *loaded=soundFileManager->getActive();
270 if(loaded!=NULL && loaded->channel->isPlaying())
271 {
272 CSoundPlayerChannel *channel=loaded->channel;
273 channel->setPosition(channel->getStartPosition());
274 }
275 }
276 catch(exception &e)
277 {
278 Error(e.what());
279 }
280 }
281
jumpToPreviousCue(ASoundFileManager * soundFileManager)282 void jumpToPreviousCue(ASoundFileManager *soundFileManager)
283 {
284 try
285 {
286 CLoadedSound *loaded=soundFileManager->getActive();
287 if(loaded!=NULL && loaded->channel->isPlaying())
288 {
289 CSoundPlayerChannel *channel=loaded->channel;
290
291 // find the previous cue relative to the current play position
292 const sample_pos_t playPosition=channel->getPosition();
293 CSound *sound=loaded->sound;
294
295 sample_pos_t smallestDistance=MAX_LENGTH;
296 size_t previousCueIndex=0; // zero means not found yet, and we always store the index+1 when we do find one
297
298 // For each cue whose time is in front of the current play position, find the nearest one.
299 // But since the play position is always advancing, I also ignore cues that aren't more than
300 // 0.4 of a second from the play position so that a user can click faster than 0.4 of a second
301 // to go back, back, back on the cues. (EXCEPT, if it's paused, forget about the 0.4 minimum
302 // requirement)
303 const size_t cueCount=sound->getCueCount();
304 for(size_t t=0;t<cueCount;t++)
305 {
306 const sample_pos_t cueTime=sound->getCueTime(t);
307 if(cueTime<playPosition)
308 {
309 const sample_pos_t distance=playPosition-cueTime;
310 const sample_fpos_t distanceInTime=((sample_fpos_t)distance)/(sample_fpos_t)sound->getSampleRate();
311 if(previousCueIndex==0 || (distance<smallestDistance && (distanceInTime>0.4 || channel->isPaused())))
312 {
313 smallestDistance=(distanceInTime>0.4 || channel->isPaused()) ? distance : smallestDistance;
314 previousCueIndex=t+1;
315 }
316 }
317 }
318
319 if(previousCueIndex!=0)
320 {
321 previousCueIndex--;
322 channel->setPosition(sound->getCueTime(previousCueIndex));
323 }
324 else
325 gStatusComm->beep();
326 }
327 }
328 catch(exception &e)
329 {
330 Error(e.what());
331 }
332 }
333
jumpToNextCue(ASoundFileManager * soundFileManager)334 void jumpToNextCue(ASoundFileManager *soundFileManager)
335 {
336 try
337 {
338 CLoadedSound *loaded=soundFileManager->getActive();
339 if(loaded!=NULL && loaded->channel->isPlaying())
340 {
341 CSoundPlayerChannel *channel=loaded->channel;
342
343 // find the previous cue relative to the current play position
344 const sample_pos_t playPosition=channel->getPosition();
345 CSound *sound=loaded->sound;
346
347 sample_pos_t smallestDistance=MAX_LENGTH;
348 size_t nextCueIndex=0; // zero means not found yet, and we always store the index+1 when we do find one
349
350 // For each cue whose time is beyond of the current play position, find the nearest one.
351 const size_t cueCount=sound->getCueCount();
352 for(size_t t=0;t<cueCount;t++)
353 {
354 const sample_pos_t cueTime=sound->getCueTime(t);
355 if(cueTime>playPosition)
356 {
357 const sample_pos_t distance=cueTime-playPosition;
358 if(nextCueIndex==0 || distance<smallestDistance)
359 {
360 smallestDistance=distance;
361 nextCueIndex=t+1;
362 }
363 }
364 }
365
366 if(nextCueIndex!=0)
367 {
368 nextCueIndex--;
369 channel->setPosition(sound->getCueTime(nextCueIndex));
370 }
371 else
372 gStatusComm->beep();
373 }
374 }
375 catch(exception &e)
376 {
377 Error(e.what());
378 }
379 }
380
381
382
383
384
385
386 // -----------------------------------------------------------------------------------
387 // --- Undo Operations ---------------------------------------------------------------
388 // -----------------------------------------------------------------------------------
389
undo(ASoundFileManager * soundFileManager)390 void undo(ASoundFileManager *soundFileManager)
391 {
392 try
393 {
394 CLoadedSound *s=soundFileManager->getActive();
395 if(s!=NULL)
396 {
397 if(!s->actions.empty())
398 {
399 AAction *a=s->actions.top();
400 s->actions.pop();
401
402 // if we're recording a macro, then un-record this action being undone
403 try {
404 if(AActionFactory::macroRecorder.isRecording())
405 AActionFactory::macroRecorder.popAction(a->getFactory()->getName());
406 } catch(...) { }
407
408 a->undoAction(s->channel);
409
410 // restore the frontend position information as part of undoing the action
411 soundFileManager->setPositionalInfo(a->positionalInfo);
412
413 delete a; // ??? what ever final logic is implemented for undo, it should probably push it onto a redo stack
414
415 soundFileManager->updateAfterEdit(NULL,true);
416 }
417 else
418 gStatusComm->beep();
419 }
420 else
421 gStatusComm->beep();
422 }
423 catch(exception &e)
424 {
425 Error(e.what());
426 }
427 }
428
clearUndoHistory(ASoundFileManager * soundFileManager)429 void clearUndoHistory(ASoundFileManager *soundFileManager)
430 {
431 try
432 {
433 CLoadedSound *s=soundFileManager->getActive();
434 if(s!=NULL)
435 {
436 s->clearUndoHistory();
437 soundFileManager->updateAfterEdit();
438 }
439 else
440 gStatusComm->beep();
441 }
442 catch(exception &e)
443 {
444 Error(e.what());
445 }
446 }
447
448
449
450