You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rosegarden/src/sound/WAVAudioFile.cpp

249 lines
6.7 KiB

/*
Rosegarden
A sequencer and musical notation editor.
This program is Copyright 2000-2008
Guillaume Laurent <glaurent@telegraph-road.org>,
Chris Cannam <cannam@all-day-breakfast.com>,
Richard Bown <bownie@bownie.com>
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 <sstream>
#include "WAVAudioFile.h"
#include "RealTime.h"
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<float *> &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;
}
}