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