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 "CMarkQuietAreasAction.h"
22
23 #include "../CActionParameters.h"
24 #include "../unit_conv.h"
25 #include "../DSP/LevelDetector.h"
26
27 #include <utility>
28
CMarkQuietAreasAction(const AActionFactory * factory,const CActionSound * actionSound,const float _quietThreshold,const float _quietTime,const float _unquietTime,const float _detectorWindow,const string _quietBeginCue,const string _quietEndCue)29 CMarkQuietAreasAction::CMarkQuietAreasAction(const AActionFactory *factory,const CActionSound *actionSound,const float _quietThreshold,const float _quietTime,const float _unquietTime,const float _detectorWindow,const string _quietBeginCue,const string _quietEndCue) :
30 AAction(factory,actionSound),
31 quietThreshold(_quietThreshold),
32 quietTime(_quietTime),
33 unquietTime(_unquietTime),
34 detectorWindow(_detectorWindow),
35 quietBeginCue(_quietBeginCue),
36 quietEndCue(_quietEndCue)
37 {
38 }
39
~CMarkQuietAreasAction()40 CMarkQuietAreasAction::~CMarkQuietAreasAction()
41 {
42 }
43
doActionSizeSafe(CActionSound * actionSound,bool prepareForUndo)44 bool CMarkQuietAreasAction::doActionSizeSafe(CActionSound *actionSound,bool prepareForUndo)
45 {
46 const sample_pos_t start=actionSound->start;
47 const sample_pos_t stop=actionSound->stop;
48 const sample_t quietThreshold=dBFS_to_amp(this->quietThreshold);
49 const sample_pos_t quietTime=max((sample_pos_t)1,ms_to_samples(this->quietTime,actionSound->sound->getSampleRate()));
50 const sample_pos_t unquietTime=ms_to_samples(this->unquietTime,actionSound->sound->getSampleRate());
51
52
53 /*
54 * The algorithm follows this state machine:
55 *
56 * >quiet threshold <=quiet threshold
57 * ------------------------------ ---------------------------
58 * | | | |
59 * | | | |
60 * V | V |
61 *
62 * --- <=quiet threshold --- >=quiet time --- >quiet treshold ---
63 * -------> | 0 | -----------------------> | 1 | ------------------> | 2 | --------------------> | 3 |
64 * --- --- add begin cue --- ---
65 * ^ |
66 * | |
67 * | add end cue |
68 * ------------------------------------------------------------------------------------
69 * >=unquiet time
70 */
71
72 // ??? might want to limit the number of cues added by checking for a max before each add
73
74 int state=0;
75 sample_pos_t quietBeginPos=start;
76 sample_pos_t quietEndPos;
77 sample_pos_t quietCounter;
78 sample_pos_t unquietCounter;
79 CDSPRMSLevelDetector levelDetector(max((sample_pos_t)1,ms_to_samples(detectorWindow,actionSound->sound->getSampleRate())));
80 const CRezPoolAccesser src=actionSound->sound->getAudio(0); // ??? which channel to use or both? (prompt the user!)
81 for(sample_pos_t pos=start;pos<=stop;pos++)
82 {
83 const mix_sample_t l=levelDetector.readLevel(src[pos]);
84
85 switch(state)
86 {
87 case 0: // waiting for level to fall below threshold
88 if(l<=quietThreshold)
89 {
90 state=1;
91 quietCounter=0;
92 quietBeginPos=pos;
93 }
94
95 break;
96
97 case 1: // waiting for level that is below threshold to remain that way for quiet time
98 if(l>quietThreshold)
99 { // level wasn't below threshold for long enough
100 state=0;
101 }
102 else if(quietCounter>=quietTime)
103 { // level has now been below the threshold for long enough
104 state=2;
105 actionSound->sound->addCue(quietBeginCue,quietBeginPos,false);
106 }
107 else
108 quietCounter++;
109
110 break;
111
112 case 2: // level has been below the threshold for quiet time samples, now waiting for it to go back above the threshold
113 if(l>quietThreshold)
114 {
115 state=3;
116 unquietCounter=0;
117 quietEndPos=pos;
118 }
119
120 break;
121
122 case 3: // waiting for level to remain above the quiet threshold for unquiet time samples
123 if(l<=quietThreshold)
124 { // level has fallen back below the treshold, start over waiting for unquiet time
125 state=2;
126 }
127 else if(unquietCounter>=unquietTime)
128 { // level has now been above the threshold for unquiet time samples
129 state=0;
130 actionSound->sound->addCue(quietEndCue,quietEndPos,false);
131 }
132 else
133 unquietCounter++;
134
135 break;
136 }
137
138 }
139
140 return true;
141 }
142
canUndo(const CActionSound * actionSound) const143 AAction::CanUndoResults CMarkQuietAreasAction::canUndo(const CActionSound *actionSound) const
144 {
145 return curYes;
146 }
147
undoActionSizeSafe(const CActionSound * actionSound)148 void CMarkQuietAreasAction::undoActionSizeSafe(const CActionSound *actionSound)
149 {
150 // it is not necessary to do anything here because AAction handles restoring all the cues
151 }
152
153
154 // --------------------------------------------------
155
CMarkQuietAreasActionFactory(AActionDialog * normalDialog)156 CMarkQuietAreasActionFactory::CMarkQuietAreasActionFactory(AActionDialog *normalDialog) :
157 AActionFactory(N_("Mark Quiet Areas"),_("Mark Quiet Areas with Cues"),NULL,normalDialog)
158 {
159 }
160
~CMarkQuietAreasActionFactory()161 CMarkQuietAreasActionFactory::~CMarkQuietAreasActionFactory()
162 {
163 }
164
manufactureAction(const CActionSound * actionSound,const CActionParameters * actionParameters) const165 CMarkQuietAreasAction *CMarkQuietAreasActionFactory::manufactureAction(const CActionSound *actionSound,const CActionParameters *actionParameters) const
166 {
167 return new CMarkQuietAreasAction(
168 this,
169 actionSound,
170 actionParameters->getValue<float>("Threshold for Quiet"),
171 actionParameters->getValue<float>("Must Remain Quiet for"),
172 actionParameters->getValue<float>("Must Remain Unquiet for"),
173 actionParameters->getValue<float>("Level Detector Window Time"),
174 actionParameters->getValue<string>("Quiet Begin Cue Name"),
175 actionParameters->getValue<string>("Quiet End Cue Name")
176 );
177 }
178
179
180