2 * This file is part of ShapeFusion (Copyright 2000 Tito Dal Canton)
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.
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.
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
19 #include "wx/wxprec.h"
29 #if wxUSE_STD_IOSTREAM
30 #include "wx/ioswrap.h"
32 #include "wx/txtstrm.h"
34 #include "wx/filename.h"
37 #include "SoundsElements.h"
38 #include "../LittleEndianBuffer.h"
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))
47 sf_adapter(std::vector<unsigned char>& data) : data_(data), p_(data.begin()) { }
49 static sf_count_t get_filelen(void *pv) {
50 return ((sf_adapter*) pv)->_get_filelen();
53 static sf_count_t seek(sf_count_t offset, int whence, void *pv) {
54 return ((sf_adapter*) pv)->_seek(offset, whence);
57 static sf_count_t read(void *ptr, sf_count_t count, void *pv) {
58 return ((sf_adapter*) pv)->_read(ptr, count);
61 static sf_count_t write(const void *ptr, sf_count_t count, void *pv) {
62 return ((sf_adapter*) pv)->_write(ptr, count);
65 static sf_count_t tell(void *pv) {
66 return ((sf_adapter*) pv)->_tell();
70 std::vector<unsigned char>& data_;
71 std::vector<unsigned char>::iterator p_;
73 sf_count_t _get_filelen() {
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)
85 return ((p_ >= data_.begin() && p_ <= data_.end()) ? 0 : -1);
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);
92 for (; i < count && p_ < data_.end(); ++i)
100 sf_count_t _write(const void *ptr, sf_count_t count) {
101 if (p_ >= data_.end()) return -1;
103 const char *src = reinterpret_cast<const char *>(ptr);
105 for (; i < count && p_ < data_.end(); ++i)
114 return p_ - data_.begin();
119 AppleSoundHeader::AppleSoundHeader(bool verbose): SoundsElement(verbose)
123 AppleSoundHeader::~AppleSoundHeader()
127 bool AppleSoundHeader::operator==(const AppleSoundHeader& right) const
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);
140 BigEndianBuffer& AppleSoundHeader::LoadObject(BigEndianBuffer& buffer)
142 unsigned char headerType = buffer.Data()[buffer.Position() + 20];
143 switch (headerType) {
144 case standardSoundHeader:
151 buffer.ReadULong(); // sample pointer
153 int frames = buffer.ReadLong();
154 mSampleRate = buffer.ReadULong();
155 mLoopStart = buffer.ReadLong();
156 mLoopEnd = buffer.ReadLong();
158 buffer.ReadUChar(); // type
159 mBaseFrequency = buffer.ReadUChar();
161 mData.resize(frames);
162 buffer.ReadBlock(mData.size(), &mData[0]);
165 case extendedSoundHeader:
166 case compressedSoundHeader:
168 buffer.ReadULong(); // samplePtr
169 mStereo = (buffer.ReadLong() == 2);
170 mSampleRate = buffer.ReadULong();
171 mLoopStart = buffer.ReadLong();
172 mLoopEnd = buffer.ReadLong();
174 buffer.ReadUChar(); // type
175 mBaseFrequency = buffer.ReadUChar();
177 int frames = buffer.ReadLong();
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);
193 buffer.ReadShort(); // packetSize
194 buffer.ReadShort(); // unused
195 mSixteenBit = (buffer.ReadShort() == 16);
198 buffer.Position(buffer.Position() + 22);
199 mSixteenBit = (buffer.ReadShort() == 16);
200 buffer.Position(buffer.Position() + 14);
203 mBytesPerFrame = (mSixteenBit ? 2 : 1) * (mStereo ? 2 : 1);
204 mData.resize(frames * mBytesPerFrame);
205 buffer.ReadBlock(mData.size(), &mData[0]);
209 wxLogError(_("[AppleSoundHeader] Unknown header type %.2x."), headerType);
218 BigEndianBuffer& AppleSoundHeader::SaveObject(BigEndianBuffer& buffer)
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);
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);
243 buffer.WriteULong(0); // instrument chunks
244 buffer.WriteULong(0); // AESRecording
245 buffer.WriteShort(mSixteenBit ? 16 : 8);
246 buffer.WriteZeroes(14); // futureUse1 through futureUse4
248 buffer.WriteBlock(mData.size(), &mData[0]);
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]);
264 static const int kBufferSize = 8192;
269 SNDFILE_ptr(SNDFILE* file) : mFile(file) {}
270 ~SNDFILE_ptr() { if (mFile) sf_close(mFile); mFile = 0; }
271 SNDFILE* get() { return mFile; }
273 SNDFILE_ptr(const SNDFILE_ptr&);
274 SNDFILE_ptr& operator= (const SNDFILE_ptr&);
278 bool AppleSoundHeader::LoadFromFile(wxString path)
281 SNDFILE_ptr infile(sf_open(path.fn_str(), SFM_READ, &inputInfo));
283 wxLogError(_("[AppleSoundHeader] libsndfile could not open file."));
287 mSixteenBit = !(inputInfo.format & (SF_FORMAT_PCM_S8 | SF_FORMAT_PCM_U8));
288 if (inputInfo.samplerate <= 44100) {
289 mSampleRate = inputInfo.samplerate << 16;
291 mSampleRate = 44100 << 16;
293 mStereo = (inputInfo.channels >= 2);
295 mBytesPerFrame = (mSixteenBit ? 2 : 1) * (mStereo ? 2 : 1);
296 mLoopStart = mLoopEnd = 0;
300 outputInfo.samplerate = mSampleRate >> 16;
301 outputInfo.channels = mStereo ? 2 : 1;
303 outputInfo.format = SF_FORMAT_PCM_16 | SF_FORMAT_RAW | SF_ENDIAN_BIG;
305 outputInfo.format = SF_FORMAT_PCM_U8 | SF_FORMAT_RAW | SF_ENDIAN_BIG;
308 SF_VIRTUAL_IO virtual_io = {
309 &sf_adapter::get_filelen,
315 mData.resize(inputInfo.frames * mBytesPerFrame);
316 sf_adapter adapter(mData);
318 SNDFILE_ptr outfile(sf_open_virtual(&virtual_io, SFM_WRITE, &outputInfo, &adapter));
319 if (!outfile.get()) {
320 wxLogError(_("[AppleSoundHeader] libsndfile write error."));
324 int frames_remaining = inputInfo.frames;
325 while (frames_remaining > 0) {
326 int buf[kBufferSize * 2];
327 int frames = std::min(kBufferSize, frames_remaining);
329 if (sf_readf_int(infile.get(), buf, frames) != frames) {
330 wxLogError(_("[AppleSoundHeader] libsndfile read error."));
333 if (sf_writef_int(outfile.get(), buf, frames) != frames) {
334 wxLogError(_("[AppleSoundHeader] libsndfile write error."));
338 frames_remaining -= frames;
344 bool AppleSoundHeader::SaveToWaveOrAiff(wxString path, bool aiff)
350 inputFormat = outputFormat = SF_FORMAT_PCM_16;
351 } else if (mSigned) {
352 inputFormat = SF_FORMAT_PCM_S8;
353 outputFormat = SF_FORMAT_PCM_U8;
355 inputFormat = outputFormat = SF_FORMAT_PCM_U8;
359 outputInfo.samplerate = mSampleRate >> 16;
360 outputInfo.channels = mStereo ? 2 : 1;
362 outputInfo.format = SF_FORMAT_AIFF | outputFormat;
364 outputInfo.format = SF_FORMAT_WAV | outputFormat;
367 SNDFILE* outfile = sf_open(path.fn_str(), SFM_WRITE, &outputInfo);
369 SF_VIRTUAL_IO virtual_io = {
370 &sf_adapter::get_filelen,
376 sf_adapter adapter(mData);
379 inputInfo.samplerate = mSampleRate >> 16;
380 inputInfo.channels = mStereo ? 2 : 1;
381 inputInfo.format = SF_FORMAT_RAW | inputFormat | SF_ENDIAN_BIG;
383 SNDFILE* infile = sf_open_virtual(&virtual_io, SFM_READ, &inputInfo, &adapter);
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"));
395 if (sf_writef_int(outfile, buf, frames) != frames) {
396 wxLogError(_("[AppleSoundHeader] libsndfile write error"));
402 frames_remaining -= frames;
411 void AppleSoundHeader::PlaySound(void)
413 wxString tempfile = wxFileName::CreateTempFileName(wxT("sf")) + wxString(wxT(".wav"));
416 SaveToWave(tempfile);
417 wxSound(tempfile).Play(wxSOUND_SYNC);
418 wxRemoveFile(tempfile);
422 unsigned int AppleSoundHeader::Size(void)
424 if (mSixteenBit || mStereo || mSigned) {
425 // compressed or extended header
426 return mData.size() + 64;
428 return mData.size() + 22;
432 unsigned char* AppleSoundHeader::Data(void)
437 SoundsDefinition::SoundsDefinition(bool verbose): SoundsElement(verbose)
440 mBehaviorIndex = _sound_is_quiet;
446 SoundsDefinition::~SoundsDefinition()
450 bool SoundsDefinition::HaveSameAttributesAs(const SoundsDefinition& right) const
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));
460 bool SoundsDefinition::HaveSameSoundsAs(const SoundsDefinition& right) const
462 return (mSounds == right.mSounds);
465 bool SoundsDefinition::operator== (const SoundsDefinition& right) const
467 return (HaveSameAttributesAs(right) && HaveSameSoundsAs(right));
470 bool SoundsDefinition::operator!=(const SoundsDefinition& right) const
472 return (!HaveSameAttributesAs(right) || !HaveSameSoundsAs(right));
475 unsigned int SoundsDefinition::GetSizeInFile(void)
477 unsigned int size = SIZEOF_sound_definition;
479 for (unsigned int i = 0; i < mSounds.size(); i++)
480 size += mSounds[i].Size();
484 short SoundsDefinition::GetChance(void) const
486 for (int i = 0; i < 10; i++) {
487 if (mChance == 32768*i/10)
490 wxLogDebug(_("Invalid chance %d"), mChance);
494 void SoundsDefinition::SetChance(short chance)
496 if (chance < 0 || chance > 10)
497 wxLogDebug(_("Invalid chance %d"), mChance);
498 mChance = 32768*chance/10;
501 BigEndianBuffer& SoundsDefinition::SaveObject(BigEndianBuffer& buffer, unsigned int& offset)
503 unsigned int oldpos = buffer.Position();
505 // We write sound_definition header
506 buffer.WriteShort(mSoundCode);
508 buffer.WriteShort(mBehaviorIndex);
509 buffer.WriteUShort(mFlags);
511 buffer.WriteUShort(mChance);
513 float low_pitch_integer, low_pitch_fractional,
514 high_pitch_integer, high_pitch_fractional;
515 long low_pitch = 0, high_pitch = 0;
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;
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;
526 buffer.WriteLong(low_pitch);
527 buffer.WriteLong(high_pitch);
529 buffer.WriteShort(mSounds.size());
530 buffer.WriteUShort(mPermutationsPlayed);
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);
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();
544 // ... and write everything
545 buffer.WriteLong(offset);
546 buffer.WriteLong(single_length);
547 buffer.WriteLong(total_length);
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]);
557 buffer.WriteULong(mLastPlayed);
559 // Now, we write actual sound data where it belongs...
560 buffer.Position(offset);
562 for (unsigned int i = 0; i < mSounds.size(); i++) {
563 mSounds[i].SaveObject(buffer);
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;
575 BigEndianBuffer& SoundsDefinition::LoadObject(BigEndianBuffer& buffer)
577 mSoundCode = buffer.ReadShort();
579 mBehaviorIndex = buffer.ReadShort();
580 mFlags = buffer.ReadUShort();
582 mChance = buffer.ReadUShort();
584 if ((mBehaviorIndex > NUMBER_OF_SOUND_BEHAVIOR_DEFINITIONS) || (mChance > _ten_percent)) {
585 wxLogError(_("[SoundsDefinition] incorrect Behavior/Chance (%d/%d)"), mBehaviorIndex, mChance);
589 wxInt32 low_pitch_fixed, high_pitch_fixed;
591 low_pitch_fixed = buffer.ReadLong();
592 high_pitch_fixed = buffer.ReadLong();
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
597 short permutations = buffer.ReadShort();
599 if (permutations < 0 || permutations > MAXIMUM_PERMUTATIONS_PER_SOUND) {
600 wxLogError(_("[SoundsDefinition] incorrect permutation count : %d"), permutations);
604 mPermutationsPlayed = buffer.ReadUShort();
605 int groupOffset = buffer.ReadULong();
606 int singleLength = buffer.ReadULong();
607 int totalLength = buffer.ReadULong();
609 // Bug fix for RED Sounds : When groupOffset is out of bounds, consider sound empty.
613 if (permutations != 0 && (unsigned int)(groupOffset + totalLength) > buffer.Size()) {
614 wxLogError(_("[SoundsDefinition] incorrect group offset / total length (%d/%d)"), groupOffset, totalLength);
618 std::vector<long> soundOffsets;
620 soundOffsets.resize(MAXIMUM_PERMUTATIONS_PER_SOUND);
621 for (unsigned int i = 0; i < MAXIMUM_PERMUTATIONS_PER_SOUND; i++) {
622 soundOffsets[i] = buffer.ReadLong();
625 mLastPlayed = buffer.ReadULong();
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);
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();
646 for (short i = 0; i < permutations; i++) {
647 unsigned int size = 0;
648 if (permutations == 1)
650 else if (i == permutations - 1)
651 size = totalLength - soundOffsets[i];
653 size = soundOffsets[i + 1] - soundOffsets[i];
655 AppleSoundHeader sndbuffer(IsVerbose());
657 buffer.Position(groupOffset + soundOffsets[i]);
658 sndbuffer.LoadObject(buffer);
659 if (sndbuffer.IsGood()) {
660 mSounds.push_back(sndbuffer);
667 buffer.Position(oldpos);
673 AppleSoundHeader* SoundsDefinition::GetPermutation(unsigned int permutation_index)
675 if (permutation_index >= mSounds.size())
677 return &mSounds[permutation_index];
680 void SoundsDefinition::DeletePermutation(unsigned int permutation_index)
682 mSounds.erase(mSounds.begin() + permutation_index);
685 AppleSoundHeader* SoundsDefinition::NewPermutation(wxString path)
687 if (mSounds.size() >= MAXIMUM_PERMUTATIONS_PER_SOUND) {
691 AppleSoundHeader header(IsVerbose());
692 if (header.LoadFromFile(path)) {
693 mSounds.push_back(header);
694 return &mSounds.back();