/* * Copyright (C) 2004 Christopher John Purnell * cjp@lost.org.uk * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #if __BYTE_ORDER == __LITTLE_ENDIAN #define WAV_RIFF 0x46464952 #define WAV_WAVE 0x45564157 #define WAV_fmt 0x20746D66 #define WAV_data 0x61746164 #define WAV_smpl 0x6C706D73 #define swap16le(D) (D) #define swap32le(D) (D) #elif __BYTE_ORDER == __BIG_ENDIAN #define WAV_RIFF 0x52494646 #define WAV_WAVE 0x57415645 #define WAV_fmt 0x666D7420 #define WAV_data 0x64617461 #define WAV_smpl 0x736D706C #define swap16le(D) (((D)<<8) | ((D)>>8)) #define swap32le(D) ((((D)<<24) | (((D)<<8)&0x00FF0000) | (((D)>>8)&0x0000FF00) | ((D)>>24))) #else #error "Unknown endian" #endif typedef struct { u_int32_t id; u_int32_t size; u_int32_t type; u_int32_t *data; u_int32_t len; } AL_wav_file; typedef struct { u_int32_t id; u_int32_t size; u_int32_t *data; } AL_wav_chunk; static ALboolean _alutReadWavHeader(AL_wav_file *file, u_int32_t *memory) { if ((file->id = *(memory++)) != WAV_RIFF) { return AL_FALSE; } file->size = swap32le(*(memory++)); if (file->size < 4) { return AL_FALSE; } if ((file->type = *(memory++)) != WAV_WAVE) { return AL_FALSE; } file->data = memory; file->len = (file->size - 1) >> 2; return AL_TRUE; } static ALboolean _alutReadWavChunk(AL_wav_file *file, AL_wav_chunk *chunk) { ALuint len; if (file->len < 2) { return AL_FALSE; } chunk->id = file->data[0]; chunk->size = swap32le(file->data[1]); len = (chunk->size + 11) >> 2; if (file->len < len) { return AL_FALSE; } chunk->data = file->data + 2; file->data += len; file->len -= len; return AL_TRUE; } ALvoid alutLoadWAVMemory(ALbyte *memory, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *freq, ALboolean *loop) { AL_wav_file file; AL_wav_chunk chunk; ALuint bits = 0; *data = 0; *size = 0; *freq = 0; *format = 0; *loop = 0; if (!_alutReadWavHeader(&file, (u_int32_t *)memory)) { return; } while (_alutReadWavChunk(&file, &chunk)) { u_int16_t *data16 = (u_int16_t *)chunk.data; switch (chunk.id) { case WAV_fmt: if (data16[0] != 1) { return; } bits = swap16le(data16[7]); *freq = swap32le(chunk.data[1]); switch (swap16le(data16[1])) { case 1: switch (bits) { case 8: *format = AL_FORMAT_MONO8; break; case 16: *format = AL_FORMAT_MONO16; break; default: return; } break; case 2: switch (bits) { case 8: *format = AL_FORMAT_STEREO8; break; case 16: *format = AL_FORMAT_STEREO16; break; default: return; } break; default: return; } break; case WAV_data: if (!bits) { return; } *data = malloc(chunk.size); *size = chunk.size; #if __BYTE_ORDER == __LITTLE_ENDIAN memcpy(*data, chunk.data, chunk.size); #else if (bits == 16) { u_int16_t *ptr = *data; ALuint i, s = chunk.size << 1; for (i = 0; i < s; i++) { ptr[i] = swap16le(data16[i]); } } else { memcpy(*data, chunk.data, chunk.size); } #endif break; case WAV_smpl: *loop = swap32le(chunk.data[7]); break; } } } ALvoid alutLoadWAVFile(ALbyte *file, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *freq, ALboolean *loop) { int fd; void *memory; struct stat st; if ((fd = open(file, O_RDONLY)) < 0) { return; } if (!fstat(fd, &st)) { memory = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (memory != MAP_FAILED) { alutLoadWAVMemory(memory, format, data, size, freq, loop); munmap(memory, st.st_size); } } close(fd); } ALboolean alutLoadWAV(const char *fname, ALvoid **wave, ALsizei *format, ALsizei *size, ALsizei *bits, ALsizei *freq ) { ALboolean loop; alutLoadWAVFile((ALubyte *)fname, format, wave, size, freq, &loop); if (! *wave) { return AL_FALSE; } switch (*format) { case AL_FORMAT_MONO8: case AL_FORMAT_STEREO8: *bits = 8; break; case AL_FORMAT_MONO16: case AL_FORMAT_STEREO16: *bits = 16; break; } return AL_TRUE; } ALvoid alutUnloadWAV(ALenum format ATTRIBUTE_UNUSED, ALvoid *data, ALsizei size ATTRIBUTE_UNUSED, ALsizei freq ATTRIBUTE_UNUSED) { if (data) { free(data); } }