openal-alsa-creative/src/alut_wav.c

289 lines
5.1 KiB
C

/*
* 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 <ansidecl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <AL/alut.h>
#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);
}
}