soundeditor/qatsh/SampledSound.cpp

258 lines
8.4 KiB
C++

// SampledSound class implementation
//
// A sound as an array of multi-channel samples
//
// QATSH Copyright 2009 Jean-Philippe MEURET <jpmeuret@free.fr>
#include "SampledSound.h"
#include <iostream>
#include <limits>
#include <cstring>
#include <sndfile.hh>
SampledSound::SampledSound(int nChannels, int nFrames, int nSamplingRate,
double** adSamples)
: _nChannels(nChannels), _nFrames(nFrames), _nSamplingRate(nSamplingRate), _adSamples(0)
{
if (_nChannels)
{
_adSamples = new double*[_nChannels];
if (_nFrames && _adSamples)
{
for (int nChanInd = 0; nChanInd < _nChannels; nChanInd++)
{
_adSamples[nChanInd] = new double[_nFrames];
if (adSamples && _adSamples[nChanInd])
memcpy(_adSamples[nChanInd], adSamples[nChanInd], nFrames*sizeof(double));
}
}
}
}
SampledSound::~SampledSound()
{
freeSamples();
}
void SampledSound::freeSamples()
{
if (_adSamples)
{
for (int nChanInd = 0; nChanInd < _nChannels; nChanInd++)
delete [] _adSamples[nChanInd];
delete [] _adSamples;
_adSamples = 0;
}
}
int SampledSound::samplingRate() const
{
return _nSamplingRate;
}
int SampledSound::nbChannels() const
{
return _nChannels;
}
int SampledSound::nbFrames() const
{
return _nFrames;
}
double** SampledSound::samples()
{
return _adSamples;
}
double* SampledSound::channel(int nChanInd)
{
return _adSamples[nChanInd];
}
bool SampledSound::load(const char* pszFileName)
{
std::cout << "SampledSound : Loading sound file '" << pszFileName << "' ..." << std::endl;
// Open sound file.
SndfileHandle sndFileHdle(pszFileName, SFM_READ);
// Check if all right till now.
if (!sndFileHdle)
{
std::cerr << "SampledSound : Failed to open sound file '" << pszFileName
<< "' : " << sndFileHdle.strError() << std::endl;
return false;
}
// Check for number of frames : as we load the whole sample into memory,
// we support only up to numeric_limits<int>::max() frames (samples per channel).
if (sndFileHdle.frames() > std::numeric_limits<int>::max())
{
std::cerr << "SampledSound : Sound file '" << pszFileName
<< "' too big (can't support more than " << std::numeric_limits<int>::max()
<< " samples)" << std::endl;
return false;
}
// Save sample properties
_nFrames = (int)sndFileHdle.frames();
_nChannels = sndFileHdle.channels();
_nSamplingRate = sndFileHdle.samplerate();
// Load the sample into memory.
// 1) Allocate the sample arrays (1 per channel).
freeSamples();
_adSamples = new double*[_nChannels];
for (int nChanInd = 0; nChanInd < _nChannels; nChanInd++)
_adSamples[nChanInd] = new double[_nFrames];
// 2) Allocate a buffer for IO
const int knFramesToRead = 2048;
double* aBuffer = new double[_nChannels*knFramesToRead];
// 3) Read the file and store the samples into the arrays.
sf_count_t nReadSamples = 0;
sf_count_t nReadFrames;
int nSampInd;
int nCurrFrameInd = 0;
while ((nReadFrames = sndFileHdle.readf(aBuffer, knFramesToRead)))
{
nSampInd = 0;
while (nReadFrames--)
{
for (int nChanInd = 0; nChanInd < _nChannels; nChanInd++)
_adSamples[nChanInd][nCurrFrameInd] = aBuffer[nSampInd++];
nCurrFrameInd++;
}
nReadSamples += nSampInd;
}
delete [] aBuffer;
std::cout << "... done (" << nReadSamples << " samples x channels read)" << std::endl;
return nReadSamples == _nFrames * _nChannels;
}
//======================================================================
// File extension to libsndfile::SF_FORMAT_* major type conversion table
// Note: Was setup for libsndfile 1.0.17 (more formats may be supported now).
struct SSndFileMajorFormatDesc
{
const char* pszFileExt;
int nSndFileMajorFormat;
};
static SSndFileMajorFormatDesc KSndFileMajorType[] =
{
{ ".wav", SF_FORMAT_WAV }, // Microsoft WAV format (little endian).
{ ".aif", SF_FORMAT_AIFF }, // Apple/SGI AIFF format (big endian).
{ ".aifc", SF_FORMAT_AIFF }, // Apple/SGI AIFF format (big endian).
{ ".aiff", SF_FORMAT_AIFF }, // Apple/SGI AIFF format (big endian).
{ ".au", SF_FORMAT_AU }, // Sun/NeXT AU format (big endian).
{ ".snd", SF_FORMAT_AU }, // Sun/NeXT AU format (big endian).
{ ".raw", SF_FORMAT_RAW }, // RAW PCM data.
{ ".pcm", SF_FORMAT_RAW }, // RAW PCM data.
{ ".voc", SF_FORMAT_VOC }, // VOC files.
{ ".caf", SF_FORMAT_CAF }, // Apple Core Audio File format
{ ".w64", SF_FORMAT_W64 }, // Sonic Foundry's 64 bit RIFF/WAV
{ ".sf", SF_FORMAT_IRCAM }, // Berkeley/IRCAM/CARL
{ ".paf", SF_FORMAT_PAF }, // Ensoniq PARIS file format.
{ ".iff", SF_FORMAT_SVX }, // Amiga IFF / SVX8 / SV16 format.
{ ".svx", SF_FORMAT_SVX }, // Amiga IFF / SVX8 / SV16 format.
{ ".nist", SF_FORMAT_NIST }, // Sphere NIST format.
{ ".sph", SF_FORMAT_NIST }, // Sphere NIST format.
{ ".flac", SF_FORMAT_FLAC }, // FLAC lossless file format
// { ".", SF_FORMAT_MAT4 }, // Matlab (tm) V4.2 / GNU Octave 2.0
// { ".", SF_FORMAT_MAT5 }, // Matlab (tm) V5.0 / GNU Octave 2.1
// { ".", SF_FORMAT_PVF }, // Portable Voice Format
// { ".", SF_FORMAT_XI }, // Fasttracker 2 Extended Instrument
// { ".", SF_FORMAT_HTK }, // HMM Tool Kit format
// { ".", SF_FORMAT_SDS }, // Midi Sample Dump Standard
// { ".", SF_FORMAT_AVR }, // Audio Visual Research
// { ".", SF_FORMAT_WAVEX }, // MS WAVE with WAVEFORMATEX
// { ".", SF_FORMAT_SD2 }, // Sound Designer 2
{ 0, 0 } // End of data marker; don't remove.
};
// ESampleFormat to libsndfile::SF_FORMAT_* subtype conversion table
static int KSndFileSubType[SampledSound::eSampFormatNumber] =
{
SF_FORMAT_PCM_16, // Signed 16 bit data
SF_FORMAT_PCM_24, // Signed 24 bit data
SF_FORMAT_PCM_32, // Signed 32 bit data
SF_FORMAT_FLOAT, // 32 bit float data
SF_FORMAT_DOUBLE // 64 bit float data
};
// Build a libsndfile format from the file extension (major format)
// and the sample format (format subtype) (see libsndfile API).
static int SndfileFormat(const char* pszFileExt, SampledSound::ESampleFormat eSampFormat)
{
SSndFileMajorFormatDesc* pFormaDesc = KSndFileMajorType;
while (pFormaDesc->pszFileExt && strcmp(pFormaDesc->pszFileExt, pszFileExt))
pFormaDesc++;
std::cout << "SampledSound : SndfileFormat(" << pszFileExt << ") : Major="
<< std::hex << pFormaDesc->nSndFileMajorFormat
<< ", SubType=" << KSndFileSubType[eSampFormat] << std::dec << std::endl;
return pFormaDesc->nSndFileMajorFormat | KSndFileSubType[eSampFormat];
}
// Store samples to the given file using given sample format.
bool SampledSound::store(const char* pszFileName, ESampleFormat eSampFormat) const
{
std::cout << "SampledSound : Storing sound file '" << pszFileName << "' ..." << std::endl;
// Determine sample output format and check if it is supported.
const int format = SndfileFormat(strrchr(pszFileName, '.'), eSampFormat);
if (!SndfileHandle::formatCheck(format, _nChannels, _nSamplingRate))
{
std::cerr << "SampledSound : Unsupported output format for '" << pszFileName
<< "'" << std::endl;
return false;
}
// Try and open sound file.
SndfileHandle sndFileHdle(pszFileName, SFM_WRITE, format, _nChannels, _nSamplingRate);
if (!sndFileHdle)
{
std::cerr << "SampledSound : Failed to open sound file '" << pszFileName
<< "' : " << sndFileHdle.strError() << std::endl;
return false;
}
// Write samples to file.
const int nFrameBlockLen = 2048; // Nb of frames (1 frame = nbChannels() samples)
double* aFrameBlock = new double[nFrameBlockLen*_nChannels];
int nCurrFrameInd = 0; // Index in _adSamples[*] of the frame to write
while (nCurrFrameInd < _nFrames) // For each possible frame block :
{
// Copy samples to the frame block
int nSampInd = 0; // Index in aFrameBlock of the sample to copy from _adSamples
while (nSampInd < nFrameBlockLen*_nChannels && nCurrFrameInd < _nFrames)
{
for (int nChanInd = 0; nChanInd < _nChannels; nChanInd++)
aFrameBlock[nSampInd++] = _adSamples[nChanInd][nCurrFrameInd];
nCurrFrameInd++;
}
// Write the frame block.
if (sndFileHdle.writef(aFrameBlock, nSampInd / _nChannels) < nSampInd / _nChannels)
{
std::cerr << "SampledSound : Failed to write frame block in '" << pszFileName
<< "' : " << sndFileHdle.strError() << std::endl;
return false;
}
}
delete [] aFrameBlock;
return true;
}