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.
256 lines
6.8 KiB
256 lines
6.8 KiB
// -*- c-basic-offset: 4 -*-
|
|
|
|
/*
|
|
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 "WAVAudioFile.h"
|
|
#include "RealTime.h"
|
|
|
|
#if (__GNUC__ < 3)
|
|
#include <strstream>
|
|
#define stringstream strstream
|
|
#else
|
|
#include <sstream>
|
|
#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<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;
|
|
}
|
|
|
|
|
|
}
|