/* audio/wav.h This file is part of the Osirion project and is distributed under the terms of the GNU General Public License version 2 */ /* see http://ccrma.stanford.edu/CCRMA/Courses/422/projects/WaveFormat/ */ #include "string.h" #include #include #include "audio/wav.h" #include "filesystem/filesystem.h" #include "sys/sys.h" namespace audio { PCM *Wav::load(std::string const & name) { if (!name.size()) return 0; std::string filename("sounds/"); filename.append(name); filename.append(".wav"); filesystem::File *wav_file = filesystem::open(filename.c_str()); if (!wav_file) { con_warn << "Could not open " << filename << std::endl; return 0; } unsigned char header[44]; memset(header, 0, sizeof(header)); if (!wav_file->read(header, 44)) { con_warn << "Error reading " << filename << std::endl; filesystem::close(wav_file); return 0; } // header RIFF if (strncmp((char *)header, "RIFF", 4)) { con_warn << "Error reading " << filename << ": invalid RIFF header!" << std::endl; filesystem::close(wav_file); return 0; } // format WAVE if (strncmp((char *)header + 8, "WAVE", 4) != 0) { con_warn << "Error reading " << filename << ": invalid WAVE header!" << std::endl; filesystem::close(wav_file); return 0; } // file size //size_t chunksize = header[4] + (header[5] << 8) + (header[6] << 16) + (header[7] << 24); if (strncmp((char *)header + 12, "fmt ", 4) != 0) { con_warn << "Error reading " << filename << ": invalid format header!" << std::endl; filesystem::close(wav_file); return 0; } size_t subchunksize1 = header[16] + (header[17] << 8) + (header[18] << 16) + (header[19] << 24); size_t audioformat = header[20] + (header[21] << 8); if ((subchunksize1 != 16) || (audioformat != 1)) { con_warn << "Error reading " << filename << ": unrecognized file format!" << std::endl; filesystem::close(wav_file); return 0; } unsigned int channels = header[22] + (header[23] << 8); if ((channels < 1) || (channels > 2)) { con_warn << "Error reading " << filename << ": invalid number of channels!" << std::endl; filesystem::close(wav_file); return 0; } unsigned int samplerate = header[24] + ((size_t)header[25] << 8) + ((size_t)header[26] << 16) + ((size_t)header[27] << 24); //size_t byterate = header[28] + (header[29] << 8) + (header[30] << 16) + (header[31] << 24); //size_t blockalign = header[32] + (header[33] << 8); size_t bitspersample = header[34] + (header[35] << 8); if (strncmp((char *)header + 36, "LIST", 4) == 0) { // this wav file contains meta data (author, title, ..) // byte 40 through 43 contains the metadata length instead of the datasize size_t metasize = header[40] + (header[41] << 8) + (header[42] << 16) + (header[43] << 24); wav_file->skip(metasize); // the data header follows the meta data, we overwrite the original metadata header if (!wav_file->read(header + 36, 8)) { con_warn << "Error reading " << filename << ": unexpected end of file!" << std::endl; filesystem::close(wav_file); return 0; } } if (strncmp((char *)header + 36, "data", 4) != 0) { con_warn << "Error reading " << filename << ": invalid data header!" << std::endl; filesystem::close(wav_file); return 0; } size_t datasize = header[40] + (header[41] << 8) + (header[42] << 16) + (header[43] << 24); PCM *pcm = new PCM(samplerate, bitspersample, channels, datasize); const size_t readblocksize = channels * (bitspersample / 8); size_t total_bytes = 0; while ((wav_file->read((void *)(*pcm)[total_bytes], readblocksize)) && (total_bytes < datasize)) { total_bytes += readblocksize; } if (total_bytes < datasize) { con_warn << "Error reading " << filename << ": file truncated!" << std::endl; } con_debug << " " << filename << " " << pcm->samplerate() << "Hz " << pcm->bitspersample() << "bit " << pcm->channels() << " chan " << pcm->size() << " bytes" << std::endl; filesystem::close(wav_file); return pcm; } }