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 "CSound.h"
22 #include "AStatusComm.h"
23 
24 #include <math.h>
25 #include <stdio.h> // ??? just for console info printfs
26 
27 #include <stdexcept>
28 
29 #include <CPath.h>
30 
31 #include <istring>
32 
33 #include "settings.h"
TStaticPoolAccesser(pool_file_t * _poolFile,poolId_t _poolId)34 #include "unit_conv.h" // for seconds_to_string()
35 
36 
37 /* TODO:
38 	- add try and catch around space modifier methods that call matchUpChannelLengths even upon exception
39 	- create MAX_LENGTH for an arbitrary maximum length incase I need to enforce such a limit
40 	- I need to rework the bounds checking not to ever add where+len since it could overflow, but do it correctly where it should never have a problem
41 		- make some macros for doing this since I do it in several places
42 		- I have already fixed some of the methods
43 
44 	- the peak chunk mantainance is not absolutly perfect... if I delete a portion of the data and thus remove some of the
45 	  entries in the peak chunk data, the min and maxes aren't exactly correct now because the PEAK_CHUNK_SIZE samples which
46 	  were calculated for teh originaly data is now scewed a little on top of the new data... normally not enough to worry
47 	  about in just a frontend rendering of it... However overtime this error in the peak chunk data accumulates which is
48 	  why I have a refresh button which recalculates all the peak data.  Perhaps there is some other method which could better
49 	  reduce or eliminate this error without completely recaclulating the peak chunk data anytime an operation is done on a
50 	  channel... perhaps with undo, I could store the peak chunk data in temp pools just as I did the audio data and could
51 	  just as readily restore it on undo
52 */
53 
54 // ??? I could just set peakChunk data for space that I know I just silenced to min=max=0 and not bother setting it dirty
55 
56 
57 #define PEAK_CHUNK_SIZE 500
58 
59 // ??? probably check a static variable that doesn't require a lock if the application is not threaded
60 #define ASSERT_RESIZE_LOCK \
61 	if(!poolFile.isExclusiveLocked()) \
62 		throw(runtime_error(string(__func__)+" -- this CSound object has not been locked for resize"));
63 
64 #define ASSERT_SIZE_LOCK \
65 	if(!poolFile.isExclusiveLocked() && poolFile.getSharedLockCount()<=0) \
~TStaticPoolAccesser()66 		throw(runtime_error(string(__func__)+" -- this CSound object's size has not been locked"));
67 
68 
69 // current pool names for the current version
70 #define FORMAT_INFO_POOL_NAME "Format Info"
71 #define AUDIO_POOL_NAME "Channel "
72 #define PEAK_CHUNK_POOL_NAME "PeakChunk "
73 #define TEMP_AUDIO_POOL_NAME "TempAudioPool_"
74 #define CUES_POOL_NAME "Cues"
75 #define NOTES_POOL_NAME "UserNotes"
76 
77 #define GET_WORKING_FILENAME(workDir,filename) (workDir+CPath::dirDelim+CPath(filename).baseName()+".pf$")
78 
79 CSound::CSound() :
80 	poolFile(REZOUND_POOLFILE_BLOCKSIZE,REZOUND_WORKING_POOLFILE_SIGNATURE),
81 
82 	size(0),
operator =(const TStaticPoolAccesser<pool_element_t,pool_file_t> & rhs)83 	sampleRate(0),
84 	channelCount(0),
85 
86 	metaInfoPoolID(0),
87 
88 	tempAudioPoolKeyCounter(1),
89 
90 	_isModified(true),
91 
92 	cueAccesser(NULL),
93 	adjustCuesOnSpaceChanges(true)
94 {
95 	for(unsigned t=0;t<MAX_CHANNELS;t++)
96 		peakChunkAccessers[t]=NULL;
97 }
98 
99 CSound::CSound(const string &_filename,const unsigned _sampleRate,const unsigned _channelCount,const sample_pos_t _size) :
read(pool_element_t buffer[],l_addr_t count) const100 	poolFile(REZOUND_POOLFILE_BLOCKSIZE,REZOUND_WORKING_POOLFILE_SIGNATURE),
101 
102 	size(0),
103 	sampleRate(0),
104 	channelCount(0),
105 
106 	metaInfoPoolID(0),
107 
108 	tempAudioPoolKeyCounter(1),
109 
110 	_isModified(true),
111 
112 	cueAccesser(NULL)
113 {
114 	for(unsigned t=0;t<MAX_CHANNELS;t++)
115 		peakChunkAccessers[t]=NULL;
116 
117 	try
118 	{
119 		createWorkingPoolFile(_filename,_sampleRate,_channelCount,_size);
120 	}
121 	catch(...)
122 	{
123 		try
124 		{
125 			poolFile.closeFile(false,true);
126 		} catch(...) {}
127 		throw;
128 	}
129 }
130 
overflowWrite(const pool_element_t buffer[],l_addr_t count,bool append)131 CSound::~CSound()
132 {
133     // going away, so release all read locks
134     // ??? add back, but implement correctly
135 /*
136     while(poolFile.getSharedLockCount()>0)
137         unlockSize();
138 */
139 
140 	if(poolFile.isOpen())
141 	{
142 		deletePeakChunkAccessers();
143 		deleteCueAccesser();
144     		poolFile.closeFile(false,false);
145 	}
146 }
147 
148 void CSound::changeWorkingFilename(const string newOriginalFilename)
149 {
150 	const string workDir=CPath(poolFile.getFilename()).dirName();
151 	poolFile.rename(GET_WORKING_FILENAME(workDir,newOriginalFilename));
152 }
153 
154 void CSound::closeSound()
155 {
156 	// ??? probably should get a lock?
157 	deletePeakChunkAccessers();
158 	deleteCueAccesser();
159 	poolFile.closeFile(false,true);
160 }
161 
162 // locks to keep the size from changing (multiple locks can be obtained of this type)
163 void CSound::lockSize() const
164 {
165 	poolFile.sharedLock();
copyData(l_addr_t destWhere,const TStaticPoolAccesser<pool_element_t,pool_file_t> & src,l_addr_t srcWhere,l_addr_t length)166 }
167 
168 bool CSound::trylockSize() const
169 {
170 	return(poolFile.sharedTrylock());
171 }
172 
173 void CSound::unlockSize() const
174 {
175 	poolFile.sharedUnlock();
176 }
177 
178 // locks to be able to change the size (only one lock can be obtained of this type)
179 void CSound::lockForResize() const
180 {
181 	poolFile.exclusiveLock();
182 }
183 
184 bool CSound::trylockForResize() const
185 {
zeroData(l_addr_t where,l_addr_t length)186     return(poolFile.exclusiveTrylock());
187 }
188 
189 void CSound::unlockForResize() const
190 {
191 	poolFile.exclusiveUnlock();
192 }
193 
194 CSound::CInternalRezPoolAccesser CSound::getAudioInternal(unsigned channel)
195 {
196 	return(poolFile.getPoolAccesser<sample_t>(channelPoolIDs[channel]));
197 }
cacheBlock(l_addr_t where) const198 
199 CSound::CInternalRezPoolAccesser CSound::getTempDataInternal(unsigned tempAudioPoolKey,unsigned channel)
200 {
201 	return(poolFile.getPoolAccesser<sample_t>(createTempAudioPoolName(tempAudioPoolKey,channel)));
202 }
203 
204 CRezPoolAccesser CSound::getAudio(unsigned channel)
205 {
206 	if(channel>=channelCount)
207 		throw(runtime_error(string(__func__)+" -- invalid channel: "+istring(channel)));
208 
209 	return(poolFile.getPoolAccesser<sample_t>(channelPoolIDs[channel]));
210 }
211 
212 const CRezPoolAccesser CSound::getAudio(unsigned channel) const
213 {
214 	if(channel>=channelCount)
215 		throw(runtime_error(string(__func__)+" -- invalid channel: "+istring(channel)));
216 
217 	return(poolFile.getPoolAccesser<sample_t>(channelPoolIDs[channel]));
218 }
219 
220 
221 
222 CRezPoolAccesser CSound::getTempAudio(unsigned tempAudioPoolKey,unsigned channel)
223 {
224 	return(poolFile.getPoolAccesser<sample_t>(createTempAudioPoolName(tempAudioPoolKey,channel)));
225 }
226 
227 const CRezPoolAccesser CSound::getTempAudio(unsigned tempAudioPoolKey,unsigned channel) const
228 {
229 	return(poolFile.getPoolAccesser<sample_t>(createTempAudioPoolName(tempAudioPoolKey,channel)));
230 }
231 
232 /*
233  *  - This method gets the peak chunk information between [dataPos,nextDataPos)
234  *    where dataPos and nextDataPos are positions in the sample data.
235  *  - That is, the min and max sample value between those data positions.
236  *  - This information is used to render the waveform data on screen.
237  *
238  *  - This information is stored in a pool of RPeakChunk struct objects in the
239  *    pool file for this sound object.  And a min and max is stored for every
240  *    PEAK_CHUNK_SIZE samples.
241  *  - The RPeakChunk struct has a bool flag, 'dirty', that get's set to true
242  *    for every chunk that we need to re-calculate the min and max for.  This
243  *    is necessary when perhaps an action modifies the data, so the view on
244  *    screen also needs to change.
245  *
246  *  - Sometimes it reads this precalculated peak chunk information, and sometimes
247  *    it reads the data directly.  If the nextDataPos-dataPos < PEAK_CHUNK_SIZE, then
248  *    we should just read the data normally and find the min and max on the fly.
249  *    Otherwise, we can use the precalculated information.
250  *
251  *  - When using the precalculated information:
252  *	    - I could simply return peakChunks[floor(dataPos/PEAK_CHUNK_SIZE)], but I
253  *	      don't since the next time getPeakValue is called, it may have skipped over
254  *	      more than a chunk size, so the min and max would not necessarily be accurate.
255  *	    - So, I also have the caller pass in the dataPos of the next time this method
256  *	      will be called and I combine the mins and maxes of all the chunks that will
257  *	      be skipped over by the next call.
258  *
259  *  - Also, for practical reasons, it is okay if nextDataPos is >= getLength(), it's really
260  *    'what WOULD be the next data position' not than it will be necessarily called with that
261  *    position
262  */
263 RPeakChunk CSound::getPeakData(unsigned channel,sample_pos_t dataPos,sample_pos_t nextDataPos,const CRezPoolAccesser &dataAccesser) const
264 {
265 	if(channel>=channelCount)
266 		throw(runtime_error(string(__func__)+" -- channel parameter is out of change: "+istring(channel)));
267 
268 /*
269 if(dataPos==getLength()-1)
270 {
271 	RPeakChunk p;
272 	p.min=MIN_SAMPLE;
273 	p.max=MAX_SAMPLE;
274 
275 	return(p);
276 }
277 */
278 
279 	// I could check the bounds in dataPos and nextDataPos, but I'll just let the caller have already done that
280 	if((nextDataPos-dataPos)<PEAK_CHUNK_SIZE)
281 	{ // just find min and max on the fly
282 		//printf("NOT using precalculated data\n");
283 		sample_t _min=dataAccesser[dataPos];
284 		sample_t _max=dataAccesser[dataPos];
285 
286 		// don't attempt to read past the end of the data
287 		if(nextDataPos>dataAccesser.getSize())
288 			nextDataPos=dataAccesser.getSize();
289 
290 		for(sample_pos_t t=dataPos+1;t<nextDataPos;t++)
291 		{
292 			sample_t s=dataAccesser[t];
293 			_min=min(_min,s);
294 			_max=max(_max,s);
295 		}
296 
297 		RPeakChunk p;
298 		p.min=_min;
299 		p.max=_max;
300 		return(p);
301 	}
302 	else
303 	{
304 		// scale down and truncate the data positions to offsets into the peak chunk information
305 		// which only ranges [0,size/PEAK_CHUNK_SIZE]
306 		sample_pos_t firstChunk=dataPos/PEAK_CHUNK_SIZE;
307 		sample_pos_t lastChunk=nextDataPos/PEAK_CHUNK_SIZE;
308 
309 		RPeakChunk ret;
310 
311 #warning see about using a const CPeakChunkRezPoolAccesser here
312 		CPeakChunkRezPoolAccesser &peakChunkAccesser=*(peakChunkAccessers[channel]);
313 
314 		// don't attempt to read from skipped chunks that don't exist
315 		if(lastChunk>peakChunkAccesser.getSize())
316 			lastChunk=peakChunkAccesser.getSize();
317 
318 		// - we combine all the mins and maxes of the peak chunk information between [firstChunk and lastChunk)
319 		// - Also, if any chunk is dirty along the way, we recalculate it
320 		for(sample_pos_t t=firstChunk;t<lastChunk;t++)
321 		{
322 			RPeakChunk &p=peakChunkAccesser[t];
323 
324 			// if dirty, then recalculate for this chunk
325 			if(p.dirty)
326 			{
327 				//printf("recalculating peak chunk data for: %lld\n",(long long)t);
328 				sample_pos_t start=t*PEAK_CHUNK_SIZE;
329 				sample_pos_t end=start+PEAK_CHUNK_SIZE;
330 				if(start<getLength())
331 				{
332 					if(end>getLength())
333 						end=getLength();
334 
335 					sample_t _min=dataAccesser[start];
336 					sample_t _max=dataAccesser[start];
337 
338 					for(sample_pos_t i=start+1;i<end;i++)
339 					{
340 						sample_t s=dataAccesser[i];
341 						_min=min(_min,s);
342 						_max=max(_max,s);
343 					}
344 
345 					p.min=_min;
346 					p.max=_max;
347 				}
348 				p.dirty=false;
349 			}
350 
351 			if(t==firstChunk)
352 			{
353 				ret.min=p.min;
354 				ret.max=p.max;
355 			}
356 			else
357 			{
358 				ret.min=min(ret.min,p.min);
359 				ret.max=max(ret.max,p.max);
360 			}
361 		}
362 
363 		return(ret);
364 	}
365 }
366 
367 void CSound::invalidatePeakData(unsigned channel,sample_pos_t start,sample_pos_t stop)
368 {
369 	if(channel>=channelCount)
370 		throw(runtime_error(string(__func__)+" -- channel parameter is out of change: "+istring(channel)));
371 
372 	// ??? check ranges of start and stop
373 
374 	CPeakChunkRezPoolAccesser &peakChunkAccesser=*(peakChunkAccessers[channel]);
375 
376 	// fudge one peak chunk size longer
377 	if(stop<MAX_LENGTH-PEAK_CHUNK_SIZE)
378 	{
379 		stop+=PEAK_CHUNK_SIZE;
380 		if(stop>getLength()-1)
381 			stop=getLength()-1;
382 	}
383 
384 	const sample_pos_t firstChunk=start/PEAK_CHUNK_SIZE;
385 	const sample_pos_t lastChunk=stop/PEAK_CHUNK_SIZE;
386 
387 	for(sample_pos_t t=firstChunk;t<=lastChunk;t++)
388 		peakChunkAccesser[t].dirty=true;
389 }
390 
391 void CSound::invalidatePeakData(const bool doChannel[MAX_CHANNELS],sample_pos_t start,sample_pos_t stop)
392 {
393 	for(sample_pos_t t=0;t<channelCount;t++)
394 	{
395 		if(doChannel[t])
396 			invalidatePeakData(t,start,stop);
397 	}
398 }
399 
400 void CSound::invalidateAllPeakData()
401 {
402 	deletePeakChunkAccessers();
403 	createPeakChunkAccessers();
404 }
405 
406 
407 void CSound::addChannel(bool doZeroData)
408 {
409 	prvAddChannel(true,doZeroData);
410 }
411 
412 void CSound::prvAddChannel(bool addAudioSpaceForNewChannel,bool doZeroData)
413 {
414 	ASSERT_RESIZE_LOCK
415 
416 	if((getChannelCount()+1)>MAX_CHANNELS)
417 		throw(runtime_error(string(__func__)+" -- adding another channel would exceed the maximum of "+istring(MAX_CHANNELS)+" channels"));
418 
419 	const string audioPoolName=AUDIO_POOL_NAME+istring(channelCount+1);
420 	const string peakChunkPoolName=PEAK_CHUNK_POOL_NAME+istring(channelCount);
421 
422 	peakChunkAccessers[channelCount]=NULL;
423 	bool addedToChannelCount=false;
424 	try
425 	{
426 		CInternalRezPoolAccesser audioPool=poolFile.createPool<sample_t>(audioPoolName);
427 		channelPoolIDs[channelCount]=poolFile.getPoolIdByName(audioPoolName);
428 
429 		CPeakChunkRezPoolAccesser peakChunkPool=poolFile.createPool<RPeakChunk>(peakChunkPoolName);
430 		peakChunkAccessers[channelCount]=new CPeakChunkRezPoolAccesser(peakChunkPool);
431 
432 		channelCount++;
433 		addedToChannelCount=true;
434 
435 		if(addAudioSpaceForNewChannel)
436 			matchUpChannelLengths(getLength(),doZeroData);
437 	}
438 	catch(...)
439 	{ // attempt to recover
440 		if(addedToChannelCount)
441 			channelCount--;
442 
443 		poolFile.removePool(audioPoolName,false);
444 		delete peakChunkAccessers[channelCount];
445 		poolFile.removePool(peakChunkPoolName,false);
446 
447 		throw;
448 	}
449 
450 	saveMetaInfo();
451 }
452 
453 void CSound::addChannels(unsigned where,unsigned count,bool doZeroData)
454 {
455 	ASSERT_RESIZE_LOCK
456 
457 	if(where>getChannelCount())
458 		throw(runtime_error(string(__func__)+" -- where out of range: "+istring(where)+">"+istring(getChannelCount())));
459 	if((count+getChannelCount())>MAX_CHANNELS)
460 		throw(runtime_error(string(__func__)+" -- adding "+istring(count)+" channels would exceed the maximum of "+istring(MAX_CHANNELS)+" channels"));
461 
462 	/*
463 	 * This way may seem a little obtuse, but it's the easiest way without
464 	 * renaming pools, regetting poolIds, invalidating any outstanding
465 	 * accessors (because now their poolIds are invalid) and stuff.. simply
466 	 * add a channel to the end and move it where it needs to be and do this
467 	 * for each channel to add
468 	 */
469 
470 	size_t swapCount=getChannelCount()-where;
471 	for(unsigned t=0;t<count;t++)
472 	{
473 		// add a channel to the end
474 		addChannel(doZeroData);
475 
476 		// through a series of swaps move that channel to where+t
477 		for(unsigned i=0;i<swapCount;i++)
478 			swapChannels(channelCount-i-1,channelCount-i-2,0,getLength());
479 	}
480 }
481 
482 void CSound::removeChannel()
483 {
484 	ASSERT_RESIZE_LOCK
485 
486 	if(getChannelCount()<=1)
487 		throw(runtime_error(string(__func__)+" -- removing a channel would cause channel count to go to zero"));
488 
489 	const string audioPoolName=AUDIO_POOL_NAME+istring(channelCount);
490 	const string peakChunkPoolName=PEAK_CHUNK_POOL_NAME+istring(channelCount-1);
491 
492 	poolFile.removePool(audioPoolName);
493 	poolFile.removePool(peakChunkPoolName);
494 
495 	channelCount--;
496 	saveMetaInfo();
497 }
498 
499 void CSound::removeChannels(unsigned where,unsigned count)
500 {
501 	ASSERT_RESIZE_LOCK
502 
503 	if(where>getChannelCount())
504 		throw(runtime_error(string(__func__)+" -- where out of range: "+istring(where)+">"+istring(getChannelCount())));
505 	if(count>(getChannelCount()-where))
506 		throw(runtime_error(string(__func__)+" -- where/count out of range: "+istring(where)+"/"+istring(count)));
507 	if(where==0 && count>=getChannelCount())
508 		throw(runtime_error(string(__func__)+" -- removing "+istring(count)+" channels at "+istring(where)+" would cause the channel count to go to zero"));
509 
510 	unsigned swapCount=channelCount-where-1;
511 	for(unsigned t=0;t<count;t++)
512 	{
513 		// through a series of swaps move the channel at where to the end
514 		if(swapCount>0)
515 		{
516 			for(unsigned i=0;i<swapCount;i++)
517 				swapChannels(where+i,where+i+1,0,getLength());
518 			swapCount--;
519 		}
520 
521 		// remove the last channel
522 		removeChannel();
523 	}
524 }
525 
526 int CSound::moveChannelsToTemp(const bool whichChannels[MAX_CHANNELS])
527 {
528 	ASSERT_RESIZE_LOCK
529 
530 	unsigned removeCount=0;
531 	for(unsigned t=0;t<MAX_CHANNELS;t++)
532 	{
533 		removeCount+=whichChannels[t] ? 1 : 0;
534 		if(whichChannels[t] && t>=getChannelCount())
535 			throw(runtime_error(string(__func__)+" -- whichChannels specifies to remove channel "+istring(t)+" which does not exist"));
536 	}
537 	if(removeCount>=getChannelCount())
538 		throw(runtime_error(string(__func__)+" -- whichChannels specifies all the channels, which would cause the channel count to go to zero"));
539 
540 	// through a series of swaps move all the channels to be removed to become the last channels in the sound
541 	removeCount=0;
542 	for(unsigned _t=getChannelCount();_t>0;_t--)
543 	{
544 		unsigned t=_t-1;
545 		if(whichChannels[t])
546 		{
547 			for(unsigned i=t;i<getChannelCount()-1-removeCount;i++)
548 				swapChannels(i,i+1,0,getLength());
549 			removeCount++;
550 		}
551 	}
552 
553 	const int tempAudioPoolKey=tempAudioPoolKeyCounter++;
554 
555 	// move the last 'removeCount' channels to a temp pool
556 	for(unsigned t=getChannelCount()-removeCount;t<getChannelCount();t++)
557 		moveDataOutOfChannel(tempAudioPoolKey,t,0,getLength());
558 
559 	// remove the last 'removeCount' channels
560 	for(unsigned t=0;t<removeCount;t++)
561 		removeChannel();
562 
563 	return(tempAudioPoolKey);
564 }
565 
566 /*
567  * - tempPoolKey is the value returned by the previous call to moveChannelsToTemp()
568  * - whichChannels MUST match the whichChannels that was given to moveChannelsToTemp and
569  *   this sound MUST have the channel layout that it had after the return from.  The
570  *   sound MUST also be the same length as it was when moveChannelsToTemp() was called
571  *   the previous call to moveChannelsToTemp()
572  */
573 void CSound::moveChannelsFromTemp(int tempAudioPoolKey,const bool whichChannels[MAX_CHANNELS])
574 {
575 	ASSERT_RESIZE_LOCK
576 
577 	// just make sure that we will be able to add the channels without exceeding MAX_CHANNELS
578 	unsigned addCount=0;
579 	for(unsigned t=0;t<MAX_CHANNELS;t++)
580 		addCount+=whichChannels[t] ? 1 : 0;
581 	if((addCount+getChannelCount())>MAX_CHANNELS)
582 		throw(runtime_error(string(__func__)+" -- re-adding the channels specified by whichChannels would exceed the maximum of "+istring(MAX_CHANNELS)+" channels"));
583 
584 	// make sure all the temp audio pools exist for the channels specified by whichChannels and that they have the correct length
585 	// and note that the temp pool channel number would be as if it were at the end (k), because that's where it would have been when removed in moveChannelsToTemp
586 	int k=getChannelCount();
587 	for(unsigned t=0;t<MAX_CHANNELS;t++)
588 	{
589 		if(whichChannels[t])
590 		{
591 			if(!poolFile.containsPool(createTempAudioPoolName(tempAudioPoolKey,k)))
592 				throw(runtime_error(string(__func__)+" -- whichChannels specifies to add channel "+istring(t)+" from temp which does not exist as a temp audio pool"));
593 			if(getTempAudio(tempAudioPoolKey,k).getSize()!=getLength())
594 				throw(runtime_error(string(__func__)+" -- the length of the audio in this sound object is not the same now as it was when moveChannelsToTemp() was called"));
595 			k++;
596 		}
597 	}
598 
599 
600 	// move the temp audio pools back as channels at the end of the sound
601 	for(unsigned t=0;t<MAX_CHANNELS;t++)
602 	{
603 		if(whichChannels[t])
604 		{
605 			prvAddChannel(false,false);
606 			moveDataIntoChannel(tempAudioPoolKey,getChannelCount()-1,getChannelCount()-1,0,getLength(),true);
607 		}
608 	}
609 
610 	// through a series of swaps put the channels back in their original positions
611 	unsigned movedChannelIndex=getChannelCount()-addCount;
612 	for(unsigned t=0;t<getChannelCount();t++)
613 	{
614 		if(whichChannels[t])
615 		{
616 			for(unsigned i=movedChannelIndex;i>t;i--)
617 				swapChannels(i-1,i,0,getLength());
618 			movedChannelIndex++;
619 		}
620 	}
621 }
622 
623 
624 static const bool isAllChannels(CSound *sound,const bool whichChannels[MAX_CHANNELS])
625 {
626 	unsigned count=0;
627 	for(unsigned t=0;t<sound->getChannelCount();t++)
628 		if(whichChannels[t])
629 			count++;
630 	return(count==sound->getChannelCount());
631 }
632 
633 
634 void CSound::addSpace(sample_pos_t where,sample_pos_t length,bool doZeroData)
635 {
636 	ASSERT_RESIZE_LOCK
637 
638 	bool whichChannels[MAX_CHANNELS];
639 	for(unsigned t=0;t<MAX_CHANNELS;t++)
640 		whichChannels[t]=t<getChannelCount() ? true : false;
641 
642 	addSpace(whichChannels,where,length,doZeroData);
643 }
644 
645 void CSound::addSpace(const bool whichChannels[MAX_CHANNELS],sample_pos_t where,sample_pos_t length,bool doZeroData,sample_pos_t maxLength)
646 {
647 	ASSERT_RESIZE_LOCK
648 
649 	if(where>size)
650 		throw(runtime_error(string(__func__)+" -- where parameter out of range: "+istring(where)));
651 /*
652 	if(length>MAX_LENGTH)
653 		throw(runtime_error(string(__func__)+" -- length parameter out of range: "+istring(length)));
654 */
655 
656 	for(unsigned t=0;t<getChannelCount();t++)
657 	{
658 		if(whichChannels[t])
659 			addSpaceToChannel(t,where,length,doZeroData);
660 	}
661 
662 	if(isAllChannels(this,whichChannels))
663 		adjustCues(where,where+length);
664 
665 	matchUpChannelLengths(maxLength);
666 }
667 
668 void CSound::removeSpace(sample_pos_t where,sample_pos_t length)
669 {
670 	ASSERT_RESIZE_LOCK
671 
672 	bool whichChannels[MAX_CHANNELS];
673 	for(unsigned t=0;t<MAX_CHANNELS;t++)
674 		whichChannels[t]=t<getChannelCount() ? true : false;
675 
676 	removeSpace(whichChannels,where,length);
677 }
678 
679 void CSound::removeSpace(const bool whichChannels[MAX_CHANNELS],sample_pos_t where,sample_pos_t length,sample_pos_t maxLength)
680 {
681 	ASSERT_RESIZE_LOCK
682 
683 	if(where>size)
684 		throw(runtime_error(string(__func__)+" -- where parameter out of range: "+istring(where)));
685 	if(length>(size-where))
686 		throw(runtime_error(string(__func__)+" -- length parameter out of range: "+istring(length)));
687 
688 	for(unsigned t=0;t<channelCount;t++)
689 	{
690 		if(whichChannels[t])
691 			removeSpaceFromChannel(t,where,length);
692 	}
693 
694 	if(isAllChannels(this,whichChannels))
695 		adjustCues(where+length,where);
696 
697 	matchUpChannelLengths(maxLength);
698 }
699 
700 unsigned CSound::moveDataToTemp(const bool whichChannels[MAX_CHANNELS],sample_pos_t where,sample_pos_t length,sample_pos_t fudgeFactor,sample_pos_t maxLength)
701 {
702 	ASSERT_RESIZE_LOCK
703 
704 	return(moveDataToTempAndReplaceSpace(whichChannels,where,length,0,fudgeFactor));
705 }
706 
707 unsigned CSound::copyDataToTemp(const bool whichChannels[MAX_CHANNELS],sample_pos_t where,sample_pos_t length)
708 {
709 	ASSERT_SIZE_LOCK
710 
711 	if(where>size)
712 		throw(runtime_error(string(__func__)+" -- where parameter out of range: "+istring(where)));
713 	if(length>(size-where))
714 		throw(runtime_error(string(__func__)+" -- length parameter out of range: "+istring(length)));
715 
716 	const unsigned tempAudioPoolKey=tempAudioPoolKeyCounter++;
717 
718 	for(unsigned t=0;t<channelCount;t++)
719 	{
720 		if(whichChannels[t])
721 			copyDataFromChannel(tempAudioPoolKey,t,where,length);
722 	}
723 
724 	return(tempAudioPoolKey);
725 }
726 
727 unsigned CSound::moveDataToTempAndReplaceSpace(const bool whichChannels[MAX_CHANNELS],sample_pos_t where,sample_pos_t length,sample_pos_t replaceLength,sample_pos_t fudgeFactor,sample_pos_t maxLength)
728 {
729 	ASSERT_RESIZE_LOCK
730 
731 	if(where>size)
732 		throw(runtime_error(string(__func__)+" -- where parameter out of range: "+istring(where)));
733 	if(length>(size-where))
734 		throw(runtime_error(string(__func__)+" -- length parameter out of range: "+istring(length)));
735 /*
736 	if(replaceLength>MAX_LENGTH)
737 		throw(runtime_error(string(__func__)+" -- replaceLength parameter out of range: "+istring(replaceLength)));
738 */
739 
740 	const unsigned tempAudioPoolKey=tempAudioPoolKeyCounter++;
741 
742 	for(unsigned t=0;t<channelCount;t++)
743 	{
744 		if(whichChannels[t])
745 		{
746 			// move data
747 			moveDataOutOfChannel(tempAudioPoolKey,t,where,length);
748 
749 			// handle fudgeFactor
750 			appendForFudgeFactor(getTempDataInternal(tempAudioPoolKey,t),getAudioInternal(t),where,fudgeFactor);
751 
752 			// replace space
753 			addSpaceToChannel(t,where,replaceLength,false);
754 		}
755 	}
756 
757 	if(isAllChannels(this,whichChannels))
758 		adjustCues(where+length,where+replaceLength);
759 
760 	matchUpChannelLengths(maxLength);
761 
762 	return(tempAudioPoolKey);
763 }
764 
765 void CSound::moveDataFromTemp(const bool whichChannels[MAX_CHANNELS],unsigned tempAudioPoolKey,sample_pos_t moveWhere,sample_pos_t moveLength,bool removeTempAudioPools,sample_pos_t maxLength)
766 {
767 	ASSERT_RESIZE_LOCK
768 
769 	removeSpaceAndMoveDataFromTemp(whichChannels,0,0,tempAudioPoolKey,moveWhere,moveLength,removeTempAudioPools,maxLength);
770 }
771 
772 void CSound::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,sample_pos_t maxLength)
773 {
774 	ASSERT_RESIZE_LOCK
775 
776 	if(removeLength!=0 && (removeWhere>size || removeLength>(size-removeWhere)))
777 		throw(runtime_error(string(__func__)+" -- removeWhere/removeLength parameter out of range: "+istring(removeWhere)+"/"+istring(removeLength)));
778 	if(moveWhere>(size-removeLength))
779 		throw(runtime_error(string(__func__)+" -- moveWhere parameter out of range: "+istring(moveWhere)+" for removeWhere "+istring(removeLength)));
780 
781 	for(unsigned t=0;t<channelCount;t++)
782 	{
783 		if(whichChannels[t])
784 		{ // remove then add the space (by moving from temp pool)
785 			removeSpaceFromChannel(t,removeWhere,removeLength);
786 			moveDataIntoChannel(tempAudioPoolKey,t,t,moveWhere,moveLength,removeTempAudioPools);
787 		}
788 	}
789 
790 	/* ???
791 	if(isAllChannels(this,whichChannels))
792 		// I may need to handle this.... but as of now I don't because it's use only
793 		// for undo (and AAction takes care of restoring the cues) and it's a little
794 		// complicated since we can remove from one place and add to another
795 	*/
796 
797 	matchUpChannelLengths(maxLength);
798 }
799 
800 void CSound::removeTempAudioPools(unsigned tempAudioPoolKey)
801 {
802 	for(unsigned t=0;t<MAX_CHANNELS;t++)
803 	{
804 		const string tempAudioPoolName=createTempAudioPoolName(tempAudioPoolKey,t);
805 		if(poolFile.containsPool(tempAudioPoolName))
806 			poolFile.removePool(tempAudioPoolName);
807 	}
808 }
809 
810 void CSound::appendForFudgeFactor(CInternalRezPoolAccesser dest,const CInternalRezPoolAccesser src,sample_pos_t srcWhere,sample_pos_t fudgeFactor)
811 {
812 	if(fudgeFactor==0)
813 		return;
814 
815 	sample_pos_t srcLength=src.getSize();
816 
817 	const sample_pos_t l1=dest.getSize();
818 	dest.append(fudgeFactor);
819 
820 	sample_pos_t t;
821 	for(t=0;t<fudgeFactor && (t+srcWhere)<srcLength;t++)
822 		dest[t+l1]=src[t+srcWhere];
823 
824 	for(;t<fudgeFactor;t++) // 000
825 		dest[t+l1]=0;
826 }
827 
828 
829 void CSound::rotateLeft(const bool whichChannels[MAX_CHANNELS],const sample_pos_t start,const sample_pos_t stop,const sample_pos_t amount)
830 {
831 	ASSERT_RESIZE_LOCK
832 
833 	if(stop<start)
834 		throw(runtime_error(string(__func__)+" -- stop is less than start"));
835 	if(amount>(stop-start))
836 		throw(runtime_error(string(__func__)+" -- amount is greater than the distance between start and stop"));
837 
838 	for(unsigned int i=0;i<channelCount;i++)
839 	{
840 		if(whichChannels[i])
841 		{
842 			CInternalRezPoolAccesser accesser=getAudioInternal(i);
843 
844 			accesser.moveData(stop-amount+1,accesser,start,amount);
845 			invalidatePeakData(i,start,stop); // for want of a more efficient way (I already tried using move data on the peak accessers with the same parametesr as the previous call except /PEAK_CHUNK_SIZE, but error after many many operations was unacceptable)  ??? perhaps I need a flag which means 'how dirty' and it would absolutely recalculate it if it was just so dirty)
846 		}
847 	}
848 
849 }
850 
851 void CSound::rotateRight(const bool whichChannels[MAX_CHANNELS],const sample_pos_t start,const sample_pos_t stop,const sample_pos_t amount)
852 {
853 	ASSERT_RESIZE_LOCK
854 
855 	if(stop<start)
856 		throw(runtime_error(string(__func__)+" -- stop is less than start"));
857 	if(amount>(stop-start))
858 		throw(runtime_error(string(__func__)+" -- amount is greater than the distance between start and stop"));
859 
860 	for(unsigned int i=0;i<channelCount;i++)
861 	{
862 		if(whichChannels[i])
863 		{
864 			CInternalRezPoolAccesser accesser=getAudioInternal(i);
865 
866 			accesser.moveData(start,accesser,stop-amount+1,amount);
867 			invalidatePeakData(i,start,stop); // for want of a more efficient way
868 		}
869 	}
870 }
871 
872 
873 void CSound::swapChannels(unsigned channelA,unsigned channelB,const sample_pos_t where,const sample_pos_t length)
874 {
875 	ASSERT_RESIZE_LOCK
876 
877 	if(channelA>=getChannelCount())
878 		throw(runtime_error(string(__func__)+" -- channelA is out of range: "+istring(channelA)+">="+istring(getChannelCount())));
879 	if(channelB>=getChannelCount())
880 		throw(runtime_error(string(__func__)+" -- channelB is out of range: "+istring(channelB)+">="+istring(getChannelCount())));
881 
882 	if(where>size)
883 		throw(runtime_error(string(__func__)+" -- where parameter out of range: "+istring(where)));
884 	if(length>(size-where))
885 		throw(runtime_error(string(__func__)+" -- where/length parameter out of range: "+istring(where)+"/"+istring(length)));
886 
887 	if(channelA==channelB)
888 		return;
889 	if(length<=0)
890 		return;
891 
892 
893 	const unsigned tempAudioPoolKeyA=tempAudioPoolKeyCounter++;
894 	const unsigned tempAudioPoolKeyB=tempAudioPoolKeyCounter++;
895 
896 	// move data from each channel to its temp pool
897 	moveDataOutOfChannel(tempAudioPoolKeyA,channelA,where,length);
898 	moveDataOutOfChannel(tempAudioPoolKeyB,channelB,where,length);
899 
900 	// move the data back into the channel from each temp pool but swapped (and pass true to remove the temp pool)
901 	moveDataIntoChannel(tempAudioPoolKeyA,channelA,channelB,where,length,true);
902 	moveDataIntoChannel(tempAudioPoolKeyB,channelB,channelA,where,length,true);
903 }
904 
905 
906 void CSound::addSpaceToChannel(unsigned channel,sample_pos_t where,sample_pos_t length,bool doZeroData)
907 {
908 	if(length==0)
909 		return;
910 
911 	CInternalRezPoolAccesser accesser=getAudioInternal(channel);
912 
913 	const sample_pos_t peakChunkCountHave=peakChunkAccessers[channel]==NULL ? 0 : peakChunkAccessers[channel]->getSize();
914 	const sample_pos_t peakChunkCountNeeded=calcPeakChunkCount(accesser.getSize()+length);
915 
916 	// modify the audio data pools
917 	accesser.insert(where,length);
918 
919 	if(doZeroData)
920 		accesser.zeroData(where,length);
921 
922 	if(peakChunkAccessers[channel]!=NULL)
923 	{
924 		// add more peak chunks if the needed size is more than we have
925 		if(peakChunkCountNeeded>peakChunkCountHave)
926 		{
927 			const sample_pos_t insertWhere=where/PEAK_CHUNK_SIZE;
928 			const sample_pos_t insertCount=peakChunkCountNeeded-peakChunkCountHave;
929 
930 			peakChunkAccessers[channel]->insert(insertWhere,insertCount);
931 			for(sample_pos_t t=0;t<insertCount;t++)
932 				(*(peakChunkAccessers[channel]))[insertWhere+t].dirty=true;
933 		}
934 		else // just mark the one we inserted into as dirty
935 			(*(peakChunkAccessers[channel]))[where/PEAK_CHUNK_SIZE].dirty=true;
936 	}
937 }
938 
939 void CSound::removeSpaceFromChannel(unsigned channel,sample_pos_t where,sample_pos_t length)
940 {
941 	if(length==0)
942 		return;
943 
944 	CInternalRezPoolAccesser accesser=getAudioInternal(channel);
945 
946 	const sample_pos_t peakChunkCountHave=peakChunkAccessers[channel]==NULL ? 0 : peakChunkAccessers[channel]->getSize();
947 	const sample_pos_t peakChunkCountNeeded=calcPeakChunkCount(accesser.getSize()-length);
948 
949 	accesser.remove(where,length);
950 
951 	if(peakChunkAccessers[channel]!=NULL)
952 	{
953 		// modify the peak data pools if the size is dropping below the required size
954 		if(peakChunkCountHave>peakChunkCountNeeded)
955 		{
956 			const sample_pos_t removeWhere=where/PEAK_CHUNK_SIZE;
957 			const sample_pos_t removeCount=peakChunkCountHave-peakChunkCountNeeded;
958 
959 			peakChunkAccessers[channel]->remove(removeWhere,removeCount);
960 		}
961 
962 		// make the a the peak chunk at where recalculate
963 		if((where/PEAK_CHUNK_SIZE)<peakChunkAccessers[channel]->getSize())
964 			(*(peakChunkAccessers[channel]))[where/PEAK_CHUNK_SIZE].dirty=true;
965 	}
966 }
967 
968 void CSound::copyDataFromChannel(unsigned tempAudioPoolKey,unsigned channel,sample_pos_t where,sample_pos_t length)
969 {
970 	CInternalRezPoolAccesser destAccesser=createTempAudioPool(tempAudioPoolKey,channel);
971 
972 	if(length==0)
973 		return;
974 
975 	CInternalRezPoolAccesser srcAccesser=getAudioInternal(channel);
976 
977 	destAccesser.append(length);
978 	destAccesser.copyData(0,srcAccesser,where,length);
979 }
980 
981 void CSound::moveDataOutOfChannel(unsigned tempAudioPoolKey,unsigned channel,sample_pos_t where,sample_pos_t length)
982 {
983 	CInternalRezPoolAccesser destAccesser=createTempAudioPool(tempAudioPoolKey,channel);
984 
985 	if(length==0)
986 		return;
987 
988 	CInternalRezPoolAccesser srcAccesser=getAudioInternal(channel);
989 
990 	const sample_pos_t peakChunkCountHave=peakChunkAccessers[channel]==NULL ? 0 : peakChunkAccessers[channel]->getSize();
991 	const sample_pos_t peakChunkCountNeeded=calcPeakChunkCount(srcAccesser.getSize()-length);
992 
993 	destAccesser.moveData(0,srcAccesser,where,length);
994 
995 	if(peakChunkAccessers[channel]!=NULL)
996 	{
997 		// modify the peak data pools if the size is dropping below the required size
998 		if(peakChunkCountHave>peakChunkCountNeeded)
999 		{
1000 			const sample_pos_t removeWhere=where/PEAK_CHUNK_SIZE;
1001 			const sample_pos_t removeCount=peakChunkCountHave-peakChunkCountNeeded;
1002 
1003 			peakChunkAccessers[channel]->remove(removeWhere,removeCount);
1004 		}
1005 
1006 		// make the a the peak chunk at where recalculate
1007 		if((where/PEAK_CHUNK_SIZE)<peakChunkAccessers[channel]->getSize())
1008 			(*(peakChunkAccessers[channel]))[where/PEAK_CHUNK_SIZE].dirty=true;
1009 	}
1010 }
1011 
1012 void CSound::moveDataIntoChannel(unsigned tempAudioPoolKey,unsigned channelInTempPool,unsigned channelInAudio,sample_pos_t where,sample_pos_t length,bool removeTempAudioPool)
1013 {
1014 	if(length==0)
1015 		return;
1016 
1017 	CInternalRezPoolAccesser destAccesser=getAudioInternal(channelInAudio);
1018 
1019 	const sample_pos_t peakChunkCountHave=peakChunkAccessers[channelInAudio]==NULL ? 0 : peakChunkAccessers[channelInAudio]->getSize();
1020 	const sample_pos_t peakChunkCountNeeded=calcPeakChunkCount(destAccesser.getSize()+length);
1021 
1022 	CInternalRezPoolAccesser srcAccesser=getTempDataInternal(tempAudioPoolKey,channelInTempPool);
1023 	if(length>srcAccesser.getSize())
1024 		throw(runtime_error(string(__func__)+" -- length parameter out of range: "+istring(length)));
1025 
1026 	destAccesser.moveData(where,srcAccesser,0,length);
1027 
1028 	if(removeTempAudioPool)
1029 		poolFile.removePool(createTempAudioPoolName(tempAudioPoolKey,channelInTempPool));
1030 
1031 	if(peakChunkAccessers[channelInAudio]!=NULL)
1032 	{
1033 		// add more peak chunks if the needed size is more than we have
1034 		if(peakChunkCountNeeded>peakChunkCountHave)
1035 		{
1036 			const sample_pos_t insertWhere=where/PEAK_CHUNK_SIZE;
1037 			const sample_pos_t insertCount=peakChunkCountNeeded-peakChunkCountHave;
1038 
1039 			peakChunkAccessers[channelInAudio]->insert(insertWhere,insertCount);
1040 			for(sample_pos_t t=0;t<insertCount;t++)
1041 				(*(peakChunkAccessers[channelInAudio]))[insertWhere+t].dirty=true;
1042 		}
1043 		else // just mark the one we inserted into as dirty
1044 			(*(peakChunkAccessers[channelInAudio]))[where/PEAK_CHUNK_SIZE].dirty=true;
1045 	}
1046 
1047 }
1048 
1049 void CSound::silenceSound(unsigned channel,sample_pos_t where,sample_pos_t length,bool doInvalidatePeakData,bool showProgressBar)
1050 {
1051 	ASSERT_SIZE_LOCK
1052 
1053 	// ??? need a progress bar
1054 	getAudio(channel).zeroData(where,length);
1055 	if(doInvalidatePeakData)
1056 		invalidatePeakData(channel,where,where+length);
1057 }
1058 
1059 #include "DSP/TSoundStretcher.h"
1060 
1061 void CSound::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)
1062 {
1063 	ASSERT_SIZE_LOCK
1064 
1065 	if(srcSampleRate==0)
1066 		throw(runtime_error(string(__func__)+" -- srcSampleRate is 0"));
1067 
1068 	if(length==0)
1069 		return;
1070 
1071 	CRezPoolAccesser dest=getAudio(channel);
1072 	const sample_pos_t destOffset=where;
1073 	const unsigned destSampleRate=getSampleRate();
1074 
1075 #warning implement using sftChangeTempo now that I have a TTempoChanger DSP block
1076 
1077 	switch(mixMethod)
1078 	{
1079 	case mmOverwrite:
1080 		if(fitSrc!=sftNone)
1081 		{
1082 			if(fitSrc==sftChangeRate)
1083 			{
1084 				TSoundStretcher<const CRezPoolAccesser> srcStretcher(src,srcWhere,src.getSize()-srcWhere,length);
1085 				const sample_pos_t last=where+length;
1086 				if(showProgressBar)
1087 				{
1088 					CStatusBar statusBar(_("Copying/Fitting Data -- Channel ")+istring(channel),where,last);
1089 					for(sample_pos_t t=where;t<last;t++)
1090 					{
1091 						dest[t]=srcStretcher.getSample();
1092 						statusBar.update(t);
1093 					}
1094 				}
1095 				else
1096 				{
1097 					for(sample_pos_t t=where;t<last;t++)
1098 						dest[t]=srcStretcher.getSample();
1099 				}
1100 			}
1101 			else
1102 				throw runtime_error(string(__func__)+" -- unimplemented fitSrc type: "+istring(fitSrc));
1103 		}
1104 		else if(srcSampleRate!=destSampleRate)
1105 		{ // do sample rate conversion
1106 			TSoundStretcher<const CRezPoolAccesser> srcStretcher(src,srcWhere,(sample_pos_t)((sample_fpos_t)length/destSampleRate*srcSampleRate),length);
1107 			const sample_pos_t last=where+length;
1108 			if(showProgressBar)
1109 			{
1110 				CStatusBar statusBar(_("Copying Data -- Channel ")+istring(channel),where,last);
1111 				for(sample_pos_t t=where;t<last;t++)
1112 				{
1113 					dest[t]=srcStretcher.getSample();
1114 					statusBar.update(t);
1115 				}
1116 			}
1117 			else
1118 			{
1119 				for(sample_pos_t t=where;t<last;t++)
1120 					dest[t]=srcStretcher.getSample();
1121 			}
1122 		}
1123 		else
1124 		{
1125 			if((length/100)>0)
1126 			{
1127 				CStatusBar statusBar(_("Copying Data -- Channel ")+istring(channel),0,100,false);
1128 				for(sample_pos_t t=0;t<100;t++)
1129 				{
1130 					dest.copyData(destOffset+(t*(length/100)),src,srcWhere+(t*(length/100)),length/100);
1131 					statusBar.update(t);
1132 				}
1133 			}
1134 			dest.copyData(destOffset+(100*(length/100)),src,srcWhere+(100*(length/100)),length%100);
1135 		}
1136 
1137 		break;
1138 
1139 	case mmAdd:
1140 		if(fitSrc!=sftNone)
1141 		{
1142 			if(fitSrc==sftChangeRate)
1143 			{
1144 				TSoundStretcher<const CRezPoolAccesser> srcStretcher(src,srcWhere,src.getSize()-srcWhere,length);
1145 				const sample_pos_t last=where+length;
1146 				if(showProgressBar)
1147 				{
1148 					CStatusBar statusBar(_("Mixing/Fitting Data (add) -- Channel ")+istring(channel),where,last);
1149 					for(sample_pos_t t=where;t<last;t++)
1150 					{
1151 						dest[t]=ClipSample((mix_sample_t)dest[t]+(mix_sample_t)srcStretcher.getSample());
1152 						statusBar.update(t);
1153 					}
1154 				}
1155 				else
1156 				{
1157 					for(sample_pos_t t=where;t<last;t++)
1158 						dest[t]=ClipSample((mix_sample_t)dest[t]+(mix_sample_t)srcStretcher.getSample());
1159 				}
1160 			}
1161 			else
1162 				throw runtime_error(string(__func__)+" -- unimplemented fitSrc type: "+istring(fitSrc));
1163 		}
1164 		else if(srcSampleRate!=destSampleRate)
1165 		{
1166 			TSoundStretcher<const CRezPoolAccesser> srcStretcher(src,srcWhere,(sample_pos_t)((sample_fpos_t)length/destSampleRate*srcSampleRate),length);
1167 			const sample_pos_t last=where+length;
1168 			if(showProgressBar)
1169 			{
1170 				CStatusBar statusBar(_("Mixing Data (add) -- Channel ")+istring(channel),where,last);
1171 				for(sample_pos_t t=where;t<last;t++)
1172 				{
1173 					dest[t]=ClipSample((mix_sample_t)dest[t]+(mix_sample_t)srcStretcher.getSample());
1174 					statusBar.update(t);
1175 				}
1176 			}
1177 			else
1178 			{
1179 				for(sample_pos_t t=where;t<last;t++)
1180 					dest[t]=ClipSample((mix_sample_t)dest[t]+(mix_sample_t)srcStretcher.getSample());
1181 			}
1182 		}
1183 		else
1184 		{ // not fiting src and sample rates match
1185 			const sample_pos_t last=where+length;
1186 			if(showProgressBar)
1187 			{
1188 				CStatusBar statusBar(_("Mixing Data (add) -- Channel ")+istring(channel),where,last);
1189 				for(sample_pos_t t=where;t<last;t++)
1190 				{
1191 					dest[t]=ClipSample((mix_sample_t)dest[t]+(mix_sample_t)src[srcWhere++]);
1192 					statusBar.update(t);
1193 				}
1194 			}
1195 			else
1196 			{
1197 				for(sample_pos_t t=where;t<last;t++)
1198 					dest[t]=ClipSample((mix_sample_t)dest[t]+(mix_sample_t)src[srcWhere++]);
1199 			}
1200 		}
1201 
1202 		break;
1203 
1204 	case mmSubtract:
1205 		if(fitSrc!=sftNone)
1206 		{
1207 			if(fitSrc==sftChangeRate)
1208 			{
1209 				TSoundStretcher<const CRezPoolAccesser> srcStretcher(src,srcWhere,src.getSize()-srcWhere,length);
1210 				const sample_pos_t last=where+length;
1211 				if(showProgressBar)
1212 				{
1213 					CStatusBar statusBar(_("Mixing/Fitting Data (subtract) -- Channel ")+istring(channel),where,last);
1214 					for(sample_pos_t t=where;t<last;t++)
1215 					{
1216 						dest[t]=ClipSample((mix_sample_t)dest[t]-(mix_sample_t)srcStretcher.getSample());
1217 						statusBar.update(t);
1218 					}
1219 				}
1220 				else
1221 				{
1222 					for(sample_pos_t t=where;t<last;t++)
1223 						dest[t]=ClipSample((mix_sample_t)dest[t]-(mix_sample_t)srcStretcher.getSample());
1224 				}
1225 			}
1226 			else
1227 				throw runtime_error(string(__func__)+" -- unimplemented fitSrc type: "+istring(fitSrc));
1228 		}
1229 		else if(srcSampleRate!=destSampleRate)
1230 		{
1231 			TSoundStretcher<const CRezPoolAccesser> srcStretcher(src,srcWhere,(sample_pos_t)((sample_fpos_t)length/destSampleRate*srcSampleRate),length);
1232 			const sample_pos_t last=where+length;
1233 			if(showProgressBar)
1234 			{
1235 				CStatusBar statusBar(_("Mixing Data (subtract) -- Channel ")+istring(channel),where,last);
1236 				for(sample_pos_t t=where;t<last;t++)
1237 				{
1238 					dest[t]=ClipSample((mix_sample_t)dest[t]-(mix_sample_t)srcStretcher.getSample());
1239 					statusBar.update(t);
1240 				}
1241 			}
1242 			else
1243 			{
1244 				for(sample_pos_t t=where;t<last;t++)
1245 					dest[t]=ClipSample((mix_sample_t)dest[t]-(mix_sample_t)srcStretcher.getSample());
1246 			}
1247 		}
1248 		else
1249 		{ // not fiting src and sample rates match
1250 			const sample_pos_t last=where+length;
1251 			if(showProgressBar)
1252 			{
1253 				CStatusBar statusBar(_("Mixing Data (subtract) -- Channel ")+istring(channel),where,last);
1254 				for(sample_pos_t t=where;t<last;t++)
1255 				{
1256 					dest[t]=ClipSample((mix_sample_t)dest[t]-(mix_sample_t)src[srcWhere++]);
1257 					statusBar.update(t);
1258 				}
1259 			}
1260 			else
1261 			{
1262 				for(sample_pos_t t=where;t<last;t++)
1263 					dest[t]=ClipSample((mix_sample_t)dest[t]-(mix_sample_t)src[srcWhere++]);
1264 			}
1265 		}
1266 
1267 		break;
1268 
1269 	case mmMultiply:
1270 		if(fitSrc!=sftNone)
1271 		{
1272 			if(fitSrc==sftChangeRate)
1273 			{
1274 				TSoundStretcher<const CRezPoolAccesser> srcStretcher(src,srcWhere,src.getSize()-srcWhere,length);
1275 				const sample_pos_t last=where+length;
1276 				if(showProgressBar)
1277 				{
1278 					CStatusBar statusBar(_("Mixing/Fitting Data (multiply) -- Channel ")+istring(channel),where,last);
1279 					for(sample_pos_t t=where;t<last;t++)
1280 					{
1281 						dest[t]=ClipSample((mix_sample_t)dest[t]*(mix_sample_t)srcStretcher.getSample()/MAX_SAMPLE);
1282 						statusBar.update(t);
1283 					}
1284 				}
1285 				else
1286 				{
1287 					for(sample_pos_t t=where;t<last;t++)
1288 						dest[t]=ClipSample((mix_sample_t)dest[t]*(mix_sample_t)srcStretcher.getSample()/MAX_SAMPLE);
1289 				}
1290 			}
1291 			else
1292 				throw runtime_error(string(__func__)+" -- unimplemented fitSrc type: "+istring(fitSrc));
1293 		}
1294 		else if(srcSampleRate!=destSampleRate)
1295 		{
1296 			TSoundStretcher<const CRezPoolAccesser> srcStretcher(src,srcWhere,(sample_pos_t)((sample_fpos_t)length/destSampleRate*srcSampleRate),length);
1297 			const sample_pos_t last=where+length;
1298 			if(showProgressBar)
1299 			{
1300 				CStatusBar statusBar(_("Mixing Data (multiply) -- Channel ")+istring(channel),where,last);
1301 				for(sample_pos_t t=where;t<last;t++)
1302 				{
1303 					dest[t]=ClipSample((mix_sample_t)dest[t]*(mix_sample_t)srcStretcher.getSample()/MAX_SAMPLE);
1304 					statusBar.update(t);
1305 				}
1306 			}
1307 			else
1308 			{
1309 				for(sample_pos_t t=where;t<last;t++)
1310 					dest[t]=ClipSample((mix_sample_t)dest[t]*(mix_sample_t)srcStretcher.getSample()/MAX_SAMPLE);
1311 			}
1312 		}
1313 		else
1314 		{ // not fiting src and sample rates match
1315 			const sample_pos_t last=where+length;
1316 			if(showProgressBar)
1317 			{
1318 				CStatusBar statusBar(_("Mixing Data (multiply) -- Channel ")+istring(channel),where,last);
1319 				for(sample_pos_t t=where;t<last;t++)
1320 				{
1321 					dest[t]=ClipSample((mix_sample_t)dest[t]*(mix_sample_t)src[srcWhere++]/MAX_SAMPLE);
1322 					statusBar.update(t);
1323 				}
1324 			}
1325 			else
1326 			{
1327 				for(sample_pos_t t=where;t<last;t++)
1328 					dest[t]=ClipSample((mix_sample_t)dest[t]*(mix_sample_t)src[srcWhere++]/MAX_SAMPLE);
1329 			}
1330 		}
1331 
1332 		break;
1333 
1334 	case mmAverage:
1335 		if(fitSrc!=sftNone)
1336 		{
1337 			if(fitSrc==sftChangeRate)
1338 			{
1339 				TSoundStretcher<const CRezPoolAccesser> srcStretcher(src,srcWhere,src.getSize()-srcWhere,length);
1340 				const sample_pos_t last=where+length;
1341 				if(showProgressBar)
1342 				{
1343 					CStatusBar statusBar(_("Mixing/Fitting Data (average) -- Channel ")+istring(channel),where,last);
1344 					for(sample_pos_t t=where;t<last;t++)
1345 					{
1346 						dest[t]=((mix_sample_t)dest[t]+(mix_sample_t)srcStretcher.getSample())/2;
1347 						statusBar.update(t);
1348 					}
1349 				}
1350 				else
1351 				{
1352 					for(sample_pos_t t=where;t<last;t++)
1353 						dest[t]=((mix_sample_t)dest[t]+(mix_sample_t)srcStretcher.getSample())/2;
1354 				}
1355 			}
1356 			else
1357 				throw runtime_error(string(__func__)+" -- unimplemented fitSrc type: "+istring(fitSrc));
1358 		}
1359 		else if(srcSampleRate!=destSampleRate)
1360 		{
1361 			TSoundStretcher<const CRezPoolAccesser> srcStretcher(src,srcWhere,(sample_pos_t)((sample_fpos_t)length/destSampleRate*srcSampleRate),length);
1362 			const sample_pos_t last=where+length;
1363 			if(showProgressBar)
1364 			{
1365 				CStatusBar statusBar(_("Mixing Data (average) -- Channel ")+istring(channel),where,last);
1366 				for(sample_pos_t t=where;t<last;t++)
1367 				{
1368 					dest[t]=((mix_sample_t)dest[t]+(mix_sample_t)srcStretcher.getSample())/2;
1369 					statusBar.update(t);
1370 				}
1371 			}
1372 			else
1373 			{
1374 				for(sample_pos_t t=where;t<last;t++)
1375 					dest[t]=((mix_sample_t)dest[t]+(mix_sample_t)srcStretcher.getSample())/2;
1376 			}
1377 		}
1378 		else
1379 		{ // not fiting src and sample rates match
1380 			const sample_pos_t last=where+length;
1381 			if(showProgressBar)
1382 			{
1383 				CStatusBar statusBar(_("Mixing Data (average) -- Channel ")+istring(channel),where,last);
1384 				for(sample_pos_t t=where;t<last;t++)
1385 				{
1386 					dest[t]=((mix_sample_t)dest[t]+(mix_sample_t)src[srcWhere++])/2;
1387 					statusBar.update(t);
1388 				}
1389 			}
1390 			else
1391 			{
1392 				for(sample_pos_t t=where;t<last;t++)
1393 					dest[t]=((mix_sample_t)dest[t]+(mix_sample_t)src[srcWhere++])/2;
1394 			}
1395 		}
1396 
1397 		break;
1398 
1399 	default:
1400 		throw(runtime_error(string(__func__)+" -- unhandled mixMethod: "+istring(mixMethod)));
1401 	}
1402 
1403 	if(doInvalidatePeakData)
1404 		invalidatePeakData(channel,where,where+length);
1405 }
1406 
1407 
1408 
1409 
1410 
1411 
1412 const string CSound::getTimePosition(sample_pos_t samplePos,int secondsDecimalPlaces,bool includeUnits) const
1413 {
1414 	const sample_fpos_t sampleRate=getSampleRate();
1415 	const sample_fpos_t sTime=samplePos/sampleRate;
1416 
1417 	return seconds_to_string(sTime,secondsDecimalPlaces,includeUnits);
1418 }
1419 
1420 #include <stdio.h> // for sscanf
1421 const sample_pos_t CSound::getPositionFromTime(const string time,bool &wasInvalid) const
1422 {
1423 	wasInvalid=false;
1424 	sample_pos_t samplePos=0;
1425 
1426 	if(istring(time).count(':')==2)
1427 	{ // supposedly HH:MM:SS.sssss
1428 		unsigned h=0,m=0;
1429 		double s=0.0;
1430 				// ??? this may be a potential porting issue
1431 		sscanf(time.c_str()," %u:%u:%lf ",&h,&m,&s);
1432 		samplePos=(sample_pos_t)(((sample_fpos_t)h*3600.0+(sample_fpos_t)m*60.0+(sample_fpos_t)s)*(sample_fpos_t)getSampleRate());
1433 	}
1434 	else if(istring(time).count(':')==1)
1435 	{ // supposedly MM:SS.sssss
1436 		unsigned m=0;
1437 		double s=0.0;
1438 				// ??? this may be a potential porting issue
1439 		sscanf(time.c_str()," %u:%lf ",&m,&s);
1440 		samplePos=(sample_pos_t)(((sample_fpos_t)m*60.0+(sample_fpos_t)s)*(sample_fpos_t)getSampleRate());
1441 	}
1442 	else if(istring(time).count(':')==0)
1443 	{ // supposedly SSSS.sssss
1444 		double s=0.0;
1445 				// ??? this may be a potential porting issue
1446 		sscanf(time.c_str()," %lf ",&s);
1447 		samplePos=(sample_pos_t)(((sample_fpos_t)s)*(sample_fpos_t)getSampleRate());
1448 	}
1449 	else
1450 	{
1451 		wasInvalid=true;
1452 		samplePos=0;
1453 	}
1454 
1455 	return(samplePos);
1456 }
1457 
1458 const string CSound::getAudioDataSize(const sample_pos_t sampleCount) const
1459 {
1460 	sample_fpos_t audioDataSize=sampleCount*sizeof(sample_t)*channelCount;
1461 	if(audioDataSize>=1024*1024*1024)
1462 	{ // return as gb
1463 		return(istring(audioDataSize/1024.0/1024.0/1024.0,5,3)+"gb");
1464 	}
1465 	else if(audioDataSize>=1024*1024)
1466 	{ // return as mb
1467 		return(istring(audioDataSize/1024.0/1024.0,5,2)+"mb");
1468 	}
1469 	else if(audioDataSize>=1024)
1470 	{ // return as kb
1471 		return(istring(audioDataSize/1024.0,5,1)+"kb");
1472 	}
1473 	else
1474 	{ // return as b
1475 		return(istring((sample_pos_t)audioDataSize)+"b");
1476 	}
1477 }
1478 
1479 const string CSound::getAudioDataSize() const
1480 {
1481 	return(getAudioDataSize(isEmpty() ? 0 : getAudio(0).getSize()));
1482 }
1483 
1484 const string CSound::getPoolFileSize() const
1485 {
1486 	uint64_t iPoolFileSize=poolFile.getFileSize();
1487 	if(iPoolFileSize>=1024*1024*1024)
1488 	{ // return as gb
1489 		return(istring((long double)iPoolFileSize/1024.0/1024.0/1024.0,5,3)+"gb");
1490 	}
1491 	else if(iPoolFileSize>=1024*1024)
1492 	{ // return as mb
1493 		return(istring((long double)iPoolFileSize/1024.0/1024.0,5,2)+"mb");
1494 	}
1495 	else if(iPoolFileSize>=1024)
1496 	{ // return as kb
1497 		return(istring((long double)iPoolFileSize/1024.0,5,1)+"kb");
1498 	}
1499 	else
1500 	{ // return as b
1501 		return(istring(iPoolFileSize)+"b");
1502 	}
1503 }
1504 
1505 void CSound::defragPoolFile()
1506 {
1507 	lockForResize();
1508 	try
1509 	{
1510 		poolFile.defrag();
1511 		unlockForResize();
1512 	}
1513 	catch(...)
1514 	{
1515 		unlockForResize();
1516 		throw;
1517 	}
1518 }
1519 
1520 void CSound::printSAT()
1521 {
1522 	poolFile.printSAT();
1523 }
1524 
1525 void CSound::verifySAT()
1526 {
1527 	poolFile.verifyAllBlockInfo(false);
1528 }
1529 
1530 
1531 void CSound::flush()
1532 {
1533 	poolFile.flushData();
1534 }
1535 
1536 
1537 /*
1538 	Finds a working directory for a working file
1539 	for the given filename.  A suitable working
1540 	directory should be writable and have free
1541 	space of 110% of the given file's size
1542 */
1543 #if defined(rez_OS_SOLARIS)
1544         #include <sys/statvfs.h>
1545         #define statfs statvfs
1546 #elif defined(rez_OS_BSD)
1547 	#include <sys/param.h> /* I think there's a bug in sys/ucred.h because NGROUPS isn't defined by any previous include, so I include this to get it defined */
1548         #include <sys/mount.h>
1549 #elif defined(rez_OS_LINUX)
1550         #include <sys/statfs.h>
1551 #else
1552 	#error complier error imminent no xxxfs.h has been included
1553 #endif
1554 
1555 #include <stdio.h>
1556 #include <string.h>
1557 #include <errno.h>
1558 const string findWorkDir(const string filename)
1559 {
1560 	vector<string> workingDirs;
1561 		if(gPrimaryWorkDir!="" && CPath(gPrimaryWorkDir).isDirectory()) {
1562 			workingDirs.push_back(CPath(gPrimaryWorkDir).realPath());
1563 		}
1564 		workingDirs.push_back(CPath(filename).dirName()); // first try the dirname of the given filename
1565 		workingDirs.push_back(gFallbackWorkDir); // next try to fallback working dir (in the future, this may be a list)
1566 
1567 	for(size_t t=0;t<workingDirs.size();t++)
1568 	{
1569 		const string workDir=workingDirs[t];
1570 		const string testFilename=workDir+CPath::dirDelim+"rezrez842rezrez";
1571 		FILE *f=fopen(testFilename.c_str(),"wb");
1572 		if(f!=NULL)
1573 		{ // directry is writable
1574 			fclose(f);
1575 			remove(testFilename.c_str());
1576 
1577 			struct statfs s;
1578 			if(statfs(workDir.c_str(),&s)!=0)
1579 			{
1580 				int e=errno;
1581 				fprintf(stderr,"error getting free space on working directory candidate: %s -- %s\n",workDir.c_str(),strerror(e));
1582 				continue; // couldn't stat the fs for some reason
1583 			}
1584 
1585 				// ??? this would only be true if it's an uncompressed format
1586 				// I really need a way to know how big the working file will be after loading
1587 			const int64_t fsSize= (int64_t)s.f_bsize * (int64_t)s.f_bfree;
1588 			const int64_t fileSize=CPath(filename).getSize(false);
1589 
1590 			if(fsSize<(fileSize+(fileSize/10))) // ??? 10% overhead
1591 			{
1592 				fprintf(stderr,"insufficient free space in working directory candidate: %s\n",workDir.c_str());
1593 				continue; // not enough free space on that partition
1594 			}
1595 
1596 			return(workDir);
1597 		}
1598 		else
1599 		{
1600 			int e=errno;
1601 			fprintf(stderr,"cannot write to working directory candidate: %s -- %s\n",workDir.c_str(),strerror(e));
1602 		}
1603 	}
1604 
1605 	throw(runtime_error(string(__func__)+" -- no suitable working directory available to load the file: "+filename));
1606 }
1607 
1608 void CSound::createWorkingPoolFile(const string originalFilename,const unsigned _sampleRate,const unsigned _channelCount,const sample_pos_t _size)
1609 {
1610 	if(poolFile.isOpen())
1611 		throw(runtime_error(string(__func__)+" -- poolFile is already opened"));
1612 
1613 	if(_channelCount>MAX_CHANNELS)
1614 		throw(runtime_error(string(__func__)+" -- invalid number of channels: "+istring(_channelCount)));
1615 
1616 	channelCount=_channelCount;
1617 	sampleRate=_sampleRate;
1618 	size=_size;
1619 
1620 	// determine a suitable place for the working file
1621 	const string workDir=findWorkDir(originalFilename);
1622 
1623 	const string workingFilename=GET_WORKING_FILENAME(workDir,originalFilename);
1624 	PoolFile_t::removeFile(workingFilename);
1625 	poolFile.openFile(workingFilename,true);
1626 	removeAllTempAudioPools();
1627 
1628 	CFormatInfoPoolAccesser a=poolFile.createPool<RFormatInfo>(FORMAT_INFO_POOL_NAME);
1629 	metaInfoPoolID=poolFile.getPoolIdByName(FORMAT_INFO_POOL_NAME);
1630 	a.append(1);
1631 
1632 	// create an audio pool for each channel
1633 	for(unsigned t=0;t<channelCount;t++)
1634 	{
1635 		string poolName=AUDIO_POOL_NAME+istring(t+1);
1636 		CInternalRezPoolAccesser a1=poolFile.createPool<sample_t>(poolName);
1637 		channelPoolIDs[t]=poolFile.getPoolIdByName(poolName);
1638 		a1.append(size);
1639 	}
1640 
1641 	// create all dirty peakChunks after we know the data size
1642 	createPeakChunkAccessers();
1643 
1644 	createCueAccesser();
1645 
1646 /*??? maybe could speed things up by not zeroing the data here */
1647 	matchUpChannelLengths(NIL_SAMPLE_POS);
1648 
1649 	saveMetaInfo();
1650 }
1651 
1652 bool CSound::createFromWorkingPoolFileIfExists(const string originalFilename,bool promptIfFound)
1653 {
1654 	if(poolFile.isOpen())
1655 		throw(runtime_error(string(__func__)+" -- poolFile is already opened"));
1656 
1657 	try
1658 	{
1659 
1660 		vector<string> workingDirs;
1661 			workingDirs.push_back(CPath(originalFilename).dirName());
1662 			workingDirs.push_back(gFallbackWorkDir);
1663 
1664 		// look in all possible working spaces
1665 		string workingFilename="";
1666 		for(size_t t=0;t<workingDirs.size();t++)
1667 		{
1668 			const string f=GET_WORKING_FILENAME(workingDirs[t],originalFilename);
1669 				// ??? and we need to know that if this file is being used by any other loaded file.. then this is not the file and we need to alter the working filename at that point some how.. or just refuse to load the file at all
1670 			if(CPath(f).exists())
1671 			{
1672 				workingFilename=f;
1673 				break;
1674 			}
1675 		}
1676 
1677 		if(workingFilename=="")
1678 			return(false); // wasn't found
1679 
1680 		if(promptIfFound)
1681 		{
1682 			// ??? probably have a cancel button to avoid loaded the sound at all.. probably throw an exception of a different type which is an ESkipLoadingFile
1683 			if(Question(_("File: ")+workingFilename+"\n\n"+_("A temporary file was found indicating that this file was previously being edited when a crash occurred or the process was killed.\n\nDo you wish to attempt to recover from this temporary file (otherwise the file will be deleted)?"),yesnoQues)==noAns)
1684 			{
1685 				// ??? doesn't remove other files in the set of files if it was a multi file set >2gb
1686 				PoolFile_t::removeFile(workingFilename);
1687 				return(false);
1688 			}
1689 		}
1690 
1691 		poolFile.openFile(workingFilename,false);
1692 		_isModified=true;
1693 
1694 		removeAllTempAudioPools();
1695 		deletePeakChunkAccessers();
1696 
1697 		deleteCueAccesser();
1698 
1699 		// now that file is successfully opened, ask user if they want
1700 		// to try to pick up where they left off or forget all edit
1701 		// history
1702 
1703 		metaInfoPoolID=poolFile.getPoolIdByName(FORMAT_INFO_POOL_NAME);
1704 
1705 		// check version at the beginning of RFormat and perhaps handle things differently
1706 		uint32_t version=0xffffffff;
1707 		poolFile.readPoolRaw(metaInfoPoolID,&version,sizeof(version));
1708 		if(version==1)
1709 		{
1710 			const CFormatInfoPoolAccesser a=poolFile.getPoolAccesser<RFormatInfo>(metaInfoPoolID);
1711 			RFormatInfo r;
1712 			r=a[0];
1713 
1714 			if(r.size>MAX_LENGTH)
1715 			{
1716 				// ??? what should I do? truncate the sound or just error out?
1717 			}
1718 
1719 			size=r.size; // actually overwritten by matchUpChannelLengths
1720 			sampleRate=r.sampleRate;
1721 			channelCount=r.channelCount;
1722 		}
1723 		else
1724 			throw(runtime_error(string(__func__)+" -- unhandled format version: "+istring(version)+" in found working file: "+workingFilename));
1725 
1726 		if(channelCount<0 || channelCount>MAX_CHANNELS)
1727 		{
1728 			deletePeakChunkAccessers();
1729 			deleteCueAccesser();
1730 			poolFile.closeFile(false,false);
1731 			throw(runtime_error(string(__func__)+" -- invalid number of channels: "+istring(channelCount)+" in found working file: "+workingFilename));
1732 		}
1733 
1734 		for(unsigned t=0;t<channelCount;t++)
1735 			channelPoolIDs[t]=poolFile.getPoolIdByName(AUDIO_POOL_NAME+istring(t+1));
1736 
1737 
1738 		// just in case the channels have different lengths
1739 		matchUpChannelLengths(NIL_SAMPLE_POS);
1740 
1741 		// create all dirty peakChunks after we know the data size
1742 		createPeakChunkAccessers();
1743 
1744 		createCueAccesser();
1745 
1746 		saveMetaInfo();
1747 
1748 		return(true);
1749 	}
1750 	catch(...)
1751 	{
1752 		// remove the file if it was corrupt and just has a problem opening
1753 		deletePeakChunkAccessers();
1754 		deleteCueAccesser();
1755 		poolFile.closeFile(false,true);
1756 		return(false);
1757 	}
1758 }
1759 
1760 void CSound::setSampleRate(unsigned newSampleRate)
1761 {
1762 	sampleRate=newSampleRate;
1763 	saveMetaInfo();
1764 }
1765 
1766 void CSound::saveMetaInfo()
1767 {
1768 	if(!poolFile.isOpen())
1769 		throw(runtime_error(string(__func__)+" -- poolFile is not opened"));
1770 
1771 	CFormatInfoPoolAccesser b=poolFile.getPoolAccesser<RFormatInfo>(metaInfoPoolID);
1772 	if(b.getSize()==1)
1773 	{
1774 		RFormatInfo &r=b[0];
1775 
1776 		// always write the newest format
1777 		r.version=1;
1778 		r.size=size;
1779 		r.sampleRate=sampleRate;
1780 		r.channelCount=channelCount;
1781 	}
1782 	else
1783 	{
1784 		RFormatInfo r;
1785 
1786 		// always write the newest format
1787 		r.version=1;
1788 		r.size=size;
1789 		r.sampleRate=sampleRate;
1790 		r.channelCount=channelCount;
1791 
1792 		poolFile.clearPool(metaInfoPoolID);
1793 		poolFile.setPoolAlignment(metaInfoPoolID,sizeof(RFormatInfo));
1794 
1795 		CFormatInfoPoolAccesser b=poolFile.getPoolAccesser<RFormatInfo>(metaInfoPoolID);
1796 		b.append(1);
1797 		b[0]=r;
1798 	}
1799 
1800 	// really slows things down especially for recording
1801 	// flush();
1802 }
1803 
1804 void CSound::createPeakChunkAccessers()
1805 {
1806 	if(poolFile.isOpen())
1807 	{
1808 		sample_pos_t peakCount=calcPeakChunkCount(size);
1809 		for(unsigned i=0;i<channelCount;i++)
1810 		{
1811 			peakChunkAccessers[i]=new CPeakChunkRezPoolAccesser(poolFile.createPool<RPeakChunk>(PEAK_CHUNK_POOL_NAME+istring(i),false));
1812 			peakChunkAccessers[i]->clear();
1813 			peakChunkAccessers[i]->append(peakCount);
1814 			for(sample_pos_t t=0;t<peakCount;t++)
1815 				(*(peakChunkAccessers[i]))[t].dirty=true;
1816 		}
1817 	}
1818 }
1819 
1820 void CSound::deletePeakChunkAccessers()
1821 {
1822 	if(poolFile.isOpen())
1823 	{
1824 		for(unsigned t=0;t<MAX_CHANNELS;t++)
1825 		{
1826 			delete peakChunkAccessers[t];
1827 			peakChunkAccessers[t]=NULL;
1828 		}
1829 	}
1830 }
1831 
1832 // returns the number of peak chunks that there needs to be for the given size
1833 sample_pos_t CSound::calcPeakChunkCount(sample_pos_t givenSize)
1834 {
1835 	sample_pos_t v=((sample_pos_t)ceil(((sample_fpos_t)givenSize)/((sample_fpos_t)PEAK_CHUNK_SIZE)));
1836 	if(v<=0)
1837 		v=1;
1838 	return(v);
1839 }
1840 
1841 
1842 const string CSound::createTempAudioPoolName(unsigned tempAudioPoolKey,unsigned channel)
1843 {
1844 	return(TEMP_AUDIO_POOL_NAME+istring(tempAudioPoolKey)+"_"+istring(channel));
1845 }
1846 
1847 CSound::CInternalRezPoolAccesser CSound::createTempAudioPool(unsigned tempAudioPoolKey,unsigned channel)
1848 {
1849 	return(poolFile.createPool<sample_t>(createTempAudioPoolName(tempAudioPoolKey,channel)));
1850 }
1851 
1852 void CSound::removeAllTempAudioPools()
1853 {
1854 	for(size_t t=0;t<poolFile.getPoolIndexCount();t++)
1855 	{
1856 		const PoolFile_t::poolId_t poolId=poolFile.getPoolIdByIndex(t);
1857 		const string _poolName=poolFile.getPoolNameById(poolId);
1858 		const char *poolName=_poolName.c_str();
1859 			// ??? since I'm using strstr, string probably needs/has some string searching methods
1860 		if(strstr(poolName,TEMP_AUDIO_POOL_NAME)==poolName)
1861 		{
1862 			poolFile.removePool(poolId);
1863 			t--;
1864 		}
1865 	}
1866 }
1867 
1868 
1869 // appends silence to the end of any channel that is shorter than the longest one
1870 void CSound::matchUpChannelLengths(sample_pos_t maxLength,bool doZeroData)
1871 {
1872 /*
1873 	if(maxLength>MAX_LENGTH)
1874 		throw(runtime_error(string(__func__)+" -- invalid maxLength: "+istring(maxLength)));
1875 */
1876 
1877 	// get the max size of all the audio pools
1878 	sample_pos_t maxAudioPoolSize=0;
1879 	for(unsigned t=0;t<channelCount;t++)
1880 		maxAudioPoolSize=max(maxAudioPoolSize,getAudioInternal(t).getSize());
1881 
1882 	if(maxLength==NIL_SAMPLE_POS)
1883 	{
1884 		// add space to the end of any audio pool that is shorter
1885 		for(unsigned t=0;t<channelCount;t++)
1886 		{
1887 			const sample_pos_t channelSize=getAudioInternal(t).getSize();
1888 			if(channelSize<maxAudioPoolSize)
1889 				addSpaceToChannel(t,channelSize,maxAudioPoolSize-channelSize,doZeroData);
1890 		}
1891 	}
1892 	else
1893 	{
1894 		maxAudioPoolSize=min(maxAudioPoolSize,maxLength);
1895 
1896 		// add space to the end of any audio pool that is shorter and truncate ones that are longer
1897 		for(unsigned t=0;t<channelCount;t++)
1898 		{
1899 			const sample_pos_t channelSize=getAudioInternal(t).getSize();
1900 			if(channelSize>maxAudioPoolSize)
1901 				removeSpaceFromChannel(t,maxAudioPoolSize,channelSize-maxAudioPoolSize);
1902 			else if(channelSize<maxAudioPoolSize)
1903 				addSpaceToChannel(t,channelSize,maxAudioPoolSize-channelSize,doZeroData);
1904 		}
1905 	}
1906 
1907 	size=maxAudioPoolSize;
1908 	ensureNonZeroLength();
1909 	saveMetaInfo();
1910 }
1911 
1912 void CSound::ensureNonZeroLength()
1913 {
1914 	if(size<=0)
1915 	{
1916 		for(unsigned t=0;t<channelCount;t++)
1917 		{
1918 			CInternalRezPoolAccesser a=getAudioInternal(t);
1919 			a.clear();
1920 			a.append(1);
1921 			a[a.getSize()-1]=0;
1922 
1923 			// I would also do it for peakChunkAccessers, but I handled that
1924 			// by always insisting on at least 1 chunk in calcPeakChunkCount
1925 		}
1926 		size=1;
1927 	}
1928 }
1929 
1930 void CSound::setIsModified(bool v)
1931 {
1932 	_isModified=v;
1933 }
1934 
1935 const bool CSound::isModified() const
1936 {
1937 	return(_isModified);
1938 }
1939 
1940 
1941 
1942 
1943 // -----------------------------------------------------
1944 // --- Cue Methods -------------------------------------
1945 // -----------------------------------------------------
1946 
1947 const size_t CSound::getCueCount() const
1948 {
1949 	return(cueAccesser->getSize());
1950 }
1951 
1952 const string CSound::getCueName(size_t index) const
1953 {
1954 	if(index>=getCueCount())
1955 		throw runtime_error(string(__func__)+" -- index is out of bounds: "+istring(index));
1956 	return((*cueAccesser)[index].name);
1957 }
1958 
1959 const sample_pos_t CSound::getCueTime(size_t index) const
1960 {
1961 	if(index>=getCueCount())
1962 		throw runtime_error(string(__func__)+" -- index is out of bounds: "+istring(index));
1963 	return((*cueAccesser)[index].time);
1964 }
1965 
1966 void CSound::setCueTime(size_t index,sample_pos_t newTime)
1967 {
1968 	if(index>=getCueCount())
1969 		throw runtime_error(string(__func__)+" -- index is out of bounds: "+istring(index));
1970 	(*cueAccesser)[index].time=newTime;
1971 
1972 	// update cueIndex
1973 	rebuildCueIndex();
1974 }
1975 
1976 const bool CSound::isCueAnchored(size_t index) const
1977 {
1978 	if(index>=getCueCount())
1979 		throw runtime_error(string(__func__)+" -- index is out of bounds: "+istring(index));
1980 	return((*cueAccesser)[index].isAnchored);
1981 }
1982 
1983 void CSound::addCue(const string &name,const sample_pos_t time,const bool isAnchored)
1984 {
1985 	if(name.size()>=MAX_SOUND_CUE_NAME_LENGTH-1)
1986 		throw(runtime_error(string(__func__)+" -- cue name too long"));
1987 
1988 	cueAccesser->append(1);
1989 	(*cueAccesser)[cueAccesser->getSize()-1]=RCue(name.c_str(),time,isAnchored);
1990 
1991 	// update cueIndex
1992 	cueIndex.insert(map<sample_pos_t,size_t>::value_type(time,cueAccesser->getSize()-1));
1993 }
1994 
1995 void CSound::insertCue(size_t index,const string &name,const sample_pos_t time,const bool isAnchored)
1996 {
1997 	if(name.size()>=MAX_SOUND_CUE_NAME_LENGTH-1)
1998 		throw(runtime_error(string(__func__)+" -- cue name too long"));
1999 	if(index>cueAccesser->getSize())
2000 		throw(runtime_error(string(__func__)+" -- invalid index: "+istring(index)));
2001 
2002 	cueAccesser->insert(index,1);
2003 	(*cueAccesser)[index]=RCue(name.c_str(),time,isAnchored);
2004 
2005 	// update cueIndex
2006 	rebuildCueIndex();
2007 }
2008 
2009 void CSound::removeCue(size_t index)
2010 {
2011 	if(index>=getCueCount())
2012 		throw runtime_error(string(__func__)+" -- index is out of bounds: "+istring(index));
2013 	cueAccesser->remove(index,1);
2014 
2015 	// update cueIndex
2016 	rebuildCueIndex();
2017 }
2018 
2019 size_t CSound::__default_cue_index;
2020 bool CSound::containsCue(const string &name,size_t &index) const
2021 {
2022 	for(size_t t=0;t<cueAccesser->getSize();t++)
2023 	{
2024 		if(strncmp((*cueAccesser)[t].name,name.c_str(),MAX_SOUND_CUE_NAME_LENGTH)==0)
2025 		{
2026 			index=t;
2027 			return true;
2028 		}
2029 	}
2030 	return false;
2031 }
2032 
2033 bool CSound::findCue(const sample_pos_t time,size_t &index) const
2034 {
2035 	const map<sample_pos_t,size_t>::const_iterator i=cueIndex.find(time);
2036 
2037 	if(i!=cueIndex.end())
2038 	{
2039 		index=i->second;
2040 		return true;
2041 	}
2042 	else
2043 		return false;
2044 }
2045 
2046 bool CSound::findNearestCue(const sample_pos_t time,size_t &index,sample_pos_t &distance) const
2047 {
2048 	if(cueIndex.empty())
2049 		return false;
2050 
2051 	map<sample_pos_t,size_t>::const_iterator i=cueIndex.lower_bound(time);
2052 
2053 	if(i!=cueIndex.begin())
2054 	{
2055 		if(i==cueIndex.end())
2056 		{
2057 			i--; // go back one
2058 			index=i->second;
2059 			distance=(sample_pos_t)sample_fpos_fabs((sample_fpos_t)time-(sample_fpos_t)i->first);
2060 		}
2061 		else
2062 		{
2063 			map<sample_pos_t,size_t>::const_iterator pi=i;
2064 			pi--;
2065 
2066 			const sample_pos_t d1=(sample_pos_t)sample_fpos_fabs((sample_fpos_t)time-(sample_fpos_t)pi->first);
2067 			const sample_pos_t d2=(sample_pos_t)sample_fpos_fabs((sample_fpos_t)time-(sample_fpos_t)i->first);
2068 
2069 			if(d1<d2)
2070 			{
2071 				index=pi->second;
2072 				distance=d1;
2073 			}
2074 			else
2075 			{
2076 				index=i->second;
2077 				distance=d2;
2078 			}
2079 		}
2080 	}
2081 	else
2082 	{
2083 		index=i->second;
2084 		distance=(sample_pos_t)sample_fpos_fabs((sample_fpos_t)time-(sample_fpos_t)i->first);
2085 	}
2086 
2087 	return true;
2088 }
2089 
2090 bool CSound::findPrevCue(const sample_pos_t time,size_t &index) const
2091 {
2092 	map<sample_pos_t,size_t>::const_iterator i=cueIndex.find(time);
2093 	if(i==cueIndex.end())
2094 		return false;
2095 
2096 	if(i!=cueIndex.begin())
2097 		i--;
2098 	index=i->second;
2099 	return true;
2100 }
2101 
2102 bool CSound::findNextCue(const sample_pos_t time,size_t &index) const
2103 {
2104 	map<sample_pos_t,size_t>::const_iterator i=cueIndex.find(time);
2105 	if(i==cueIndex.end())
2106 		return false;
2107 
2108 	i++;
2109 	if(i==cueIndex.end())
2110 		return false;
2111 	index=i->second;
2112 	return true;
2113 }
2114 
2115 bool CSound::findPrevCueInTime(const sample_pos_t time,size_t &index) const
2116 {
2117 	if(cueIndex.empty())
2118 		return false;
2119 
2120 	// upper_bound returns the element that is greater than the position where 'time' would be inserted if it were
2121 	map<sample_pos_t,size_t>::const_iterator i=cueIndex.upper_bound(time);
2122 
2123 	if(i==cueIndex.end())
2124 	{
2125 		i--;
2126 		index=i->second;
2127 		return true; // ok return the last cue in time
2128 	}
2129 	else
2130 	{
2131 		if(i==cueIndex.begin())
2132 			return false; // ok, so time is are prior to all cues
2133 		i--;
2134 		index=i->second;
2135 		return true; // ok we're at the cue prior to the one upper_bound found
2136 	}
2137 }
2138 
2139 bool CSound::findNextCueInTime(const sample_pos_t time,size_t &index) const
2140 {
2141 	// upper_bound returns the element that is greater than the position where 'time' would be inserted if it were
2142 	map<sample_pos_t,size_t>::const_iterator i=cueIndex.upper_bound(time);
2143 
2144 	if(i!=cueIndex.end())
2145 	{
2146 		index=i->second;
2147 		return true;
2148 	}
2149 	else
2150 		return false;
2151 }
2152 
2153 const string CSound::getUnusedCueName(const string &prefix) const
2154 {
2155 	// ??? containsCue is not the most efficient, but we're not talking about huge amounts of data here
2156 	for(unsigned t=1;t<200;t++)
2157 	{
2158 		if(!containsCue(prefix+istring(t)))
2159 			return prefix+istring(t);
2160 	}
2161 	return "";
2162 }
2163 
2164 void CSound::clearCues()
2165 {
2166 	cueAccesser->clear();
2167 
2168 	// update cueIndex
2169 	cueIndex.clear();
2170 }
2171 
2172 void CSound::enableCueAdjustmentsOnSpaceChanges(bool enabled)
2173 {
2174 	adjustCuesOnSpaceChanges=enabled;
2175 }
2176 
2177 /*
2178  * This method handles the adjustment of cues
2179  * pos1 can be less than pos2 indicating an addition of space at pos1 for pos2-pos1 samples
2180  * or pos2 can be less then pos1 indicating a removal of space as pos2 for pos1-pos2 samples
2181  */
2182 void CSound::adjustCues(const sample_pos_t pos1,const sample_pos_t pos2)
2183 {
2184 	if(!adjustCuesOnSpaceChanges)
2185 		return;
2186 
2187 	if(pos1<pos2)
2188 	{ // added data
2189 		sample_pos_t addedLength=pos2-pos1;
2190 		for(size_t t=0;t<getCueCount();t++)
2191 		{
2192 			if(isCueAnchored(t))
2193 				continue; // ignore
2194 
2195 			if(getCueTime(t)>=pos1)
2196 				setCueTime(t,getCueTime(t)+addedLength);
2197 		}
2198 	}
2199 	else // if(pos2<=pos1)
2200 	{ // removed data
2201 		sample_pos_t removedLength=pos1-pos2;
2202 		for(size_t t=0;t<getCueCount();t++)
2203 		{
2204 			if(isCueAnchored(t))
2205 				continue; // ignore
2206 
2207 			if(getCueTime(t)>pos2 && getCueTime(t)<pos1)
2208 				removeCue(t--);
2209 			else if(getCueTime(t)>=pos1)
2210 				setCueTime(t,getCueTime(t)-removedLength);
2211 		}
2212 
2213 	}
2214 
2215 	// update cueIndex
2216 	rebuildCueIndex();
2217 }
2218 
2219 void CSound::createCueAccesser()
2220 {
2221 	if(poolFile.isOpen())
2222 	{
2223 		poolFile.createPool<RCue>(CUES_POOL_NAME,false);
2224 		cueAccesser=new CCuePoolAccesser(poolFile.getPoolAccesser<RCue>(CUES_POOL_NAME));
2225 
2226 		// update cueIndex
2227 		rebuildCueIndex();
2228 	}
2229 }
2230 
2231 void CSound::deleteCueAccesser()
2232 {
2233 	if(poolFile.isOpen())
2234 	{
2235 		delete cueAccesser;
2236 		cueAccesser=NULL;
2237 
2238 		// update cueIndex
2239 		cueIndex.clear();
2240 	}
2241 }
2242 
2243 void CSound::rebuildCueIndex()
2244 {
2245 	cueIndex.clear();
2246 
2247 	for(size_t t=0;t<cueAccesser->getSize();t++)
2248 		cueIndex.insert(map<sample_pos_t,size_t>::value_type(getCueTime(t),t));
2249 }
2250 
2251 // -----------------------------------------------------
2252 // -----------------------------------------------------
2253 // -----------------------------------------------------
2254 
2255 
2256 const string CSound::getUserNotes() const
2257 {
2258 	if(poolFile.containsPool(NOTES_POOL_NAME))
2259 	{
2260 		const TStaticPoolAccesser<char,PoolFile_t> a=poolFile.getPoolAccesser<char>(NOTES_POOL_NAME);
2261 
2262 		string s;
2263 
2264 		char buffer[101];
2265 		for(size_t t=0;t<a.getSize()/100;t++)
2266 		{
2267 			a.read(buffer,100);
2268 			buffer[100]=0;
2269 			s+=buffer;
2270 		}
2271 
2272 		a.read(buffer,a.getSize()%100);
2273 		buffer[a.getSize()%100]=0;
2274 		s+=buffer;
2275 
2276 		return(s);
2277 	}
2278 	else
2279 		return("");
2280 }
2281 
2282 void CSound::setUserNotes(const string &notes)
2283 {
2284 	TPoolAccesser<char,PoolFile_t> a=poolFile.containsPool(NOTES_POOL_NAME) ? poolFile.getPoolAccesser<char>(NOTES_POOL_NAME) : poolFile.createPool<char>(NOTES_POOL_NAME);
2285 
2286 	a.clear();
2287 	a.write(notes.c_str(),notes.size());
2288 }
2289 
2290 
2291 // this is the explicit instantiation of the TPoolFile for CSound's purposes
2292 // #include <TPoolFile.cpp>
2293 template class TPoolFile<sample_pos_t,uint64_t>;
2294 
2295 // Some explicit template method instantiations (not sure why some are necessary and some aren't)
2296 
2297 /* I'm not sure why, but when I enable float instead of int16_t as the native audio type, it complains that these methods weren't instantiated anywhere when linking .. fine, but I'm not explicitly instantiating them when the type if int16_t either */
2298 template TStaticPoolAccesser<int16_t,TPoolFile<sample_pos_t,uint64_t> > TPoolFile<sample_pos_t,uint64_t>::createPool<int16_t>(const string, const bool);
2299 template TStaticPoolAccesser<int16_t,TPoolFile<sample_pos_t,uint64_t> > const TPoolFile<sample_pos_t,uint64_t>::getPoolAccesser<int16_t>(const string) const;
2300 
2301 
2302 #include <TStaticPoolAccesser.h>
2303 #include <TPoolAccesser.h>
2304 
2305 // I fairly certain that these are from calls to getGeneralDataAccesser<int>
2306 template void TPoolFile<sample_pos_t,uint64_t>::addAccesser<int>(TStaticPoolAccesser<int, TPoolFile<sample_pos_t,uint64_t> > const *);
2307 template void TPoolFile<sample_pos_t,uint64_t>::unreferenceCachedBlock<int>(TStaticPoolAccesser<int, TPoolFile<sample_pos_t,uint64_t> > const *);
2308 template void TPoolFile<sample_pos_t,uint64_t>::removeAccesser<int>(TStaticPoolAccesser<int, TPoolFile<sample_pos_t,uint64_t> > const *);
2309 template void TStaticPoolAccesser<int, TPoolFile<sample_pos_t,uint64_t> >::cacheBlock(const sample_pos_t) const;
2310 template void TPoolFile<sample_pos_t,uint64_t>::cacheBlock<int>(sample_pos_t, TStaticPoolAccesser<int, TPoolFile<sample_pos_t,uint64_t> > const *);
2311 
2312 template TStaticPoolAccesser<int, TPoolFile<sample_pos_t,uint64_t> > TPoolFile<sample_pos_t,uint64_t>::getPoolAccesser<int>(const string);
2313 template TStaticPoolAccesser<int, TPoolFile<sample_pos_t,uint64_t> > const TPoolFile<sample_pos_t,uint64_t>::getPoolAccesser<int>(const string) const;
2314 template TStaticPoolAccesser<int, TPoolFile<sample_pos_t,uint64_t> > TPoolFile<sample_pos_t,uint64_t>::createPool<int>(const string, const bool);
2315 
2316 template TStaticPoolAccesser<uint8_t, TPoolFile<sample_pos_t,uint64_t> > TPoolFile<sample_pos_t,uint64_t>::getPoolAccesser<uint8_t>(const string);
2317 template TStaticPoolAccesser<uint8_t, TPoolFile<sample_pos_t,uint64_t> > const TPoolFile<sample_pos_t,uint64_t>::getPoolAccesser<uint8_t>(const string) const;
2318 template TStaticPoolAccesser<uint8_t, TPoolFile<sample_pos_t,uint64_t> > TPoolFile<sample_pos_t,uint64_t>::createPool<uint8_t>(const string, const bool);
2319 
2320