// SampledSound class implementation // // A sound as an array of multi-channel samples // // QATSH Copyright 2009 Jean-Philippe MEURET #include "SampledSound.h" #include #include #include #include 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::max() frames (samples per channel). if (sndFileHdle.frames() > std::numeric_limits::max()) { std::cerr << "SampledSound : Sound file '" << pszFileName << "' too big (can't support more than " << std::numeric_limits::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; }