/* Copyright (C) 1991-2007 and beyond by Bungie Studios, Inc. and the "Aleph One" developers. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. This license is contained in the file "COPYING", which is included with this source code; it is available online at http://www.gnu.org/licenses/gpl.html Handles uncompressed WAV and AIFF files */ #include "BasicIFFDecoder.h" #include "AStream.h" #include #include using std::vector; BasicIFFDecoder::BasicIFFDecoder() : sixteen_bit(false), stereo(false), signed_8bit(false), bytes_per_frame(0), rate(0), little_endian(false), length(0), data_offset(0) { } bool BasicIFFDecoder::Open(FileSpecifier& File) { if (!File.Open(file)) return false; SDL_RWops *music_rw = file.GetRWops(); uint32 sample_rate; // Read magic ID uint32 magic = SDL_ReadBE32(music_rw); if (magic == FOUR_CHARS_TO_INT('F', 'O', 'R', 'M')) { // Maybe an AIFF file, check further uint32 total_size = SDL_ReadBE32(music_rw); if (SDL_ReadBE32(music_rw) != FOUR_CHARS_TO_INT('A', 'I', 'F', 'F')) return false; // Seems so, look for COMM and SSND chunks bool comm_found = false; bool ssnd_found = false; do { // Read chunk ID and size uint32 id = SDL_ReadBE32(music_rw); uint32 size = SDL_ReadBE32(music_rw); int pos = SDL_RWtell(music_rw); switch (id) { case FOUR_CHARS_TO_INT('C', 'O', 'M', 'M'): { comm_found = true; little_endian = false; stereo = (SDL_ReadBE16(music_rw) == 2); SDL_RWseek(music_rw, 4, SEEK_CUR); sixteen_bit = (SDL_ReadBE16(music_rw) == 16); signed_8bit = true; uint32 srate = SDL_ReadBE32(music_rw); // This is a 6888x 80-bit floating point number, but we only read the first 4 bytes and try to guess the sample rate switch (srate) { case 0x400eac44: sample_rate = 44100; break; case 0x400dac44: sample_rate = 22050; break; case 0x400cac44: default: sample_rate = 11025; } break; } case FOUR_CHARS_TO_INT('S', 'S', 'N', 'D'): ssnd_found = true; length = size; SDL_RWseek(music_rw, 8, SEEK_CUR); data_offset = SDL_RWtell(music_rw); break; } // Skip to next chunk if (size & 1) size++; SDL_RWseek(music_rw, pos + size, SEEK_SET); } while (uint32(SDL_RWtell(music_rw)) < total_size); if (!comm_found) return false; if (!ssnd_found) return false; } else if (magic == FOUR_CHARS_TO_INT('R', 'I', 'F', 'F')) { // Maybe a WAV file, check further uint32 total_size = SDL_ReadLE32(music_rw); if (SDL_ReadBE32(music_rw) != FOUR_CHARS_TO_INT('W', 'A', 'V', 'E')) return false; // Seems so, look for fmt and data chunks bool fmt_found = false; bool data_found = false; do { // Read chunk ID and size uint32 id = SDL_ReadBE32(music_rw); uint32 size = SDL_ReadLE32(music_rw); int pos = SDL_RWtell(music_rw); switch (id) { case FOUR_CHARS_TO_INT('f', 'm', 't', ' '): fmt_found = true; little_endian = true; if (SDL_ReadLE16(music_rw) != 1) // PCM encoding return false; stereo = (SDL_ReadLE16(music_rw) == 2); sample_rate = SDL_ReadLE32(music_rw); SDL_RWseek(music_rw, 4, SEEK_CUR); bytes_per_frame = SDL_ReadLE16(music_rw); sixteen_bit = (SDL_ReadLE16(music_rw) == 16); signed_8bit = false; break; case FOUR_CHARS_TO_INT('d', 'a', 't', 'a'): data_found = true; length = size; data_offset = SDL_RWtell(music_rw); break; } // Skip to next chunk if (size & 1) size++; SDL_RWseek(music_rw, pos + size, SEEK_SET); } while (uint32(SDL_RWtell(music_rw)) < total_size); if (!fmt_found) return false; if (!data_found) return false; } else { return false; } bytes_per_frame = (stereo ? 2 : 1) * (sixteen_bit ? 2 : 1); rate = sample_rate; return true; } int32 BasicIFFDecoder::Decode(uint8* buffer, int32 max_length) { if (!file.IsOpen()) return 0; long position; file.GetPosition(position); int32 length = std::min(max_length, (int32) (data_offset + this->length - position)); if (length && file.Read(length, buffer)) { return length; } else { return 0; } } bool BasicIFFDecoder::Done() { if (!file.IsOpen()) return true; long position; file.GetPosition(position); return (position >= data_offset + length); } void BasicIFFDecoder::Rewind() { if (file.IsOpen()) file.SetPosition(data_offset); } void BasicIFFDecoder::Close() { length = 0; if (file.IsOpen()) file.Close(); }