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