OSDN Git Service

ゴミデーターを削除
[marathon/ShapeFusion.git] / Sounds / SoundsElements.cpp
1 /*
2  * This file is part of ShapeFusion (Copyright 2000 Tito Dal Canton)
3  *
4  * ShapeFusion is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * ShapeFusion is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with ShapeFusion; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18
19 #include "wx/wxprec.h"
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 #ifndef WX_PRECOMP
26 #include "wx/wx.h"
27 #endif
28
29 #if wxUSE_STD_IOSTREAM
30     #include "wx/ioswrap.h"
31 #else
32     #include "wx/txtstrm.h"
33 #endif
34 #include "wx/filename.h"
35 #include "wx/sound.h"
36
37 #include "SoundsElements.h"
38 #include "../LittleEndianBuffer.h"
39
40 #include <sndfile.h>
41
42 #define FOUR_CHARS_TO_INT(a,b,c,d) (((unsigned int)(a) << 24) | ((unsigned int)(b) << 16) | ((unsigned int)(c) << 8) | (unsigned int)(d))
43
44 struct sf_adapter
45 {
46 public:
47         sf_adapter(std::vector<unsigned char>& data) : data_(data), p_(data.begin()) { }
48
49         static sf_count_t get_filelen(void *pv) {
50                 return ((sf_adapter*) pv)->_get_filelen();
51         }
52
53         static sf_count_t seek(sf_count_t offset, int whence, void *pv) {
54                 return ((sf_adapter*) pv)->_seek(offset, whence);
55         }
56
57         static sf_count_t read(void *ptr, sf_count_t count, void *pv) {
58                 return ((sf_adapter*) pv)->_read(ptr, count);
59         }
60
61         static sf_count_t write(const void *ptr, sf_count_t count, void *pv) {
62                 return ((sf_adapter*) pv)->_write(ptr, count);
63         }
64
65         static sf_count_t tell(void *pv) {
66                 return ((sf_adapter*) pv)->_tell();
67         }
68
69 private:
70         std::vector<unsigned char>& data_;
71         std::vector<unsigned char>::iterator p_;
72
73         sf_count_t _get_filelen() {
74                 return data_.size();
75         }
76
77         sf_count_t _seek(sf_count_t offset, int whence) {
78                 if (whence == SEEK_SET)
79                         p_ = data_.begin() + offset;
80                 else if (whence == SEEK_END)
81                         p_ = data_.end() - offset;
82                 else if (whence == SEEK_CUR)
83                         p_ += offset;
84
85                 return ((p_ >= data_.begin() && p_ <= data_.end()) ? 0 : -1);
86         }
87
88         sf_count_t _read(void *ptr, sf_count_t count) {
89                 if (p_ >= data_.end()) return -1;
90                 char *dst = reinterpret_cast<char *>(ptr);
91                 int i = 0;
92                 for (; i < count && p_ < data_.end(); ++i)
93                 {
94                         *(dst++) = *(p_++);
95                 }
96
97                 return i;
98         }
99
100         sf_count_t _write(const void *ptr, sf_count_t count) {
101                 if (p_ >= data_.end()) return -1;
102
103                 const char *src = reinterpret_cast<const char *>(ptr);
104                 int i = 0;
105                 for (; i < count && p_ < data_.end(); ++i)
106                 {
107                         *(p_++) = *(src++);
108                 }
109
110                 return i;
111         }
112
113         sf_count_t _tell() {
114                 return p_ - data_.begin();
115         }
116
117 };
118
119 AppleSoundHeader::AppleSoundHeader(bool verbose): SoundsElement(verbose)
120 {
121 }
122
123 AppleSoundHeader::~AppleSoundHeader()
124 {
125 }
126
127 bool AppleSoundHeader::operator==(const AppleSoundHeader& right) const
128 {
129         return (mSixteenBit == right.mSixteenBit &&
130                 mStereo == right.mStereo &&
131                 mSigned == right.mSigned &&
132                 mBytesPerFrame == right.mBytesPerFrame &&
133                 mSampleRate == right.mSampleRate &&
134                 mLoopStart == right.mLoopStart &&
135                 mLoopEnd == right.mLoopEnd &&
136                 mBaseFrequency == right.mBaseFrequency &&
137                 mData == right.mData);
138 }
139
140 BigEndianBuffer& AppleSoundHeader::LoadObject(BigEndianBuffer& buffer)
141 {
142         unsigned char headerType = buffer.Data()[buffer.Position() + 20];
143         switch (headerType) {
144                 case standardSoundHeader:
145                 {
146                         mBytesPerFrame = 1;
147                         mSigned = false;
148                         mStereo = false;
149                         mSixteenBit = false;
150
151                         buffer.ReadULong(); // sample pointer
152
153                         int frames = buffer.ReadLong();
154                         mSampleRate = buffer.ReadULong();
155                         mLoopStart = buffer.ReadLong();
156                         mLoopEnd = buffer.ReadLong();
157                         
158                         buffer.ReadUChar(); // type
159                         mBaseFrequency = buffer.ReadUChar();
160                         
161                         mData.resize(frames);
162                         buffer.ReadBlock(mData.size(), &mData[0]);
163                         break;
164                 }
165                 case extendedSoundHeader:
166                 case compressedSoundHeader:
167                 {
168                         buffer.ReadULong(); // samplePtr
169                         mStereo = (buffer.ReadLong() == 2);
170                         mSampleRate = buffer.ReadULong();
171                         mLoopStart = buffer.ReadLong();
172                         mLoopEnd = buffer.ReadLong();
173                         
174                         buffer.ReadUChar(); // type
175                         mBaseFrequency = buffer.ReadUChar();
176
177                         int frames = buffer.ReadLong();
178
179                         if (headerType == compressedSoundHeader) {
180                                 buffer.Position(buffer.Position() + 10); // AIFF rate
181                                 buffer.ReadULong(); // marker chunk
182                                 unsigned int format = buffer.ReadULong();
183                                 buffer.ReadULong(); // future use
184                                 buffer.ReadULong(); // stateVars
185                                 buffer.ReadULong(); // leftOverSamples
186                                 short comp_id = buffer.ReadShort();
187                                 if (format != FOUR_CHARS_TO_INT('t', 'w', 'o', 's') || comp_id != -1) {
188                                         wxLogError(_("[AppleSoundHeader] Unsupported compression format '%.4s.'"), &format);
189                                         mGoodData = false;
190                                         return buffer;
191                                 }
192                                 mSigned = true;
193                                 buffer.ReadShort(); // packetSize
194                                 buffer.ReadShort(); // unused
195                                 mSixteenBit = (buffer.ReadShort() == 16);
196                         } else {
197                                 mSigned = false;
198                                 buffer.Position(buffer.Position() + 22);
199                                 mSixteenBit = (buffer.ReadShort() == 16);
200                                 buffer.Position(buffer.Position() + 14);
201                         }
202                         
203                         mBytesPerFrame = (mSixteenBit ? 2 : 1) * (mStereo ? 2 : 1);
204                         mData.resize(frames * mBytesPerFrame);
205                         buffer.ReadBlock(mData.size(), &mData[0]);
206                         break;
207                 }
208                 default:
209                         wxLogError(_("[AppleSoundHeader] Unknown header type %.2x."), headerType);
210                         mGoodData = false;
211                         return buffer;
212         }
213
214         mGoodData = true;
215         return buffer;
216 }
217
218 BigEndianBuffer& AppleSoundHeader::SaveObject(BigEndianBuffer& buffer)
219 {
220         if (mSixteenBit || mStereo || mSigned) {
221                 // extended or compressed sound header
222                 buffer.WriteULong(0); // samplePtr
223                 buffer.WriteLong(mStereo ? 2 : 1);
224                 buffer.WriteULong(mSampleRate);
225                 buffer.WriteLong(mLoopStart);
226                 buffer.WriteLong(mLoopEnd);
227                 
228                 buffer.WriteUChar((mSigned && !mSixteenBit) ? compressedSoundHeader : extendedSoundHeader);
229                 buffer.WriteUChar(mBaseFrequency);
230                 buffer.WriteLong(mData.size() / mBytesPerFrame);
231                 buffer.WriteZeroes(10); // AIFF rate
232                 buffer.WriteULong(0); // marker chunk
233                 if (mSigned && !mSixteenBit) {
234                         buffer.Write4CharCode('t', 'w', 'o', 's');
235                         buffer.WriteLong(0); // futureUse2
236                         buffer.WriteULong(0); // stateVars
237                         buffer.WriteULong(0); // leftOverSamples
238                         buffer.WriteShort(-1); // compressionID
239                         buffer.WriteShort(0); // packetSize
240                         buffer.WriteShort(0); // synthID
241                         buffer.WriteShort(mSixteenBit ? 16 : 8);
242                 } else {
243                         buffer.WriteULong(0); // instrument chunks
244                         buffer.WriteULong(0); // AESRecording
245                         buffer.WriteShort(mSixteenBit ? 16 : 8);
246                         buffer.WriteZeroes(14); // futureUse1 through futureUse4
247                 }
248                 buffer.WriteBlock(mData.size(), &mData[0]);
249         } else {
250                 // standard sound header
251                 buffer.WriteULong(0); // sample ptr
252                 buffer.WriteLong(mData.size()); // frames
253                 buffer.WriteULong(mSampleRate);
254                 buffer.WriteLong(mLoopStart);
255                 buffer.WriteLong(mLoopEnd);
256                 buffer.WriteUChar(standardSoundHeader);
257                 buffer.WriteUChar(mBaseFrequency);
258                 buffer.WriteBlock(mData.size(), &mData[0]);
259         }
260
261         return buffer;
262 }
263
264 static const int kBufferSize = 8192;
265
266 // RAII for SNDFILE*
267 class SNDFILE_ptr {
268 public:
269         SNDFILE_ptr(SNDFILE* file) : mFile(file) {}
270         ~SNDFILE_ptr() { if (mFile) sf_close(mFile); mFile = 0; }
271         SNDFILE* get() { return mFile; }
272 private:
273         SNDFILE_ptr(const SNDFILE_ptr&);
274         SNDFILE_ptr& operator= (const SNDFILE_ptr&);
275         SNDFILE* mFile;
276 };
277
278 bool AppleSoundHeader::LoadFromFile(wxString path)
279 {
280         SF_INFO inputInfo;
281         SNDFILE_ptr infile(sf_open(path.fn_str(), SFM_READ, &inputInfo));
282         if (!infile.get()) {
283                 wxLogError(_("[AppleSoundHeader] libsndfile could not open file."));
284                 return false;
285         }
286
287         mSixteenBit = !(inputInfo.format & (SF_FORMAT_PCM_S8 | SF_FORMAT_PCM_U8));
288         if (inputInfo.samplerate <= 44100) {
289                 mSampleRate = inputInfo.samplerate << 16;
290         } else {
291                 mSampleRate = 44100 << 16;
292         }
293         mStereo = (inputInfo.channels >= 2);
294         mSigned = false;
295         mBytesPerFrame = (mSixteenBit ? 2 : 1) * (mStereo ? 2 : 1);
296         mLoopStart = mLoopEnd = 0;
297         mBaseFrequency = 60;
298         
299         SF_INFO outputInfo;
300         outputInfo.samplerate = mSampleRate >> 16;
301         outputInfo.channels = mStereo ? 2 : 1;
302         if (mSixteenBit) {
303                 outputInfo.format = SF_FORMAT_PCM_16 | SF_FORMAT_RAW | SF_ENDIAN_BIG;
304         } else {
305                 outputInfo.format = SF_FORMAT_PCM_U8 | SF_FORMAT_RAW | SF_ENDIAN_BIG;
306         }
307
308         SF_VIRTUAL_IO virtual_io = {
309                 &sf_adapter::get_filelen,
310                 &sf_adapter::seek,
311                 &sf_adapter::read,
312                 &sf_adapter::write,
313                 &sf_adapter::tell };
314         
315         mData.resize(inputInfo.frames * mBytesPerFrame);
316         sf_adapter adapter(mData);
317
318         SNDFILE_ptr outfile(sf_open_virtual(&virtual_io, SFM_WRITE, &outputInfo, &adapter));
319         if (!outfile.get()) {
320                 wxLogError(_("[AppleSoundHeader] libsndfile write error."));
321                 return false;
322         }
323
324         int frames_remaining = inputInfo.frames;
325         while (frames_remaining > 0) {
326                 int buf[kBufferSize * 2];
327                 int frames = std::min(kBufferSize, frames_remaining);
328                 
329                 if (sf_readf_int(infile.get(), buf, frames) != frames) {
330                         wxLogError(_("[AppleSoundHeader] libsndfile read error."));
331                         return false;
332                 }
333                 if (sf_writef_int(outfile.get(), buf, frames) != frames) {
334                         wxLogError(_("[AppleSoundHeader] libsndfile write error."));
335                         return false;
336                 }
337
338                 frames_remaining -= frames;
339         }
340
341         return true;
342 }
343
344 bool AppleSoundHeader::SaveToWaveOrAiff(wxString path, bool aiff)
345 {
346         int inputFormat;
347         int outputFormat;
348
349         if (mSixteenBit) {
350                 inputFormat = outputFormat = SF_FORMAT_PCM_16;
351         } else if (mSigned) {
352                 inputFormat = SF_FORMAT_PCM_S8;
353                 outputFormat = SF_FORMAT_PCM_U8;
354         } else {
355                 inputFormat = outputFormat = SF_FORMAT_PCM_U8;
356         }
357         
358         SF_INFO outputInfo;
359         outputInfo.samplerate = mSampleRate >> 16;
360         outputInfo.channels = mStereo ? 2 : 1;
361         if (aiff) {
362                 outputInfo.format = SF_FORMAT_AIFF | outputFormat;
363         } else {
364                 outputInfo.format = SF_FORMAT_WAV | outputFormat;
365         }
366         
367         SNDFILE* outfile = sf_open(path.fn_str(), SFM_WRITE, &outputInfo);
368
369         SF_VIRTUAL_IO virtual_io = {
370                 &sf_adapter::get_filelen,
371                 &sf_adapter::seek,
372                 &sf_adapter::read,
373                 &sf_adapter::write,
374                 &sf_adapter::tell };
375
376         sf_adapter adapter(mData);
377
378         SF_INFO inputInfo;
379         inputInfo.samplerate = mSampleRate >> 16;
380         inputInfo.channels = mStereo ? 2 : 1;
381         inputInfo.format = SF_FORMAT_RAW | inputFormat | SF_ENDIAN_BIG;
382
383         SNDFILE* infile = sf_open_virtual(&virtual_io, SFM_READ, &inputInfo, &adapter);
384
385         int frames_remaining = mData.size() / mBytesPerFrame;
386         while (frames_remaining) {
387                 int buf[kBufferSize * 2];
388                 int frames = std::min(kBufferSize, frames_remaining);
389                 if (sf_readf_int(infile, buf, frames) != frames) {
390                         wxLogError(_("[AppleSoundHeader] libsndfile read error"));
391                         sf_close(infile);
392                         sf_close(outfile);
393                         return false;
394                 }
395                 if (sf_writef_int(outfile, buf, frames) != frames) {
396                         wxLogError(_("[AppleSoundHeader] libsndfile write error"));
397                         sf_close(infile);
398                         sf_close(outfile);
399                         return false;
400                 }
401
402                 frames_remaining -= frames;
403         }
404         
405         sf_close(infile);
406         sf_close(outfile);
407
408         return true;
409 }
410
411 void AppleSoundHeader::PlaySound(void)
412 {
413         wxString        tempfile = wxFileName::CreateTempFileName(wxT("sf")) + wxString(wxT(".wav"));
414         
415         wxBeginBusyCursor();
416         SaveToWave(tempfile);
417         wxSound(tempfile).Play(wxSOUND_SYNC);
418         wxRemoveFile(tempfile);
419         wxEndBusyCursor();
420 }
421
422 unsigned int AppleSoundHeader::Size(void)
423 {
424         if (mSixteenBit || mStereo || mSigned) {
425                 // compressed or extended header
426                 return mData.size() + 64;
427         } else {
428                 return mData.size() + 22;
429         }
430 }
431
432 unsigned char* AppleSoundHeader::Data(void)
433 {
434         return &mData[0];
435 }
436
437 SoundsDefinition::SoundsDefinition(bool verbose): SoundsElement(verbose)
438 {
439         mSoundCode = -1;
440         mBehaviorIndex = _sound_is_quiet;
441         mFlags = 0;
442         mChance = _always;
443         mSounds.clear();
444 }
445
446 SoundsDefinition::~SoundsDefinition()
447 {
448 }
449
450 bool SoundsDefinition::HaveSameAttributesAs(const SoundsDefinition& right) const
451 {
452         return ((mSoundCode == right.mSoundCode) &&
453                         (mBehaviorIndex == right.mBehaviorIndex) &&
454                         (mFlags == right.mFlags) &&
455                         (mChance == right.mChance) &&
456                         (mLowPitch == right.mLowPitch) &&
457                         (mHighPitch == right.mHighPitch));
458 }
459
460 bool SoundsDefinition::HaveSameSoundsAs(const SoundsDefinition& right) const
461 {
462         return (mSounds == right.mSounds);
463 }
464
465 bool SoundsDefinition::operator== (const SoundsDefinition& right) const
466 {
467         return (HaveSameAttributesAs(right) && HaveSameSoundsAs(right));
468 }
469
470 bool SoundsDefinition::operator!=(const SoundsDefinition& right) const
471 {
472         return (!HaveSameAttributesAs(right) || !HaveSameSoundsAs(right));
473 }
474
475 unsigned int SoundsDefinition::GetSizeInFile(void)
476 {
477         unsigned int    size = SIZEOF_sound_definition;
478
479         for (unsigned int i = 0; i < mSounds.size(); i++)
480                 size += mSounds[i].Size();
481         return size;
482 }
483
484 short SoundsDefinition::GetChance(void) const
485 {
486         for (int i = 0; i < 10; i++) {
487                 if (mChance == 32768*i/10)
488                         return i;
489         }
490         wxLogDebug(_("Invalid chance %d"), mChance);
491         return -1;
492 }
493
494 void SoundsDefinition::SetChance(short chance)
495 {
496         if (chance < 0 || chance > 10)
497                 wxLogDebug(_("Invalid chance %d"), mChance);
498         mChance = 32768*chance/10;
499 }
500
501 BigEndianBuffer& SoundsDefinition::SaveObject(BigEndianBuffer& buffer, unsigned int& offset)
502 {
503         unsigned int oldpos = buffer.Position();
504         
505         // We write sound_definition header
506         buffer.WriteShort(mSoundCode);
507         
508         buffer.WriteShort(mBehaviorIndex);
509         buffer.WriteUShort(mFlags);
510         
511         buffer.WriteUShort(mChance);
512         
513         float   low_pitch_integer, low_pitch_fractional,
514                         high_pitch_integer, high_pitch_fractional;
515         long    low_pitch = 0, high_pitch = 0;
516         
517         // float to fixed
518         low_pitch_fractional = modff(mLowPitch, &low_pitch_integer);
519         low_pitch |= (((short)low_pitch_integer) << 16) & 0xffff0000;
520         low_pitch |= (short)roundf(low_pitch_fractional * 0xffff) & 0x0000ffff;
521         
522         high_pitch_fractional = modff(mHighPitch, &high_pitch_integer);
523         high_pitch |= (((short)high_pitch_integer) << 16) & 0xffff0000;
524         high_pitch |= (short)roundf(high_pitch_fractional * 0xffff) & 0x0000ffff;
525         
526         buffer.WriteLong(low_pitch);
527         buffer.WriteLong(high_pitch);
528         
529         buffer.WriteShort(mSounds.size());
530         buffer.WriteUShort(mPermutationsPlayed);
531         
532         // We need to recalculate those fields...
533         unsigned long single_length = (mSounds.size() >= 1 ? mSounds[0].Size() : 0);
534         unsigned long total_length = single_length;
535         std::vector<long> soundOffsets;
536         soundOffsets.push_back(0);
537         
538         // ... and the corresponding offsets ...
539         for (unsigned int i = 1; i < mSounds.size(); i++) {
540                 soundOffsets.push_back(total_length);
541                 total_length += mSounds[i].Size();
542         }
543         
544         // ... and write everything
545         buffer.WriteLong(offset);
546         buffer.WriteLong(single_length);
547         buffer.WriteLong(total_length);
548         
549         // We have to pad with zeroes, as engine always expect MAXIMUM_PERMUTATIONS_PER_SOUND sound offsets...
550         for (unsigned int i = 0; i < MAXIMUM_PERMUTATIONS_PER_SOUND; i++) {
551                 if (i < soundOffsets.size())
552                         buffer.WriteLong(soundOffsets[i]);
553                 else
554                         buffer.WriteLong(0);
555         }
556         
557         buffer.WriteULong(mLastPlayed);
558         
559         // Now, we write actual sound data where it belongs...
560         buffer.Position(offset);
561         
562         for (unsigned int i = 0; i < mSounds.size(); i++) {
563                 mSounds[i].SaveObject(buffer);
564         }
565         
566         // We put back position to the end of the written sound_definition...
567         buffer.Position(oldpos + SIZEOF_sound_definition);
568         // ... and add our total_length to the offset, so that next invocation
569         // writes its sound data at the correct place.
570         offset += total_length;
571         
572         return buffer;
573 }
574
575 BigEndianBuffer& SoundsDefinition::LoadObject(BigEndianBuffer& buffer)
576 {
577         mSoundCode = buffer.ReadShort();
578
579         mBehaviorIndex = buffer.ReadShort();
580         mFlags = buffer.ReadUShort();
581         
582         mChance = buffer.ReadUShort();
583         
584         if ((mBehaviorIndex > NUMBER_OF_SOUND_BEHAVIOR_DEFINITIONS) || (mChance > _ten_percent)) {
585                 wxLogError(_("[SoundsDefinition] incorrect Behavior/Chance (%d/%d)"), mBehaviorIndex, mChance);
586                 return buffer;
587         }
588         
589         wxInt32         low_pitch_fixed, high_pitch_fixed;
590         
591         low_pitch_fixed = buffer.ReadLong();
592         high_pitch_fixed = buffer.ReadLong();
593         
594         mLowPitch = ((low_pitch_fixed >> 16) & 0xffff) + (float)(low_pitch_fixed & 0xffff) / 65536.0;   // convert fixed point [0,1] to float
595         mHighPitch = ((high_pitch_fixed >> 16) & 0xffff) + (float)(high_pitch_fixed & 0xffff) / 65536.0;        // convert fixed point [0,1] to float
596
597         short permutations = buffer.ReadShort();
598         
599         if (permutations < 0 || permutations > MAXIMUM_PERMUTATIONS_PER_SOUND) {
600                 wxLogError(_("[SoundsDefinition] incorrect permutation count : %d"), permutations);
601                 return buffer;
602         }
603         
604         mPermutationsPlayed = buffer.ReadUShort();
605         int groupOffset = buffer.ReadULong();
606         int singleLength = buffer.ReadULong();
607         int totalLength = buffer.ReadULong();
608         
609         // Bug fix for RED Sounds : When groupOffset is out of bounds, consider sound empty.
610         if (groupOffset < 0)
611                 permutations = 0;
612         
613         if (permutations != 0 && (unsigned int)(groupOffset + totalLength) > buffer.Size()) {
614                 wxLogError(_("[SoundsDefinition] incorrect group offset / total length (%d/%d)"), groupOffset, totalLength);
615                 return buffer;
616         }
617         
618         std::vector<long> soundOffsets;
619         
620         soundOffsets.resize(MAXIMUM_PERMUTATIONS_PER_SOUND);
621         for (unsigned int i = 0; i < MAXIMUM_PERMUTATIONS_PER_SOUND; i++) {
622                 soundOffsets[i] = buffer.ReadLong();
623         }
624         
625         mLastPlayed = buffer.ReadULong();
626         
627         if (IsVerbose()) {
628                 wxLogDebug(_("[SoundsDefinition] Sound Code:                    %d"), mSoundCode);
629                 wxLogDebug(_("[SoundsDefinition] Behavior Index:                %d"), mBehaviorIndex);
630                 wxLogDebug(_("[SoundsDefinition] Flags:                         %d"), mFlags);
631                 wxLogDebug(_("[SoundsDefinition] Chance:                                %d"), mChance);
632                 wxLogDebug(_("[SoundsDefinition] Low Pitch:                     %f"), mLowPitch);
633                 wxLogDebug(_("[SoundsDefinition] High Pitch:                    %f"), mHighPitch);
634                 wxLogDebug(_("[SoundsDefinition] Permutations:          %d"), permutations);
635                 wxLogDebug(_("[SoundsDefinition] Permutations Played:   %d"), mPermutationsPlayed);
636                 wxLogDebug(_("[SoundsDefinition] Group Offset:          %d"), groupOffset);
637                 wxLogDebug(_("[SoundsDefinition] Single Length:         %d"), singleLength);
638                 wxLogDebug(_("[SoundsDefinition] Total Length:          %d"), totalLength);
639                 wxLogDebug(_("[SoundsDefinition] Last Played:                   %d"), mLastPlayed);
640         }
641         
642         // Now we load actual sound data
643         // We save our current position, 'coz we need to restore it at the end
644         unsigned int oldpos = buffer.Position();
645
646         for (short i = 0; i < permutations; i++) {
647                 unsigned int size = 0;
648                 if (permutations == 1)
649                         size = singleLength;
650                 else if (i == permutations - 1)
651                         size = totalLength - soundOffsets[i];
652                 else
653                         size = soundOffsets[i + 1] - soundOffsets[i];
654                 
655                 AppleSoundHeader sndbuffer(IsVerbose());
656
657                 buffer.Position(groupOffset + soundOffsets[i]);
658                 sndbuffer.LoadObject(buffer);
659                 if (sndbuffer.IsGood()) {
660                         mSounds.push_back(sndbuffer);
661                 } else {
662                         mGoodData = false;
663                         return buffer;
664                 }
665         }
666
667         buffer.Position(oldpos);
668         
669         mGoodData = true;
670         return buffer;
671 }
672
673 AppleSoundHeader* SoundsDefinition::GetPermutation(unsigned int permutation_index)
674 {
675         if (permutation_index >= mSounds.size())
676                 return NULL;
677         return &mSounds[permutation_index];
678 }
679
680 void SoundsDefinition::DeletePermutation(unsigned int permutation_index)
681 {
682         mSounds.erase(mSounds.begin() + permutation_index);
683 }
684
685 AppleSoundHeader* SoundsDefinition::NewPermutation(wxString path)
686 {
687         if (mSounds.size() >= MAXIMUM_PERMUTATIONS_PER_SOUND) {
688                 return NULL;
689         }
690
691         AppleSoundHeader header(IsVerbose());
692         if (header.LoadFromFile(path)) {
693                 mSounds.push_back(header);
694                 return &mSounds.back();
695         } else {
696                 return NULL;
697         }
698 }