1 /*
2 * Copyright (C) 2003 - Marc Brevoort
3 * Modified David W. Durham
4 *
5 * This file is part of ReZound, an audio editing application.
6 *
7 * ReZound is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published
9 * by the Free Software Foundation; either version 2 of the License,
10 * or (at your option) any later version.
11 *
12 * ReZound is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20 */
21
22 #include <stdlib.h>
23
24 #include "CGenerateNoiseAction.h"
25
26 #include "../CActionParameters.h"
27 #include <cstdlib>
28
CGenerateNoiseAction(const AActionFactory * factory,const CActionSound * actionSound,const double _noiseLength,const double _volume,const NoiseTypes _noiseType,const StereoImage _stereoImage,const double _maxParticleVelocity)29 CGenerateNoiseAction::CGenerateNoiseAction(const AActionFactory *factory,const CActionSound *actionSound,const double _noiseLength,const double _volume,const NoiseTypes _noiseType,const StereoImage _stereoImage,const double _maxParticleVelocity):
30 AAction(factory,actionSound),
31 noiseLength(_noiseLength), // seconds
32 volume(_volume), // 0 to 1 (a multiplier)
33 noiseType(_noiseType), // enum
34 stereoImage(_stereoImage), // enum
35 origLength(0),
36
37 maxParticleSpeed(_maxParticleVelocity),
38 oldRandL(0),oldRandR(0)
39 {
40 if(noiseLength<0)
41 throw runtime_error(string(__func__)+" -- noiseLength is less than zero: "+istring(noiseLength));
42 }
43
~CGenerateNoiseAction()44 CGenerateNoiseAction::~CGenerateNoiseAction()
45 {
46 }
47
initializePinkNoise(PinkNoise * pink,int numRows)48 void CGenerateNoiseAction::initializePinkNoise(PinkNoise *pink, int numRows)
49 {
50 pink->pink_Index=0;
51 pink->pink_IndexMask=(1<<numRows)-1;
52 const long pmax=(numRows+1) * (1<<(PINK_RANDOM_BITS-1));
53 pink->pink_Scalar=1.0f/pmax;
54
55 for(int i=0;i<numRows;i++)
56 pink->pink_Rows[i]=0;
57 pink->pink_RunningSum=0;
58 }
59
generateRandomNumber()60 static unsigned long generateRandomNumber()
61 {
62 // ??? this ain't too random is it?
63 static unsigned long randSeed=time(NULL);
64 randSeed=(randSeed*196314165+907633515);
65 return randSeed;
66 }
67
68 // return a pink noise value from -1 to 1
generatePinkNoise(PinkNoise * pink)69 double CGenerateNoiseAction::generatePinkNoise(PinkNoise *pink)
70 {
71 long newRandom;
72 pink->pink_Index=(pink->pink_Index+1) & pink->pink_IndexMask;
73
74 if(pink->pink_Index!=0)
75 {
76 int numZeros=0;
77 int n=pink->pink_Index;
78 while((n&1)==0)
79 {
80 n=n>>1;
81 numZeros++;
82 }
83 pink->pink_RunningSum-=pink->pink_Rows[numZeros];
84 newRandom=((long)generateRandomNumber())>>PINK_RANDOM_SHIFT;
85 pink->pink_RunningSum+=newRandom;
86 pink->pink_Rows[numZeros]=newRandom;
87 }
88
89 // Add extra white noise value
90 newRandom=((long)generateRandomNumber())>>PINK_RANDOM_SHIFT;
91 long sum=pink->pink_RunningSum+newRandom;
92 return 2.0*pink->pink_Scalar*sum; // *2 because it's actually returning half the range we want (fix nature of algorithm?)
93 }
94
gaussrand(int n)95 static double gaussrand(int n)
96 {
97 double X=0.0;
98 for (int i=0;i<n;i++)
99 X+=((rand()/(RAND_MAX+1.0))-.5)*2.0;
100 return X/n;
101 }
102
generateBrownNoise(double * _oldRand,const double maxParticleSpeed)103 double CGenerateNoiseAction::generateBrownNoise(double *_oldRand,const double maxParticleSpeed)
104 {
105 double oldRand=*_oldRand;
106 double randval=gaussrand(20)*maxParticleSpeed/100.0;
107
108 if( ((oldRand+randval)>=1.0) || (oldRand+randval<=-1.0))
109 randval=-randval;
110
111 oldRand+=randval;
112 oldRand=min(1.0,max(-1.0,oldRand)); // limit to [-1.0, 1.0]
113
114 *_oldRand=oldRand;
115 return oldRand;
116 }
117
118
119 // return a noise value from -1 to 1
getRandNoiseVal(const int noiseChannel)120 double CGenerateNoiseAction::getRandNoiseVal(const int noiseChannel)
121 {
122 // This will return a random noise value, based on the type of noise that was requested.
123 switch(noiseType)
124 {
125 case ntWhite:
126 return ((double)rand()/RAND_MAX)*2.0-1.0;
127 case ntPink:
128 return generatePinkNoise(noiseChannel ? &pinkR : &pinkL);
129 case ntBrown:
130 return generateBrownNoise(noiseChannel ? &oldRandR : &oldRandL, maxParticleSpeed);
131 case ntBlack:
132 return 0.0; // :)
133 default:
134 throw runtime_error(string(__func__)+" -- internal error -- unimplemented noiseType: "+istring(noiseType));
135 }
136 }
137
138 #warning add status bars
doActionSizeSafe(CActionSound * actionSound,bool prepareForUndo)139 bool CGenerateNoiseAction::doActionSizeSafe(CActionSound *actionSound,bool prepareForUndo)
140 {
141 origLength=actionSound->sound->getLength();
142 const sample_pos_t start=actionSound->start;
143 const sample_pos_t sampleCount=(sample_pos_t)(noiseLength*actionSound->sound->getSampleRate());
144
145 if(sampleCount==0)
146 {
147 actionSound->stop=actionSound->start;
148 return true;
149 }
150
151 actionSound->sound->addSpace(actionSound->doChannel,actionSound->start,sampleCount,true);
152 actionSound->stop=(actionSound->start+sampleCount)-1;
153
154 // new space is made for the noise, now calculate the values.
155
156 switch(noiseType)
157 {
158 case ntWhite:
159 srand(time(NULL));
160 break;
161
162 case ntPink:
163 initializePinkNoise(&pinkL,16);
164 initializePinkNoise(&pinkR,16);
165 break;
166
167 case ntBrown:
168 oldRandL=0;
169 oldRandR=0;
170 break;
171
172 case ntBlack:
173 break;
174
175 default:
176 throw runtime_error(string(__func__)+" -- internal error -- unimplemented noiseType: "+istring(noiseType));
177 }
178
179 srand(time(NULL));
180
181 // build a vector of indexes to the only channels that should be affected
182 vector<unsigned> doChannels;
183 for(unsigned i=0;i<actionSound->sound->getChannelCount();i++)
184 {
185 if(actionSound->doChannel[i])
186 doChannels.push_back(i);
187 }
188
189 for(unsigned i=0;i<doChannels.size();i++)
190 {
191 CRezPoolAccesser destL=actionSound->sound->getAudio(doChannels[i]);
192
193 if((i+1) < doChannels.size())
194 { // process a pair of channels
195 CRezPoolAccesser destR=actionSound->sound->getAudio(doChannels[i+1]);
196 sample_pos_t destPos=start;
197 for(sample_pos_t t=0;t<sampleCount;t++)
198 {
199 double randval=getRandNoiseVal(0);
200
201 destL[destPos]=convert_sample<double,sample_t>(randval*volume);
202
203 switch(stereoImage)
204 {
205 case siMono:
206 //randval=randval;
207 break;
208
209 case siInverse:
210 randval=-randval;
211 break;
212
213 case siIndependent:
214 case siSpatialStereo:
215 randval=getRandNoiseVal(1);
216 break;
217
218 default:
219 throw runtime_error(string(__func__)+" -- internal error -- unimplemented stereoImage: "+istring(stereoImage));
220 }
221 destR[destPos]=convert_sample<double,sample_t>(randval*volume);
222
223 destPos++;
224 }
225
226 i++; // skip the next channel because we just did it
227 }
228 else
229 { // process a single channel
230 sample_pos_t destPos=start;
231 for(sample_pos_t t=0;t<sampleCount;t++)
232 {
233 const double randval=getRandNoiseVal(0);
234 destL[destPos]=convert_sample<double,sample_t>(randval*volume);
235 destPos++;
236 }
237 }
238 }
239 return true;
240 }
241
canUndo(const CActionSound * actionSound) const242 AAction::CanUndoResults CGenerateNoiseAction::canUndo(const CActionSound *actionSound) const
243 {
244 return curYes;
245 }
246
undoActionSizeSafe(const CActionSound * actionSound)247 void CGenerateNoiseAction::undoActionSizeSafe(const CActionSound *actionSound)
248 {
249 const sample_pos_t sampleCount=(sample_pos_t)(noiseLength*actionSound->sound->getSampleRate());
250 actionSound->sound->removeSpace(actionSound->doChannel,actionSound->start,sampleCount,origLength);
251 }
252
253
254
255 // ------------------------------
256
CGenerateNoiseActionFactory(AActionDialog * channelSelectDialog,AActionDialog * dialog)257 CGenerateNoiseActionFactory::CGenerateNoiseActionFactory(AActionDialog *channelSelectDialog,AActionDialog *dialog) :
258 AActionFactory(N_("Generate Noise"),_("Generate Various Colors of Noise"),channelSelectDialog,dialog)
259 {
260 }
261
~CGenerateNoiseActionFactory()262 CGenerateNoiseActionFactory::~CGenerateNoiseActionFactory()
263 {
264 }
265
266 // ??? t'would be nice if I had two factories: 1 for insert noise and 1 for replace selection with noise
manufactureAction(const CActionSound * actionSound,const CActionParameters * actionParameters) const267 CGenerateNoiseAction *CGenerateNoiseActionFactory::manufactureAction(const CActionSound *actionSound,const CActionParameters *actionParameters) const
268 {
269 return new CGenerateNoiseAction(
270 this,
271 actionSound,
272 actionParameters->getValue<double>("Length"),
273 actionParameters->getValue<double>("Volume"),
274 (CGenerateNoiseAction::NoiseTypes)actionParameters->getValue<unsigned>("Noise Color"),
275 (CGenerateNoiseAction::StereoImage)actionParameters->getValue<unsigned>("Stereo Image"),
276 actionParameters->getValue<double>("Max Particle Velocity")
277 );
278 }
279
280