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