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 #ifndef __CSound_H__
22 #define __CSound_H__
23 
24 #include "../../config/common.h"
25 
26 #include <string.h>
27 
28 #include <string>
29 #include <map>
30 
31 #include "CSound_defs.h"
32 #include <endian_util.h>
33 
34 // it would be nice if I didn't have to include all this ???
35 #include <TPoolFile.h>
36 #include <TPoolAccesser.h>
37 #define REZOUND_POOLFILE_BLOCKSIZE 32768
38 #define REZOUND_POOLFILE_SIGNATURE "ReZoundF"
39 #define REZOUND_WORKING_POOLFILE_SIGNATURE "ReZoundW"
40 
41 typedef TStaticPoolAccesser<sample_t,TPoolFile<sample_pos_t,CMultiFile::l_addr_t> > CRezPoolAccesser;
42 
43 struct RPeakChunk
44 {
45 public:
46 	sample_t min;
47 	sample_t max;
48 private:
49 	friend class CSound;
50 	bool dirty;
51 };
52 
53 class CSound
54 {
55 public:
56 	// the p_addr_t needs to agree with CSound::getAudioDataSize()'s implementation too
57 	typedef TPoolFile<sample_pos_t,uint64_t> PoolFile_t;
58 
59 	CSound();
60 		// works like createWorkingPoolFile
61 	CSound(const string &_filename,const unsigned _sampleRate,const unsigned _channelCount,const sample_pos_t _size);
62 	virtual ~CSound();
63 
64 	// creates a poolFile with a temp name based off of originalFilename and populates the given meta-info
65 	// 	- don't want as public except that each derivation of ASoundTranslator would have ot be a friend
66 	void createWorkingPoolFile(const string originalFilename,const unsigned _sampleRate,const unsigned _channelCount,const sample_pos_t _size);
67 
68 	// attempts to open a poolFile with the given name and if it succeeds then it populates the data-members with the format-info
69 	bool createFromWorkingPoolFileIfExists(const string originalFilename,bool promptIfFound=true);
70 
71 
72 
73 	void closeSound();
74 
75 	void changeWorkingFilename(const string newOriginalFilename);
76 
77 
78 	CRezPoolAccesser getAudio(unsigned channel);
79 	const CRezPoolAccesser getAudio(unsigned channel) const;
80 
81 	// tempAudioPoolKey is returned from moveDataTemp or moveDataToTempAndReplaceSpace
82 	CRezPoolAccesser getTempAudio(unsigned tempAudioPoolKey,unsigned channel);
83 	const CRezPoolAccesser getTempAudio(unsigned tempAudioPoolKey,unsigned channel) const;
84 
85 
86 	/*
87 	 * - This returns a min and max sample value of the data between [dataPos,nextDataPos)
88 	 * - This should be used to render the waveform as it gives a more consistance
89 	 *   shape when scaling the data
90 	 */
91 	RPeakChunk getPeakData(unsigned channel,sample_pos_t dataPos,sample_pos_t nextDataPos,const CRezPoolAccesser &dataAccesser) const;
92 
93 	/*
94 	 * - Should be called by any code that modified the data in such a way that peak data needs to be modified
95 	 * - It's not necessary to call it for data that was touched by the methods in CSound that modify or resize
96 	 *   the data
97 	 */
98 	void invalidatePeakData(unsigned channel,sample_pos_t start,sample_pos_t stop);
99 	void invalidatePeakData(const bool doChannel[MAX_CHANNELS],sample_pos_t start,sample_pos_t stop);
100 	void invalidateAllPeakData();
101 
102 
103 
getLength()104 	sample_pos_t getLength() const		{ return size; }
getChannelCount()105 	unsigned getChannelCount() const	{ return channelCount; }
getSampleRate()106 	unsigned getSampleRate() const		{ return sampleRate; }
107 	void setSampleRate(unsigned newSampleRate);
isEmpty()108 	bool isEmpty() const			{ return size==0 || channelCount==0; }
109 
110 	/*
111 	 * It is very important to lock the sound (writer's lock) before calling these methods.
112 	 * And except will occur if a resizeLock has not been obtained before calling these methods
113 	 * (??? I could make a static bool variable that says it's okay not to have a lock if I know the app isn't multi-threaded)
114 	 */
115 	// ??? all of the space modifier methods should make sure that a resize lock has been obtained... if it hasn't do INTERNAL_ERROR
116 
117 
118 
119 	// Structure Manipulation Members (Most require a resize lock to have been obtained)
120 
121 	void addChannel(bool doZeroData); // adds a new channel at the end
122 	void addChannels(unsigned where,unsigned count,bool doZeroData);
123 
124 	void removeChannel(); // removes the last channel
125 	void removeChannels(unsigned where,unsigned count);
126 
127 	// moves each channel specified by whichChannels to a temp pool and returns a handle (tempAudioPoolKey) to those temp pools to be restored
128 	int moveChannelsToTemp(const bool whichChannels[MAX_CHANNELS]);
129 
130 	// moves channels back into position from a moveChannelsToTempPool()
131 	// tempAudioPoolKey was returned by moveChannelsToTempPool()
132 	// whichChannels MUST be the same as when moveChannelsToTempPool() was called
133 	// the audio length MUST be the same as when moveChannelsToTempPool() was called
134 	// the channel layout MUST be the same as after the moveChannelsToTempPool() was finished
135 	void moveChannelsFromTemp(int tempAudioPoolKey,const bool whichChannels[MAX_CHANNELS]);
136 
137 
138 
139 	/*
140 	 * - Adds 'length' samples of space at position 'where' to all channels
141 	 * - If 'doZeroData' is true, then the newly created space will be initialized to zero
142 	 */
143 	void addSpace(sample_pos_t where,sample_pos_t length,bool doZeroData=false);
144 
145 	/*
146 	 * - Adds 'length' samples of space at position 'where' to channels where whichChannels[i] is true
147 	 * - Any channel that was not affected will have 'length' samples of silence appended to it
148 	 * - If 'doZeroData' is true, then the newly created space will be initialized to zero
149 	 * - If 'maxLength' is given, then any channels longer than that will be truncated, and silence appended to unaffected channels will not make the channel exceed this maximum length
150 	 */
151 	void addSpace(const bool whichChannels[MAX_CHANNELS],sample_pos_t where,sample_pos_t length,bool doZeroData=false,sample_pos_t maxLength=NIL_SAMPLE_POS);
152 
153 
154 	/*
155 	 * - Removed 'length' samples of space from position 'where' to all channels
156 	 */
157 	void removeSpace(sample_pos_t where,sample_pos_t length);
158 
159 	/*
160 	 * - Removed 'length' samples of space from position 'where' to channels where whichChannels[i] is true
161 	 * - If all channels are not affected, then channels that were affected will have 'length' samples of silence appended to it
162 	 * - If 'maxLength' is given, then any channels longer than that will be truncated, and silence appended to unaffected channels will not make the channel exceed this maximum length
163 	 */
164 	void removeSpace(const bool whichChannels[MAX_CHANNELS],sample_pos_t where,sample_pos_t length,sample_pos_t maxLength=NIL_SAMPLE_POS);
165 
166 
167 	/*
168 	 * - Moves 'length' samples of data from position 'where' for each channel where whichChannels[i] is true to a newly created temporary pool in the pool file for this sound object
169 	 * - If all channels were not affected, then each channel that was affected will have 'length' samples of silence appended to it
170 	 * - The value returned is a handle to the temporary pools created by the move, it is the tempAudioPoolKey parameter passed to getTempData, moveDataFromTemp, removeSpaceAndMoveDataFromTemp, etc
171 	 * - If fudgeFactor is greater than zero, then that many samples will also be appended to the temp pool by copying them not moving them
172 	 *   	- The fudgeFactor can specify lengths that extend beyond the end of the sound, silence will just result int he temp pool
173 	 *   	- This is useful for functions which may need to read ahead in a temp buffer, but don't wish to incur the cost of an if statement to check bounds for just a few needed samples
174 	 * - If 'maxLength' is given, then any channels longer than that will be truncated, and silence appended to unaffected channels will not make the channel exceed this maximum length
175 	 */
176 	unsigned moveDataToTemp(const bool whichChannels[MAX_CHANNELS],sample_pos_t where,sample_pos_t length,sample_pos_t fudgeFactor=0,sample_pos_t maxLength=NIL_SAMPLE_POS);
177 
178 	/*
179 	 * - Copies 'length' samples of data from position 'where' for each channel where whichChannels[i] is true to a newly created temporary pool in the pool file for this sound object
180 	 * - The value returned is a handle to the temporary pools created by the copy, it is the tempAudioPoolKey parameter passed to getTempData, moveDataFromTemp, removeSpaceAndMoveDataFromTemp, etc
181 	 */
182 	unsigned copyDataToTemp(const bool whichChannels[MAX_CHANNELS],sample_pos_t where,sample_pos_t length);
183 
184 	/*
185 	 * - Moves 'length' samples of data from position 'where' but replaces the space with 'replaceLength' samples, for each channel where whichChannels[i] is true to a newly created temporary pool in the pool file for this sound object
186 	 * - If all channels were not affected then:
187 	 *   	- if more space is moved than replaced, then each channel affected will have 'length-replaceLength' samples of silence appended to it
188 	 *   	- if more space is replaced than moved, then each channel NOT affected will have 'replaceLength-length' samples of silencs appended to it
189 	 * - The value returned is a handle to the temporary pools created by the move, it is the tempAudioPoolKey parameter passed to getTempData, moveDataFromTemp, removeSpaceAndMoveDataFromTemp, etc
190 	 * - If fudgeFactor is greater than zero, then that many samples will also be appended to the temp pool by copying them not moving them
191 	 *   	- The fudgeFactor can specify lengths that extend beyond the end of the sound, silence will just result in the temp pool
192 	 *   	- This is useful for functions which may need to read ahead in a temp buffer, but don't wish to incur the cost of an if statement to check bounds for just a few needed samples
193 	 * - If 'maxLength' is given, then any channels longer than that will be truncated, and silence appended to unaffected channels will not make the channel exceed this maximum length
194 	 */
195 	unsigned moveDataToTempAndReplaceSpace(const bool whichChannels[MAX_CHANNELS],sample_pos_t where,sample_pos_t length,sample_pos_t replaceLength,sample_pos_t fudgeFactor=0,sample_pos_t maxLength=NIL_SAMPLE_POS);
196 
197 
198 	/*
199 	 * - Moves 'moveLength' samples of data from the beginning of a temporary pool specified by 'tempAudioPoolKey' to position 'moveWhere' in the normal audio data pools for each channel where whichChannels[i] is true
200 	 * - If all channels where not affected then, each channel that was not affected will have 'moveLength' samples of silenced appended to it
201 	 * - If 'removeTempAudioPools' is true, then all temporarly pools associated with 'tempAudioPoolKey' will be removed
202 	 * - If 'maxLength' is given, then any channels longer than that will be truncated, and silence appended to unaffected channels will not make the channel exceed this maximum length
203 	 */
204 	void moveDataFromTemp(const bool whichChannels[MAX_CHANNELS],unsigned tempAudioPoolKey,sample_pos_t moveWhere,sample_pos_t moveLength,bool removeTempAudioPools=true,sample_pos_t maxLength=NIL_SAMPLE_POS);
205 
206 	/*
207 	 * - Removes 'removeLength' samples of space from position 'removeWhere' and then moves 'moveLength' samples of data from the beginning of a temporary pool specified by 'tempAudioPoolKey' to position 'moveWhere' in the normal audio data pools for each channel where whichChannels[i] is true
208 	 * - If all channels where not affected then, each channel that was not affected will have 'moveLength' samples of silenced appended to it
209 	 * - If 'removeTempAudioPools' is true, then all temporarly pools associated with 'tempAudioPoolKey' will be removed
210 	 * - If 'maxLength' is given, then any channels longer than that will be truncated, and silence appended to unaffected channels will not make the channel exceed this maximum length
211 	 */
212 	void removeSpaceAndMoveDataFromTemp(const bool whichChannels[MAX_CHANNELS],sample_pos_t removeWhere,sample_pos_t removeLength,unsigned tempAudioPoolKey,sample_pos_t moveWhere,sample_pos_t moveLength,bool removeTempAudioPools=true,sample_pos_t maxLength=NIL_SAMPLE_POS);
213 
214 	/*
215 	 * - Removes all temporarly pools associated with 'tempAudioPoolKey'
216 	 */
217 	void removeTempAudioPools(unsigned tempAudioPoolKey);
218 
219 	// rotates 'amount' samples of data in the specified channel to the left or right between the 'start' and 'stop' positions
220 	void rotateLeft(const bool whichChannels[MAX_CHANNELS],const sample_pos_t start,const sample_pos_t stop,const sample_pos_t amount);
221 	void rotateRight(const bool whichChannels[MAX_CHANNELS],const sample_pos_t start,const sample_pos_t stop,const sample_pos_t amount);
222 
223 	// swaps the audio between channel A and channel B within the given region
224 	void swapChannels(unsigned channelA,unsigned channelB,const sample_pos_t where,const sample_pos_t length);
225 
226 
227 	void silenceSound(unsigned channel,sample_pos_t where,sample_pos_t length,bool doInvalidatePeakData=true,bool showProgressBar=true);
228 
229 	/*
230 	 * - mixes audio from src onto channel 'channel' starting at 'where' in this sound for 'length' samples (in this sound's sampleRate)
231 	 * - srcSampleRate specifies what sample rate src is in so that when it reads from src it will convert to this sound's sample rate
232 	 * - mixMethod specifies how to mix the data
233 	 * - if srcFit is no sftNone then src.getSize() samples from source will be squeezed into the [where,where+length) region in this sound using the specifed method to fit
234 	 */
235 	void mixSound(unsigned channel,sample_pos_t where,const CRezPoolAccesser src,sample_pos_t srcWhere,unsigned srcSampleRate,sample_pos_t length,MixMethods mixMethod,SourceFitTypes fitSrc,bool doInvalidatePeakData,bool showProgressBar);
236 
237 
238 	const string getTimePosition(sample_pos_t samplePos,int secondsDecimalPlaces=3,bool includeUnits=true) const;
239 	const sample_pos_t getPositionFromTime(const string time,bool &wasInvalid) const;
240 	const string getAudioDataSize() const;
241 	const string getAudioDataSize(sample_pos_t sampleCount) const; // with this object's format
242 	const string getPoolFileSize() const;
243 
244 	void setIsModified(bool v);
245 	const bool isModified() const;
246 
247 	// --------------------------------------------------------------------------
248 	// these members are used to manage the cues which a sound object can contain
249 	// --------------------------------------------------------------------------
250 
251 	#define MAX_SOUND_CUE_NAME_LENGTH 64
252 	struct RCue
253 	{
RCueRCue254 		RCue(const char _name[MAX_SOUND_CUE_NAME_LENGTH],sample_pos_t _time,bool _isAnchored)
255 		{
256 			strncpy(name,_name,MAX_SOUND_CUE_NAME_LENGTH);
257 			name[MAX_SOUND_CUE_NAME_LENGTH]=0;
258 			memset(reserved,0,sizeof(reserved));
259 
260 			time=_time;
261 			isAnchored=_isAnchored ? 1 : 0;
262 		}
263 
RCueRCue264 		RCue(const RCue &src)
265 		{
266 			operator=(src);
267 		}
268 
269 		RCue &operator=(const RCue &rhs)
270 		{
271 			strncpy(name,rhs.name,MAX_SOUND_CUE_NAME_LENGTH);
272 
273 			name[MAX_SOUND_CUE_NAME_LENGTH]=0;
274 			time=rhs.time;
275 			isAnchored=rhs.isAnchored;
276 
277 			return *this;
278 		}
279 
280 		/*
281 		 * Instead of char I would like to have int8_t
282 		 * I have a test in configure.ac that makes SURE char is 1 byte, if there's
283 		 * ever a platform with char size other than 1, I will deal with it then
284 		 * because making this int8_t causes the string class and strxxx() functions
285 		 * to be incompatible
286 		 */
287 		char name[MAX_SOUND_CUE_NAME_LENGTH+1];
288 
289 
290 		// If for some reason, this isn't enough space in the future,
291 		// I could just go with a new pool name and when loading an
292 		// older version, just update to the new pool name and convert
293 		// the older cues
294 		uint8_t reserved[58];
295 		uint8_t isAnchored;
296 		uint64_t time;
297 
298 		typedef uint8_t PackedChunk[
299 			MAX_SOUND_CUE_NAME_LENGTH+1+	//sizeof(name)+
300 			58+				//sizeof(reserved)+
301 			1+				//sizeof(isAnchored)+
302 			8				//sizeof(time)
303 		];
304 
packRCue305 		void pack(PackedChunk &r) const
306 		{
307 			// pack the values of the data members into r in little-endian
308 
309 			register unsigned offset=0;
310 
311 			memcpy(r+offset,name,sizeof(name));
312 			offset+=sizeof(name);
313 
314 			memset(r+offset,0,sizeof(reserved));
315 			offset+=sizeof(reserved);
316 
317 			memcpy(r+offset,&isAnchored,sizeof(isAnchored));
318 			offset+=sizeof(isAnchored);
319 
320 			uint64_t tTime=hetle(time);
321 			memcpy(r+offset,&tTime,sizeof(time));
322 			offset+=sizeof(time);
323 		}
324 
unpackRCue325 		void unpack(const PackedChunk &r)
326 		{
327 			// unpack the little-endian values from r into the data members
328 
329 			register unsigned offset=0;
330 
331 			memcpy(&name,r+offset,sizeof(name));
332 			offset+=sizeof(name);
333 
334 			// no need to copy reserved
335 			offset+=sizeof(reserved);
336 
337 			memcpy(&isAnchored,r+offset,sizeof(isAnchored));
338 			offset+=sizeof(isAnchored);
339 
340 			memcpy(&time,r+offset,sizeof(time));
341 			lethe(&time);
342 			offset+=sizeof(time);
343 		}
344 
345 	private:
346 		friend class CrezSoundTranslator;
RCueRCue347 		RCue() {}
348 
349 	};
350 
351 	const size_t getCueCount() const;
352 
353 	const string getCueName(size_t index) const;
354 
355 	const sample_pos_t getCueTime(size_t index) const;
356 	void setCueTime(size_t index,sample_pos_t newTime);
357 
358 	const bool isCueAnchored(size_t index) const;
359 
360 	void addCue(const string &name,const sample_pos_t time,const bool isAnchored);
361 	void insertCue(size_t index,const string &name,const sample_pos_t time,const bool isAnchored);
362 	void removeCue(size_t index);
363 	void clearCues();
364 	void enableCueAdjustmentsOnSpaceChanges(bool enabled);
365 
366 	static size_t __default_cue_index;
367 	bool containsCue(const string &name,size_t &index=__default_cue_index) const;
368 		// these following find methods return true if something is found else false
369 	bool findCue(const sample_pos_t time,size_t &index) const;
370 	bool findNearestCue(const sample_pos_t time,size_t &index,sample_pos_t &distance) const; // finds the cue nearest to the given time
371 	bool findPrevCue(const sample_pos_t time,size_t &index) const; // finds the previous cue in time at the cue with the given time (false if none is found)
372 	bool findNextCue(const sample_pos_t time,size_t &index) const; // finds the following cue in time at the cue with the given time (false if none is found)
373 
374 	bool findPrevCueInTime(const sample_pos_t time,size_t &index) const; // finds the previous cue in time from the given time (false if none is found)
375 	bool findNextCueInTime(const sample_pos_t time,size_t &index) const; // finds the next cue in time from the given time (false if none is found)
376 
377 	const string getUnusedCueName(const string &prefix="noname") const;
378 
379 	// --------------------------------------------------------------------------
380 	// --------------------------------------------------------------------------
381 	// --------------------------------------------------------------------------
382 
383 
384 
385 	// --- Methods for managing user notes
386 	const string getUserNotes() const;
387 	void setUserNotes(const string &notes);
388 
389 
390 
391 	// - This method returns an accesser to a pool of the template type which can be used
392 	//   by other subsystems within ReZound to store any data they need to persist after
393 	//   the file is closed.
394 	// - I prefix the pool name with "__GENERAL__ " so that they will be easily identifiable if
395 	//   necessary.  And it means the method cannot be used to get at say one of the audio pools.
396 	// - For now, the CSoundPlayerChannel class uses this to store the output routes of the
397 	//   channels within this sound, so a 4 channel sound can be played on a stereo sound card.
getGeneralDataAccesser(const string poolName)398 	template<class T> TPoolAccesser<T,PoolFile_t > getGeneralDataAccesser(const string poolName)
399 	{
400 		if(poolFile.containsPool("__GENERAL__ "+poolName))
401 			return poolFile.PoolFile_t::getPoolAccesser<T>("__GENERAL__ "+poolName);
402 		else
403 			return poolFile.PoolFile_t::createPool<T>("__GENERAL__ "+poolName);
404 	}
405 
getGeneralDataAccesser(const string poolName)406 	template<class T> const TPoolAccesser<T,PoolFile_t > getGeneralDataAccesser(const string poolName) const
407 	{
408 		if(poolFile.containsPool("__GENERAL__ "+poolName))
409 			return poolFile.PoolFile_t::getPoolAccesser<T>("__GENERAL__ "+poolName);
410 		else
411 			throw runtime_error(string(__func__)+" -- general data pool does not exist: "+poolName);
412 	}
413 
containsGeneralDataPool(const string poolName)414 	bool containsGeneralDataPool(const string poolName) const { return poolFile.containsPool("__GENERAL__ "+poolName); }
415 
removeGeneralDataPool(const string poolName)416 	void removeGeneralDataPool(const string poolName) { poolFile.removePool("__GENERAL__ "+poolName,false); }
417 
418 
419 	void flush();
420 
421 	void defragPoolFile();
422 	void printSAT(); // temporary for debugging ???
423 	void verifySAT(); // temporary for debugging ???
424 
425 private:
426 	friend class ASoundTranslator; // so it can verify some things
427 	friend class CrezSoundTranslator;
428 	friend class ASoundRecorder; // so it can backupSAT() on the pool file when recording is done
429 
430 
431 	typedef TPoolAccesser<sample_t,PoolFile_t > CInternalRezPoolAccesser;
432 
433 	CInternalRezPoolAccesser getAudioInternal(unsigned channel);
434 	CInternalRezPoolAccesser getTempDataInternal(unsigned tempAudioPoolKey,unsigned channel);
435 
436 	PoolFile_t poolFile;
437 
438 	sample_pos_t size; // ??? rename to sampleCount
439 	unsigned sampleRate;
440 	unsigned channelCount;
441 
442 	// this should be called after every space modification and even loading of a file
443 	// and pass NIL_SAMPLE_POS as the maxLength parameter to match to the longest channel
444 	void matchUpChannelLengths(sample_pos_t maxLength,bool doZeroData=true);
445 
446 	void addSpaceToChannel(unsigned channel,sample_pos_t where,sample_pos_t length,bool doZeroData);
447 	void removeSpaceFromChannel(unsigned channel,sample_pos_t where,sample_pos_t length);
448 	void copyDataFromChannel(unsigned tempAudioPoolKey,unsigned channel,sample_pos_t where,sample_pos_t length);
449 	void moveDataOutOfChannel(unsigned tempAudioPoolKey,unsigned channel,sample_pos_t where,sample_pos_t length);
450 	void moveDataIntoChannel(unsigned tempAudioPoolKey,unsigned channelInTempPool,unsigned channelInAudio,sample_pos_t where,sample_pos_t length,bool removeTempAudioPool);
451 
452 	static void appendForFudgeFactor(CInternalRezPoolAccesser dest,const CInternalRezPoolAccesser src,sample_pos_t srcWhere,sample_pos_t fudgeFactor);
453 
454 	void prvAddChannel(bool addAudioSpaceForNewChannel,bool doZeroData);
455 
456 	struct RFormatInfo
457 	{
458 		uint32_t version;
459 		uint64_t size;
460 		uint32_t sampleRate;
461 		uint32_t channelCount;
462 
463 		void operator=(const RFormatInfo &src)
464 		{
465 			version=src.version;
466 			size=src.size;
467 			sampleRate=src.sampleRate;
468 			channelCount=src.channelCount;
469 		}
470 
471 	};
472 	typedef TPoolAccesser<RFormatInfo,PoolFile_t > CFormatInfoPoolAccesser;
473 
474 	int metaInfoPoolID;
475 	int channelPoolIDs[MAX_CHANNELS];
476 
477 	typedef TPoolAccesser<RPeakChunk,PoolFile_t > CPeakChunkRezPoolAccesser;
478 	CPeakChunkRezPoolAccesser *peakChunkAccessers[MAX_CHANNELS];
479 
480 	unsigned tempAudioPoolKeyCounter;
481 
482 	bool _isModified;
483 
484 
485 	typedef TPoolAccesser<RCue,PoolFile_t> CCuePoolAccesser;
486 	CCuePoolAccesser *cueAccesser;
487 	map<sample_pos_t,size_t> cueIndex; // index into cueAccesser by time
488 	bool adjustCuesOnSpaceChanges;
489 
490 	void adjustCues(const sample_pos_t pos1,const sample_pos_t pos2);
491 	void createCueAccesser();
492 	void deleteCueAccesser();
493 	void rebuildCueIndex();
494 
495 
496 
497 
498 	void createPeakChunkAccessers();
499 	void deletePeakChunkAccessers();
500 
501 	static sample_pos_t calcPeakChunkCount(sample_pos_t givenSize);
502 
503 	static const string createTempAudioPoolName(unsigned tempAudioPoolKey,unsigned channel);
504 	CInternalRezPoolAccesser createTempAudioPool(unsigned tempAudioPoolKey,unsigned channel);
505 	void removeAllTempAudioPools();
506 
507 	// saves the data-members to the meta info in the temporary poolFile
508 	void saveMetaInfo();
509 
510 	// called after every space modification to ensure that there is at least 1 sample of length
511 	// zero length poses problems with selection positions, so I just always make sure there is at least 1 sample
512 	void ensureNonZeroLength();
513 
514 
515 
516 
517 	// --- private locking methods only used by CSoundLocker ------------------------------
518 	friend class CSoundLocker;
519 
520 	// locks to keep the size from changing (multiple locks can be obtained of this type)
521 	void lockSize() const;
522 	bool trylockSize() const;
523 	void unlockSize() const;
524 
525 	// locks to be able to change the size (only one lock can be obtained of this type)
526 	void lockForResize() const;
527 	bool trylockForResize() const;
528 	void unlockForResize() const;
529 
530 };
531 
532 class CSoundLocker {
533 	const CSound *mSound;
534 	const bool mLockForResize;
535 public:
536 	CSoundLocker(const CSound *sound, bool lockForResize, bool trylock=false)
mSound(sound)537 		: mSound(sound)
538 		, mLockForResize(lockForResize)
539 	{
540 		if(mLockForResize) {
541 			if(trylock) {
542 				// trylock for resize
543 				if(!mSound->trylockForResize()) {
544 					mSound = NULL;
545 				}
546 			} else {
547 				// lock for resize
548 				mSound->lockForResize();
549 			}
550 		} else {
551 			if(trylock) {
552 				// trylock the size
553 				if(!mSound->trylockSize()) {
554 					mSound = NULL;
555 				}
556 			} else {
557 				// lock the size
558 				mSound->lockSize();
559 			}
560 		}
561 	}
562 
~CSoundLocker()563 	~CSoundLocker() {
564 		unlock();
565 	}
566 
isLocked()567 	bool isLocked() const {
568 		return mSound != NULL;
569 	}
570 
unlock()571 	void unlock() {
572 		if(mSound) {
573 			if(mLockForResize) {
574 				mSound->unlockForResize();
575 			} else {
576 				mSound->unlockSize();
577 			}
578 			mSound = NULL;
579 		}
580 	}
581 };
582 
583 #endif
584