// -*- c-basic-offset: 4 -*- /* Rosegarden A sequencer and musical notation editor. This program is Copyright 2000-2008 Guillaume Laurent , Chris Cannam , Richard Bown The moral right of the authors to claim authorship of this work has been asserted. 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. See the file COPYING included with this distribution for more information. */ #include "WAVAudioFile.h" #include "RealTime.h" #if (__GNUC__ < 3) #include #define stringstream strstream #else #include #endif using std::cout; using std::cerr; using std::endl; //#define DEBUG_DECODE 1 namespace Rosegarden { WAVAudioFile::WAVAudioFile(const unsigned int &id, const std::string &name, const std::string &fileName): RIFFAudioFile(id, name, fileName) { m_type = WAV; } WAVAudioFile::WAVAudioFile(const std::string &fileName, unsigned int channels = 1, unsigned int sampleRate = 48000, unsigned int bytesPerSecond = 6000, unsigned int bytesPerFrame = 2, unsigned int bitsPerSample = 16): RIFFAudioFile(fileName, channels, sampleRate, bytesPerSecond, bytesPerFrame, bitsPerSample) { m_type = WAV; } WAVAudioFile::~WAVAudioFile() {} bool WAVAudioFile::open() { // if already open if (m_inFile && (*m_inFile)) return true; m_inFile = new std::ifstream(m_fileName.c_str(), std::ios::in | std::ios::binary); if (!(*m_inFile)) { m_type = UNKNOWN; return false; } // Get the file size and store it for comparison later m_fileSize = m_fileInfo->size(); try { parseHeader(); } catch (BadSoundFileException e) { std::cerr << "ERROR: WAVAudioFile::open(): parseHeader: " << e.getMessage() << endl; return false; } return true; } // Open the file for writing, write out the header and move // to the data chunk to accept samples. We fill in all the // totals when we close(). // bool WAVAudioFile::write() { // close if we're open if (m_outFile) { m_outFile->close(); delete m_outFile; } // open for writing m_outFile = new std::ofstream(m_fileName.c_str(), std::ios::out | std::ios::binary); if (!(*m_outFile)) return false; // write out format header chunk and prepare for sample writing // writeFormatChunk(); return true; } void WAVAudioFile::close() { if (m_outFile == 0) return ; m_outFile->seekp(0, std::ios::end); unsigned int totalSize = m_outFile->tellp(); // seek to first length position m_outFile->seekp(4, std::ios::beg); // write complete file size minus 8 bytes to here putBytes(m_outFile, getLittleEndianFromInteger(totalSize - 8, 4)); // reseek from start forward 40 m_outFile->seekp(40, std::ios::beg); // write the data chunk size to end putBytes(m_outFile, getLittleEndianFromInteger(totalSize - 44, 4)); m_outFile->close(); delete m_outFile; m_outFile = 0; } // Set the AudioFile meta data according to WAV file format specification. // void WAVAudioFile::parseHeader() { // Read the format chunk and populate the file data. A plain WAV // file only has this chunk. Exceptions tumble through. // readFormatChunk(); } std::streampos WAVAudioFile::getDataOffset() { return 0; } bool WAVAudioFile::decode(const unsigned char *ubuf, size_t sourceBytes, size_t targetSampleRate, size_t targetChannels, size_t nframes, std::vector &target, bool adding) { size_t sourceChannels = getChannels(); size_t sourceSampleRate = getSampleRate(); size_t fileFrames = sourceBytes / getBytesPerFrame(); int bitsPerSample = getBitsPerSample(); if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && bitsPerSample != 32) { // 32-bit is IEEE-float (enforced in RIFFAudioFile) std::cerr << "WAVAudioFile::decode: unsupported " << bitsPerSample << "-bit sample size" << std::endl; return false; } #ifdef DEBUG_DECODE std::cerr << "WAVAudioFile::decode: " << sourceBytes << " bytes -> " << nframes << " frames, SSR " << getSampleRate() << ", TSR " << targetSampleRate << ", sch " << getChannels() << ", tch " << targetChannels << std::endl; #endif // If we're reading a stereo file onto a mono target, we mix the // two channels. If we're reading mono to stereo, we duplicate // the mono channel. Otherwise if the numbers of channels differ, // we just copy across the ones that do match and zero the rest. bool reduceToMono = (targetChannels == 1 && sourceChannels == 2); for (size_t ch = 0; ch < sourceChannels; ++ch) { if (!reduceToMono || ch == 0) { if (ch >= targetChannels) break; if (!adding) memset(target[ch], 0, nframes * sizeof(float)); } int tch = ch; // target channel for this data if (reduceToMono && ch == 1) { tch = 0; } float ratio = 1.0; if (sourceSampleRate != targetSampleRate) { ratio = float(sourceSampleRate) / float(targetSampleRate); } for (size_t i = 0; i < nframes; ++i) { size_t j = i; if (sourceSampleRate != targetSampleRate) { j = size_t(i * ratio); } if (j >= fileFrames) j = fileFrames - 1; float sample = convertBytesToSample (&ubuf[(bitsPerSample / 8) * (ch + j * sourceChannels)]); target[tch][i] += sample; } } // Now deal with any excess target channels for (int ch = sourceChannels; ch < targetChannels; ++ch) { if (ch == 1 && targetChannels == 2) { // copy mono to stereo if (!adding) { memcpy(target[ch], target[ch - 1], nframes * sizeof(float)); } else { for (size_t i = 0; i < nframes; ++i) { target[ch][i] += target[ch - 1][i]; } } } else { if (!adding) { memset(target[ch], 0, nframes * sizeof(float)); } } } return true; } }