/* * PolySound.cpp * Poly * * Created by Ivan Safrin on 3/15/09. * Copyright 2009 __MyCompanyName__. All rights reserved. * */ #include "PolySound.h" using namespace Polycode; size_t custom_readfunc(void *ptr, size_t size, size_t nmemb, void *datasource) { OSFILE *file = (OSFILE*) datasource; return OSBasics::read(ptr, size, nmemb, file); } int custom_seekfunc(void *datasource, ogg_int64_t offset, int whence){ OSFILE *file = (OSFILE*) datasource; return OSBasics::seek(file, offset, whence); } int custom_closefunc(void *datasource) { OSFILE *file = (OSFILE*) datasource; return OSBasics::close(file); } long custom_tellfunc(void *datasource) { OSFILE *file = (OSFILE*) datasource; return OSBasics::tell(file); } Sound::Sound(string fileName) { string extension; size_t found; found=fileName.rfind("."); if (found!=string::npos) { extension = fileName.substr(found+1); } else { extension = ""; } ALuint buffer; if(extension == "wav" || extension == "WAV") { buffer = loadWAV(fileName); } else if(extension == "ogg" || extension == "OGG") { buffer = loadOGG(fileName); } soundSource = GenSource(buffer); } Sound::~Sound() { Logger::log("destroying sound...\n"); alDeleteSources(1,&soundSource); } void Sound::soundCheck(bool result, string err) { if(!result) soundError(err); } void Sound::soundError(string err) { Logger::log("SOUND ERROR: %s\n", err.c_str()); } unsigned long Sound::readByte32(const unsigned char buffer[4]) { #if TAU_BIG_ENDIAN return (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]; #else return (buffer[3] << 24) + (buffer[2] << 16) + (buffer[1] << 8) + buffer[0]; #endif } unsigned short Sound::readByte16(const unsigned char buffer[2]) { #if TAU_BIG_ENDIAN return (buffer[0] << 8) + buffer[1]; #else return (buffer[1] << 8) + buffer[0]; #endif } void Sound::Play(bool once) { if(once) { alSourcei(soundSource, AL_LOOPING, AL_FALSE); } else { alSourcei(soundSource, AL_LOOPING, AL_TRUE); } alSourcePlay(soundSource); } void Sound::checkALError(string operation) { ALenum error = alGetError(); if(error != AL_NO_ERROR) { switch(error) { case AL_NO_ERROR: soundError(operation + ": " +ALNoErrorStr); break; case AL_INVALID_NAME: soundError(operation +": " + ALInvalidNameStr); break; case AL_INVALID_ENUM: soundError(operation + ": " +ALInvalidEnumStr); break; case AL_INVALID_VALUE: soundError(operation + ": " +ALInvalidValueStr); break; case AL_INVALID_OPERATION: soundError(operation + ": " +ALInvalidOpStr); break; case AL_OUT_OF_MEMORY: soundError(operation + ": " +ALOutOfMemoryStr); break; default: soundError(operation + ": " +ALOtherErrorStr); break; } } } void Sound::Stop() { alSourceStop(soundSource); } ALuint Sound::GenSource() { ALuint source; bool looping = false; ALfloat sourcePos[] = {0.0, 0.0, 0.0}; ALfloat sourceVel[] = {0.0, 0.0, 0.0}; alGetError(); alGenSources(1, &source); checkALError("Generating sources"); alSourcef(source, AL_PITCH, 1.0); alSourcef(source, AL_GAIN, 1.0); alSourcefv(source, AL_POSITION, sourcePos); alSourcefv(source, AL_VELOCITY, sourceVel); alSourcei(source, AL_LOOPING, looping); checkALError("Setting source properties"); return source; } ALuint Sound::GenSource(ALuint buffer) { alGetError(); ALuint source = GenSource(); alSourcei(source, AL_BUFFER, buffer); checkALError("Setting source buffer"); return source; } ALuint Sound::loadOGG(string fileName) { vector buffer; ALuint bufferID = AL_NONE; alGenBuffers(1, &bufferID); int endian = 0; // 0 for Little-Endian, 1 for Big-Endian int bitStream; long bytes; char array[BUFFER_SIZE]; // Local fixed size array OSFILE *f; ALenum format; ALsizei freq; // Open for binary reading f = OSBasics::open(fileName.c_str(), "rb"); if(!f) { soundError("Error loading OGG file!\n"); return bufferID; } vorbis_info *pInfo; OggVorbis_File oggFile; ov_callbacks callbacks; callbacks.read_func = custom_readfunc; callbacks.seek_func = custom_seekfunc; callbacks.close_func = custom_closefunc; callbacks.tell_func = custom_tellfunc; ov_open_callbacks( (void*)f, &oggFile, NULL, 0, callbacks); // ov_open(f, &oggFile, NULL, 0); // Get some information about the OGG file pInfo = ov_info(&oggFile, -1); // Check the number of channels... always use 16-bit samples if (pInfo->channels == 1) format = AL_FORMAT_MONO16; else format = AL_FORMAT_STEREO16; // end if // The frequency of the sampling rate freq = pInfo->rate; do { // Read up to a buffer's worth of decoded sound data bytes = ov_read(&oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream); // Append to end of buffer buffer.insert(buffer.end(), array, array + bytes); } while (bytes > 0); ov_clear(&oggFile); alBufferData(bufferID, format, &buffer[0], static_cast(buffer.size()), freq); return bufferID; } ALuint Sound::loadWAV(string fileName) { long bytes; vector data; ALenum format; ALsizei freq; // Local resources OSFILE *f = NULL; char *array = NULL; ALuint buffer = AL_NONE; alGetError(); // Open for binary reading f = OSBasics::open(fileName.c_str(), "rb"); if (!f) soundError("LoadWav: Could not load wav from " + fileName); // buffers char magic[5]; magic[4] = '\0'; unsigned char buffer32[4]; unsigned char buffer16[2]; // check magic soundCheck(OSBasics::read(magic,4,1,f) == 1, "LoadWav: Cannot read wav file "+ fileName ); soundCheck(std::string(magic) == "RIFF", "LoadWav: Wrong wav file format. This file is not a .wav file (no RIFF magic): "+ fileName ); // skip 4 bytes (file size) OSBasics::seek(f,4,SEEK_CUR); // check file format soundCheck(OSBasics::read(magic,4,1,f) == 1, "LoadWav: Cannot read wav file "+ fileName ); soundCheck(std::string(magic) == "WAVE", "LoadWav: Wrong wav file format. This file is not a .wav file (no WAVE format): "+ fileName ); // check 'fmt ' sub chunk (1) soundCheck(OSBasics::read(magic,4,1,f) == 1, "LoadWav: Cannot read wav file "+ fileName ); soundCheck(std::string(magic) == "fmt ", "LoadWav: Wrong wav file format. This file is not a .wav file (no 'fmt ' subchunk): "+ fileName ); // read (1)'s size soundCheck(OSBasics::read(buffer32,4,1,f) == 1, "LoadWav: Cannot read wav file "+ fileName ); unsigned long subChunk1Size = readByte32(buffer32); soundCheck(subChunk1Size >= 16, "Wrong wav file format. This file is not a .wav file ('fmt ' chunk too small, truncated file?): "+ fileName ); // check PCM audio format soundCheck(OSBasics::read(buffer16,2,1,f) == 1, "LoadWav: Cannot read wav file "+ fileName ); unsigned short audioFormat = readByte16(buffer16); soundCheck(audioFormat == 1, "LoadWav: Wrong wav file format. This file is not a .wav file (audio format is not PCM): "+ fileName ); // read number of channels soundCheck(OSBasics::read(buffer16,2,1,f) == 1, "LoadWav: Cannot read wav file "+ fileName ); unsigned short channels = readByte16(buffer16); // read frequency (sample rate) soundCheck(OSBasics::read(buffer32,4,1,f) == 1, "LoadWav: Cannot read wav file "+ fileName ); unsigned long frequency = readByte32(buffer32); // skip 6 bytes (Byte rate (4), Block align (2)) OSBasics::seek(f,6,SEEK_CUR); // read bits per sample soundCheck(OSBasics::read(buffer16,2,1,f) == 1, "LoadWav: Cannot read wav file "+ fileName ); unsigned short bps = readByte16(buffer16); if (channels == 1) format = (bps == 8) ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16; else format = (bps == 8) ? AL_FORMAT_STEREO8 : AL_FORMAT_STEREO16; // check 'data' sub chunk (2) soundCheck(OSBasics::read(magic,4,1,f) == 1, "LoadWav: Cannot read wav file "+ fileName ); soundCheck(std::string(magic) == "data", "LoadWav: Wrong wav file format. This file is not a .wav file (no data subchunk): "+ fileName ); soundCheck(OSBasics::read(buffer32,4,1,f) == 1, "LoadWav: Cannot read wav file "+ fileName ); unsigned long subChunk2Size = readByte32(buffer32); // The frequency of the sampling rate freq = frequency; soundCheck(sizeof(freq) == sizeof(frequency), "LoadWav: freq and frequency different sizes"); array = new char[BUFFER_SIZE]; while (data.size() != subChunk2Size) { // Read up to a buffer's worth of decoded sound data bytes = OSBasics::read(array, 1, BUFFER_SIZE, f); if (bytes <= 0) break; if (data.size() + bytes > subChunk2Size) bytes = subChunk2Size - data.size(); // Append to end of buffer data.insert(data.end(), array, array + bytes); }; delete []array; array = NULL; OSBasics::close(f); f = NULL; alGenBuffers(1, &buffer); soundCheck(alGetError() == AL_NO_ERROR, "LoadWav: Could not generate buffer"); soundCheck(AL_NONE != buffer, "LoadWav: Could not generate buffer"); alBufferData(buffer, format, &data[0], data.size(), freq); soundCheck(alGetError() == AL_NO_ERROR, "LoadWav: Could not load buffer data"); return buffer; // if (buffer) // if (alIsBuffer(buffer) == AL_TRUE) // alDeleteBuffers(1, &buffer); // // if (array) // delete []array; // // if (f) // OSBasics::close(f); // // throw (e); }