1 #include "CMacroRecorder.h"
2
3 #include "settings.h"
4 #include "AStatusComm.h"
5 #include "CActionParameters.h"
6 #include "AFrontendHooks.h"
7 #include "CLoadedSound.h"
8 #include "CSoundPlayerChannel.h"
9 #include "CSound.h"
10 #include "AAction.h"
11 #include "ASoundClipboard.h"
12
13 #include <CNestedDataFile/CNestedDataFile.h>
14
CMacroRecorder()15 CMacroRecorder::CMacroRecorder() :
16 recording(false),
17 file(NULL)
18 {
19 }
20
~CMacroRecorder()21 CMacroRecorder::~CMacroRecorder()
22 {
23 // would like to handle an unstopped recording, but it's possible that the CNestedDataFile we know about has been closed..
24 }
25
26
isRecording() const27 bool CMacroRecorder::isRecording() const
28 {
29 return recording;
30 }
31
startRecording(CNestedDataFile * _file,const string _macroName)32 void CMacroRecorder::startRecording(CNestedDataFile *_file,const string _macroName)
33 {
34 if(recording)
35 throw runtime_error(string(__func__)+" -- already recording");
36
37 macroName=_macroName;
38 file=_file;
39
40 key=macroName;
41
42 if(file->keyExists(key))
43 {
44 if(Question(_("A macro already exists with the name: ")+macroName+"\n\n"+_("Do you want to overwrite it?"),yesnoQues)==yesAns)
45 removeMacro(file,macroName);
46 else
47 return;
48 }
49
50 actionCount=0;
51 activeSoundIndex=-1; // indicates to use the active sound
52 file->setValue<unsigned>(key DOT "actionCount",actionCount);
53
54 recording=true;
55 }
56
stopRecording()57 void CMacroRecorder::stopRecording()
58 {
59 if(recording)
60 {
61 recording=false;
62
63 // finish up
64 if(actionCount<=0)
65 {
66 Message(_("Nothing was recorded in the macro: ")+macroName);
67 file->removeKey(key);
68 }
69 else
70 {
71 // now add the macro name to the selectable list of macro's to play
72 vector<string> macroNames=file->getValue<vector<string> >("MacroNames");
73 macroNames.push_back(macroName);
74
75 // sort the macro names
76 sort(macroNames.begin(),macroNames.end());
77
78 // remove duplicates
79 unique(macroNames.begin(),macroNames.end());
80
81 // write new macro name list to file
82 file->setValue<vector<string> >("MacroNames",macroNames);
83
84 }
85
86 file->save();
87 }
88 }
89
pushAction(const string actionName,const CActionParameters * actionParameters,CLoadedSound * loadedSound)90 bool CMacroRecorder::pushAction(const string actionName,const CActionParameters *actionParameters,CLoadedSound *loadedSound)
91 {
92 if(!recording)
93 throw runtime_error(string(__func__)+" -- not recording");
94
95 if(gRegisteredActionFactories.find(actionName)==gRegisteredActionFactories.end())
96 {
97 Message(_("The action just performed will not be recorded in your macro.")+string("\n\n")+actionName);
98 return true;
99 }
100
101 const AActionFactory *actionFactory=gRegisteredActionFactories[actionName];
102
103 // ask the user how they want to handle certain things for this action when the macro is played back
104 AFrontendHooks::MacroActionParameters macroActionParameters;
105 if(!gFrontendHooks->showMacroActionParamsDialog(actionFactory,macroActionParameters,loadedSound))
106 return false;
107
108 const string actionKey=key DOT "action"+istring(actionCount,3,true);
109
110 file->setValue<string>(actionKey DOT "actionName",actionName);
111 if(activeSoundIndex>=0)
112 file->setValue<size_t>(actionKey DOT "activeSoundIndex",(size_t)activeSoundIndex);
113 activeSoundIndex=-1; // and from now out use activeSound (until next pushActiveSoundChange) call
114 file->setValue<bool>(actionKey DOT "askToPromptForActionParametersAtPlayback",macroActionParameters.askToPromptForActionParametersAtPlayback);
115 file->setValue<string>(actionKey DOT "selectedClipboardDescription",AAction::clipboards[gWhichClipboard]->getDescription());
116 file->setValue<bool>(actionKey DOT "selectionPositionsAreApplicable",actionFactory->selectionPositionsAreApplicable);
117 if(loadedSound && actionFactory->selectionPositionsAreApplicable)
118 {
119 // store info for positioning the start and stop positions at playback
120 file->setValue<sample_pos_t>(actionKey DOT "positioning" DOT "startPosition",loadedSound->channel->getStartPosition());
121 file->setValue<sample_pos_t>(actionKey DOT "positioning" DOT "stopPosition",loadedSound->channel->getStopPosition());
122 file->setValue<sample_pos_t>(actionKey DOT "positioning" DOT "audioLength",loadedSound->sound->getLength());
123 file->setValue<unsigned>(actionKey DOT "positioning" DOT "startPosPositioning",macroActionParameters.startPosPositioning);
124 file->setValue<unsigned>(actionKey DOT "positioning" DOT "stopPosPositioning",macroActionParameters.stopPosPositioning);
125 file->setValue<string>(actionKey DOT "positioning" DOT "startPosCueName",macroActionParameters.startPosCueName);
126 file->setValue<string>(actionKey DOT "positioning" DOT "stopPosCueName",macroActionParameters.stopPosCueName);
127
128 // store info about crossfade
129 file->setValue<unsigned>(actionKey DOT "positioning" DOT "crossfadeEdges",gCrossfadeEdges);
130 file->setValue<float>(actionKey DOT "positioning" DOT "crossfadeStartTime",gCrossfadeStartTime);
131 file->setValue<float>(actionKey DOT "positioning" DOT "crossfadeStopTime",gCrossfadeStopTime);
132 file->setValue<unsigned>(actionKey DOT "positioning" DOT "crossfadeFadeMethod",gCrossfadeFadeMethod);
133 }
134
135 actionParameters->writeToFile(file,actionKey DOT "parameters");
136
137 file->setValue<unsigned>(key DOT "actionCount",++actionCount);
138 return true;
139 }
140
pushActiveSoundChange(size_t index)141 void CMacroRecorder::pushActiveSoundChange(size_t index)
142 {
143 activeSoundIndex=(int)index;
144 }
145
popAction(const string actionName)146 void CMacroRecorder::popAction(const string actionName)
147 {
148 if(!recording)
149 throw runtime_error(string(__func__)+" -- not recording");
150
151 if(gRegisteredActionFactories.find(actionName)==gRegisteredActionFactories.end())
152 return; // wouldn't have been recorded
153
154 if(actionCount>0)
155 {
156 const string actionKey=key DOT "action"+istring(actionCount-1,3,true);
157
158 if(file->getValue<string>(actionKey DOT "actionName")==actionName)
159 {
160 // forget the last action we recorded
161 actionCount--;
162 file->removeKey(actionKey);
163 file->setValue<unsigned>(key DOT "actionCount",actionCount);
164 }
165 // else otherwise, we didn't add this action for whatever reason (probably wasn't in the action registry)
166 }
167 }
168
removeMacro(CNestedDataFile * file,const string macroName)169 void CMacroRecorder::removeMacro(CNestedDataFile *file,const string macroName)
170 {
171 // get the current list of macro names
172 vector<string> macroNames=file->getValue<vector<string> >("MacroNames");
173
174 // remove the name from the list
175 for(vector<string>::iterator i=macroNames.begin();i!=macroNames.end();i++)
176 {
177 if((*i)==macroName)
178 {
179 i=macroNames.erase(i);
180 if(i==macroNames.end()) // still not quite sure about the best way to iterate over a vector while removing items
181 break;
182 }
183 }
184
185 // write new macro name list to file
186 file->setValue<vector<string> >("MacroNames",macroNames);
187
188 // now remove the macro definition from the file (??? if I could rename keys, then we could always write to a temporary name and name it for real afterwards)
189 file->removeKey(macroName);
190 }
191