summaryrefslogtreecommitdiffstats
path: root/src/sound/PeakFile.cpp
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
commit145364a8af6a1fec06556221e66d4b724a62fc9a (patch)
tree53bd71a544008c518034f208d64c932dc2883f50 /src/sound/PeakFile.cpp
downloadrosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.tar.gz
rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.zip
Added old abandoned KDE3 version of the RoseGarden MIDI tool
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/rosegarden@1097595 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/sound/PeakFile.cpp')
-rw-r--r--src/sound/PeakFile.cpp1033
1 files changed, 1033 insertions, 0 deletions
diff --git a/src/sound/PeakFile.cpp b/src/sound/PeakFile.cpp
new file mode 100644
index 0000000..8881114
--- /dev/null
+++ b/src/sound/PeakFile.cpp
@@ -0,0 +1,1033 @@
+// -*- c-indentation-style:"stroustrup" c-basic-offset: 4 -*- /*
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ 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 <cmath>
+#include <cstdlib>
+#include <kapplication.h>
+
+#include <qdatetime.h>
+#include <qstringlist.h>
+#include <qpalette.h>
+#include <kapp.h>
+
+#include "PeakFile.h"
+#include "AudioFile.h"
+#include "Profiler.h"
+
+using std::cout;
+using std::cerr;
+using std::endl;
+
+//#define DEBUG_PEAKFILE 1
+//#define DEBUG_PEAKFILE_BRIEF 1
+//#define DEBUG_PEAKFILE_CACHE 1
+
+#ifdef DEBUG_PEAKFILE
+#define DEBUG_PEAKFILE_BRIEF 1
+#endif
+
+namespace Rosegarden
+{
+
+PeakFile::PeakFile(AudioFile *audioFile):
+ SoundFile(audioFile->getPeakFilename()),
+ m_audioFile(audioFile),
+ m_version( -1), // -1 defines new file - start at 0
+ m_format(1), // default is 8-bit peak format
+ m_pointsPerValue(0),
+ m_blockSize(256), // default block size is 256 samples
+ m_channels(0),
+ m_numberOfPeaks(0),
+ m_positionPeakOfPeaks(0),
+ m_offsetToPeaks(0),
+ m_modificationTime(QDate(1970, 1, 1), QTime(0, 0, 0)),
+ m_chunkStartPosition(0),
+ m_lastPreviewStartTime(0, 0),
+ m_lastPreviewEndTime(0, 0),
+ m_lastPreviewWidth( -1),
+ m_lastPreviewShowMinima(false)
+{}
+
+PeakFile::~PeakFile()
+{}
+
+bool
+PeakFile::open()
+{
+ // Set the file size
+ //
+ QFileInfo info(QString(m_fileName.c_str()));
+ m_fileSize = info.size();
+
+ // If we're already open then don't open again
+ //
+ if (m_inFile && m_inFile->is_open())
+ return true;
+
+ // Open
+ //
+ m_inFile = new std::ifstream(m_fileName.c_str(),
+ std::ios::in | std::ios::binary);
+ // Check we're open
+ //
+ if (!(*m_inFile))
+ return false;
+
+ try {
+ parseHeader();
+ } catch (BadSoundFileException s) {
+
+#ifdef DEBUG_PEAKFILE
+ cerr << "PeakFile::open - EXCEPTION \"" << s.getMessage() << "\""
+ << endl;
+#endif
+
+ return false;
+ }
+
+ return true;
+}
+
+void
+PeakFile::parseHeader()
+{
+ if (!(*m_inFile))
+ return ;
+
+ m_inFile->seekg(0, std::ios::beg);
+
+ // get full header length
+ //
+ std::string header = getBytes(128);
+
+#if (__GNUC__ < 3)
+
+ if (header.compare(AUDIO_BWF_PEAK_ID, 0, 4) != 0)
+#else
+
+ if (header.compare(0, 4, AUDIO_BWF_PEAK_ID) != 0)
+#endif
+
+ {
+ throw(BadSoundFileException(m_fileName, "PeakFile::parseHeader - can't find LEVL identifier"));
+ }
+
+ int length = getIntegerFromLittleEndian(header.substr(4, 4));
+
+ // Get the length of the header minus the first 8 bytes
+ //
+ if (length == 0)
+ throw(BadSoundFileException(m_fileName, "PeakFile::parseHeader - can't get header length"));
+
+ // Get the file information
+ //
+ m_version = getIntegerFromLittleEndian(header.substr(8, 4));
+ m_format = getIntegerFromLittleEndian(header.substr(12, 4));
+ m_pointsPerValue = getIntegerFromLittleEndian(header.substr(16, 4));
+ m_blockSize = getIntegerFromLittleEndian(header.substr(20, 4));
+ m_channels = getIntegerFromLittleEndian(header.substr(24, 4));
+ m_numberOfPeaks = getIntegerFromLittleEndian(header.substr(28, 4));
+ m_positionPeakOfPeaks = getIntegerFromLittleEndian(header.substr(32, 4));
+
+ // Read in date string and convert it up to QDateTime
+ //
+ QString dateString = QString(header.substr(40, 28).c_str());
+
+ QStringList dateTime = QStringList::split(":", dateString);
+
+ m_modificationTime.setDate(QDate(dateTime[0].toInt(),
+ dateTime[1].toInt(),
+ dateTime[2].toInt()));
+
+ m_modificationTime.setTime(QTime(dateTime[3].toInt(),
+ dateTime[4].toInt(),
+ dateTime[5].toInt(),
+ dateTime[6].toInt()));
+
+ //printStats();
+
+}
+
+void
+PeakFile::printStats()
+{
+ cout << endl;
+ cout << "STATS for PeakFile \"" << m_fileName << "\"" << endl
+ << "-----" << endl << endl;
+
+ cout << " VERSION = " << m_version << endl
+ << " FORMAT = " << m_format << endl
+ << " BYTES/VALUE = " << m_pointsPerValue << endl
+ << " BLOCKSIZE = " << m_blockSize << endl
+ << " CHANNELS = " << m_channels << endl
+ << " PEAK FRAMES = " << m_numberOfPeaks << endl
+ << " PEAK OF PKS = " << m_positionPeakOfPeaks << endl
+ << endl;
+
+ cout << "DATE" << endl
+ << "----" << endl << endl
+ << " YEAR = " << m_modificationTime.date().year() << endl
+ << " MONTH = " << m_modificationTime.date().month() << endl
+ << " DAY = " << m_modificationTime.date().day() << endl
+ << " HOUR = " << m_modificationTime.time().hour() << endl
+ << " MINUTE = " << m_modificationTime.time().minute()
+ << endl
+ << " SECOND = " << m_modificationTime.time().second()
+ << endl
+ << " MSEC = " << m_modificationTime.time().msec()
+ << endl << endl;
+}
+
+bool
+PeakFile::write()
+{
+ return write(5); // default update every 5%
+}
+
+bool
+PeakFile::write(unsigned short updatePercentage)
+{
+ if (m_outFile) {
+ m_outFile->close();
+ delete m_outFile;
+ }
+
+ // Attempt to open AudioFile so that we can extract sample data
+ // for preview file generation
+ //
+ try {
+ if (!m_audioFile->open())
+ return false;
+ } catch (BadSoundFileException e) {
+#ifdef DEBUG_PEAKFILE
+ std::cerr << "PeakFile::write - \"" << e.getMessage() << "\"" << std::endl;
+#endif
+
+ return false;
+ }
+
+ // create and test that we've made it
+ m_outFile = new std::ofstream(m_fileName.c_str(),
+ std::ios::out | std::ios::binary);
+ if (!(*m_outFile))
+ return false;
+
+ // write out the header
+ writeHeader(m_outFile);
+
+ // and now the peak values
+ writePeaks(updatePercentage, m_outFile);
+
+ return true;
+}
+
+// Close the peak file and tidy up
+//
+void
+PeakFile::close()
+{
+ // Close any input file handle
+ //
+ if (m_inFile && m_inFile->is_open()) {
+ m_inFile->close();
+ delete m_inFile;
+ m_inFile = 0;
+ }
+
+ if (m_outFile == 0)
+ return ;
+
+ // Seek to start of chunk
+ //
+ m_outFile->seekp(m_chunkStartPosition, std::ios::beg);
+
+ // Seek to size field at set it
+ //
+ m_outFile->seekp(4, std::ios::cur);
+ putBytes(m_outFile, getLittleEndianFromInteger(m_bodyBytes + 120, 4));
+
+ // Seek to format and set it (m_format is only set at the
+ // end of writePeaks()
+ //
+ m_outFile->seekp(4, std::ios::cur);
+ putBytes(m_outFile, getLittleEndianFromInteger(m_format, 4));
+
+ // Seek to number of peak frames and write value
+ //
+ m_outFile->seekp(12, std::ios::cur);
+ putBytes(m_outFile,
+ getLittleEndianFromInteger(m_numberOfPeaks, 4));
+
+ // Peak of peaks
+ //
+ putBytes(m_outFile,
+ getLittleEndianFromInteger(m_positionPeakOfPeaks, 4));
+
+ // Seek to date field
+ //
+ m_outFile->seekp(4, std::ios::cur);
+
+ // Set modification time to now
+ //
+ m_modificationTime = m_modificationTime.currentDateTime();
+
+ QString fDate;
+ fDate.sprintf("%04d:%02d:%02d:%02d:%02d:%02d:%03d",
+ m_modificationTime.date().year(),
+ m_modificationTime.date().month(),
+ m_modificationTime.date().day(),
+ m_modificationTime.time().hour(),
+ m_modificationTime.time().minute(),
+ m_modificationTime.time().second(),
+ m_modificationTime.time().msec());
+
+ std::string dateString(fDate.data());
+
+ // Pad with spaces to make up to 28 bytes long and output
+ //
+ dateString += " ";
+ putBytes(m_outFile, dateString);
+
+ // Ok, now close and tidy up
+ //
+ m_outFile->close();
+ delete m_outFile;
+ m_outFile = 0;
+}
+
+// If the audio file is more recently modified that the modification time
+// on this peak file then we're invalid. The action to rectify this is
+// usually to regenerate the peak data.
+//
+bool
+PeakFile::isValid()
+{
+ if (m_audioFile->getModificationDateTime() > m_modificationTime)
+ return false;
+
+ return true;
+}
+
+bool
+PeakFile::writeToHandle(std::ofstream *file,
+ unsigned short /*updatePercentage*/)
+{
+ // Remember the position where we pass in the ofstream pointer
+ // so we can return there to write close() information.
+ //
+ m_chunkStartPosition = file->tellp();
+
+ return false;
+}
+
+// Build up a header string and then pump it out to the file handle
+//
+void
+PeakFile::writeHeader(std::ofstream *file)
+{
+ if (!file || !(*file))
+ return ;
+
+ std::string header;
+
+ // The "levl" identifer for this chunk
+ //
+ header += AUDIO_BWF_PEAK_ID;
+
+ // Add a four byte version of the size of the header chunk (120
+ // bytes from this point onwards)
+ //
+ header += getLittleEndianFromInteger(120, 4);
+
+ // A four byte version number (incremented every time)
+ //
+ header += getLittleEndianFromInteger(++m_version, 4);
+
+ // Format of the peak points - 1 = unsigned char
+ // 2 = unsigned short
+ //
+ header += getLittleEndianFromInteger(m_format, 4);
+
+ // Points per value - 1 = 1 peak and has vertical about x-axis
+ // 2 = 2 peaks so differs above and below x-axis
+ //
+ // .. hardcode to 2 for the mo
+ m_pointsPerValue = 2;
+ header += getLittleEndianFromInteger(m_pointsPerValue, 4);
+
+ // Block size - default and recommended is 256
+ //
+ header += getLittleEndianFromInteger(m_blockSize, 4);
+
+ // Set channels up if they're currently empty
+ //
+ if (m_channels == 0 && m_audioFile)
+ m_channels = m_audioFile->getChannels();
+
+ // Peak channels - same as AudioFile channels
+ //
+ header += getLittleEndianFromInteger(m_channels, 4);
+
+ // Number of peak frames - we write this at close() and so
+ // for the moment put spacing 0's in.
+ header += getLittleEndianFromInteger(0, 4);
+
+ // Position of peak of peaks - written at close()
+ //
+ header += getLittleEndianFromInteger(0, 4);
+
+ // Offset to start of peaks - usually the total size of this header
+ //
+ header += getLittleEndianFromInteger(128, 4);
+
+ // Creation timestamp - fill in on close() so just use spacing
+ // of 28 bytes for the moment.
+ //
+ header += getLittleEndianFromInteger(0, 28);
+
+ // reserved space - 60 bytes
+ header += getLittleEndianFromInteger(0, 60);
+
+ //cout << "HEADER LENGTH = " << header.length() << endl;
+
+ // write out the header
+ //
+ putBytes(file, header);
+}
+
+bool
+PeakFile::scanToPeak(int peak)
+{
+ if (!m_inFile)
+ return false;
+
+ if (!m_inFile->is_open())
+ return false;
+
+ // Scan to start of chunk and then seek to peak number
+ //
+ ssize_t pos = (ssize_t)m_chunkStartPosition + 128 +
+ peak * m_format * m_channels * m_pointsPerValue;
+
+ ssize_t off = pos - m_inFile->tellg();
+
+ if (off == 0) {
+ return true;
+ } else if (off < 0) {
+ // std::cerr << "PeakFile::scanToPeak: warning: seeking backwards for peak " << peak << " (" << m_inFile->tellg() << " -> " << pos << ")" << std::endl;
+ m_inFile->seekg(pos);
+ } else {
+ m_inFile->seekg(off, std::ios::cur);
+ }
+
+ // Ensure we re-read the input buffer if we're
+ // doing buffered reads as it's now meaningless
+ //
+ m_loseBuffer = true;
+
+ if (m_inFile->eof()) {
+ m_inFile->clear();
+ return false;
+ }
+
+ return true;
+}
+
+bool
+PeakFile::scanForward(int numberOfPeaks)
+{
+ if (!m_inFile)
+ return false;
+
+ if (!m_inFile->is_open())
+ return false;
+
+ // Seek forward and number of peaks
+ //
+ m_inFile->seekg(numberOfPeaks * m_format * m_channels * m_pointsPerValue,
+ std::ios::cur);
+
+ // Ensure we re-read the input buffer
+ m_loseBuffer = true;
+
+ if (m_inFile->eof()) {
+ m_inFile->clear();
+ return false;
+ }
+
+ return true;
+}
+
+
+void
+PeakFile::writePeaks(unsigned short /*updatePercentage*/,
+ std::ofstream *file)
+{
+ if (!file || !(*file))
+ return ;
+ m_keepProcessing = true;
+
+#ifdef DEBUG_PEAKFILE
+
+ cout << "PeakFile::writePeaks - calculating peaks" << endl;
+#endif
+
+ // Scan to beginning of audio data
+ m_audioFile->scanTo(RealTime(0, 0));
+
+ // Store our samples
+ //
+ std::vector<std::pair<int, int> > channelPeaks;
+ std::string samples;
+ unsigned char *samplePtr;
+
+ int sampleValue;
+ int sampleMax = 0 ;
+ int sampleFrameCount = 0;
+
+ int channels = m_audioFile->getChannels();
+ int bytes = m_audioFile->getBitsPerSample() / 8;
+
+ m_format = bytes;
+ if (bytes == 3 || bytes == 4) // 24-bit PCM or 32-bit float
+ m_format = 2; // write 16-bit PCM instead
+
+ // for the progress dialog
+ unsigned int apprxTotalBytes = m_audioFile->getSize();
+ unsigned int byteCount = 0;
+
+ for (int i = 0; i < channels; i++)
+ channelPeaks.push_back(std::pair<int, int>());
+
+ // clear down info
+ m_numberOfPeaks = 0;
+ m_bodyBytes = 0;
+ m_positionPeakOfPeaks = 0;
+
+ while (m_keepProcessing) {
+ try {
+ samples = m_audioFile->
+ getBytes(m_blockSize * channels * bytes);
+ } catch (BadSoundFileException e) {
+ std::cerr << "PeakFile::writePeaks: " << e.getMessage()
+ << std::endl;
+ break;
+ }
+
+ // If no bytes or less than the total number of bytes are returned
+ // then break out
+ //
+ if (samples.length() == 0 ||
+ samples.length() < (m_blockSize * m_audioFile->getChannels()
+ * bytes))
+ break;
+
+ byteCount += samples.length();
+
+ emit setProgress((int)(double(byteCount) /
+ double(apprxTotalBytes) * 100.0));
+ kapp->processEvents();
+
+ samplePtr = (unsigned char *)samples.c_str();
+
+ for (int i = 0; i < m_blockSize; i++) {
+ for (unsigned int ch = 0; ch < m_audioFile->getChannels(); ch++) {
+ // Single byte format values range from 0-255 and then
+ // shifted down about the x-axis. Double byte and above
+ // are already centred about x-axis.
+ //
+ if (bytes == 1) {
+ // get value
+ sampleValue = int(*samplePtr) - 128;
+ samplePtr++;
+ } else if (bytes == 2) {
+ unsigned char b2 = samplePtr[0];
+ unsigned char b1 = samplePtr[1];
+ unsigned int bits = (b1 << 8) + b2;
+ sampleValue = (short)bits;
+ samplePtr += 2;
+ } else if (bytes == 3) {
+ unsigned char b3 = samplePtr[0];
+ unsigned char b2 = samplePtr[1];
+ unsigned char b1 = samplePtr[2];
+ unsigned int bits = (b1 << 24) + (b2 << 16) + (b3 << 8);
+
+ // write out as 16-bit (m_format == 2)
+ sampleValue = int(bits) / 65536;
+
+ samplePtr += 3;
+ } else if (bytes == 4) // IEEE float (enforced by RIFFAudioFile)
+ {
+ // write out as 16-bit (m_format == 2)
+ float val = *(float *)samplePtr;
+ sampleValue = (int)(32767.0 * val);
+ samplePtr += 4;
+ } else {
+ throw(BadSoundFileException(m_fileName, "PeakFile::writePeaks - unsupported bit depth"));
+ }
+
+ // First time for each channel
+ //
+ if (i == 0) {
+ channelPeaks[ch].first = sampleValue;
+ channelPeaks[ch].second = sampleValue;
+ } else {
+ // Compare and store
+ //
+ if (sampleValue > channelPeaks[ch].first)
+ channelPeaks[ch].first = sampleValue;
+
+ if (sampleValue < channelPeaks[ch].second)
+ channelPeaks[ch].second = sampleValue;
+ }
+
+ // Store peak of peaks if it fits
+ //
+ if (abs(sampleValue) > sampleMax) {
+ sampleMax = abs(sampleValue);
+ m_positionPeakOfPeaks = sampleFrameCount;
+ }
+ }
+
+ // for peak of peaks as well as frame count
+ sampleFrameCount++;
+ }
+
+ // Write absolute peak data in channel order
+ //
+ for (unsigned int i = 0; i < m_audioFile->getChannels(); i++) {
+ putBytes(file, getLittleEndianFromInteger(channelPeaks[i].first,
+ m_format));
+ putBytes(file, getLittleEndianFromInteger(channelPeaks[i].second,
+ m_format));
+ m_bodyBytes += m_format * 2;
+ }
+
+ // increment number of peak frames
+ m_numberOfPeaks++;
+ }
+
+#ifdef DEBUG_PEAKFILE
+ cout << "PeakFile::writePeaks - "
+ << "completed peaks" << endl;
+#endif
+
+}
+
+// Get a normalised vector for the preview at a given horizontal resolution.
+// We return a value for each channel and if returnLow is set we also return
+// an interleaved low value for each channel.
+//
+//
+std::vector<float>
+PeakFile::getPreview(const RealTime &startTime,
+ const RealTime &endTime,
+ int width,
+ bool showMinima)
+{
+#ifdef DEBUG_PEAKFILE_BRIEF
+ std::cout << "PeakFile::getPreview - "
+ << "startTime = " << startTime
+ << ", endTime = " << endTime
+ << ", width = " << width
+ << ", showMinima = " << showMinima << std::endl;
+#endif
+
+ if (getSize() == 0) {
+ std::cout << "PeakFile::getPreview - PeakFile size == 0" << std::endl;
+ return std::vector<float>();
+ }
+
+ // Regenerate cache on these conditions
+ //
+ if (!m_peakCache.length()) {
+#ifdef DEBUG_PEAKFILE_CACHE
+ std::cerr << "PeakFile::getPreview - no peak cache" << std::endl;
+#endif
+
+ if (getSize() < (256 *1024)) // if less than 256K PeakFile
+ {
+ // Scan to start of peak data
+ scanToPeak(0);
+ try
+ {
+ m_peakCache = getBytes(m_inFile, getSize() - 128);
+ } catch (BadSoundFileException e)
+ {
+ std::cerr << "PeakFile::getPreview: " << e.getMessage()
+ << std::endl;
+ }
+
+#ifdef DEBUG_PEAKFILE_CACHE
+ std::cout << "PeakFile::getPreview - generated peak cache - "
+ << "size = " << m_peakCache.length() << std::endl;
+#endif
+
+ } else {
+#ifdef DEBUG_PEAKFILE_CACHE
+ std::cout << "PeakFile::getPreview - file size = " << getSize()
+ << ", not generating cache" << std::endl;
+#endif
+
+ }
+ }
+
+ // Check to see if we hit the "lastPreview" cache by comparing the last
+ // query parameters we used.
+ //
+ if (startTime == m_lastPreviewStartTime && endTime == m_lastPreviewEndTime
+ && width == m_lastPreviewWidth && showMinima == m_lastPreviewShowMinima) {
+#ifdef DEBUG_PEAKFILE_CACHE
+ std::cout << "PeakFile::getPreview - hit last preview cache" << std::endl;
+#endif
+
+ return m_lastPreviewCache;
+ } else {
+#ifdef DEBUG_PEAKFILE_CACHE
+ std::cout << "PeakFile::getPreview - last preview " << m_lastPreviewStartTime
+ << " -> " << m_lastPreviewEndTime << ", w " << m_lastPreviewWidth << "; this " << startTime << " -> " << endTime << ", w " << width << std::endl;
+#endif
+
+ }
+
+ // Clear the cache - we need to regenerate it
+ //
+ m_lastPreviewCache.clear();
+
+ int startPeak = getPeak(startTime);
+ int endPeak = getPeak(endTime);
+
+ // Sanity check
+ if (startPeak > endPeak)
+ return m_lastPreviewCache;
+
+ // Actual possible sample length in RealTime
+ //
+ double step = double(endPeak - startPeak) / double(width);
+ std::string peakData;
+ int peakNumber;
+
+#ifdef DEBUG_PEAKFILE_BRIEF
+
+ std::cout << "PeakFile::getPreview - getting preview for \""
+ << m_audioFile->getFilename() << "\"" << endl;
+#endif
+
+ // Get a divisor
+ //
+ float divisor = 0.0f;
+ switch (m_format) {
+ case 1:
+ divisor = SAMPLE_MAX_8BIT;
+ break;
+
+ case 2:
+ divisor = SAMPLE_MAX_16BIT;
+ break;
+
+ default:
+#ifdef DEBUG_PEAKFILE_BRIEF
+
+ std::cout << "PeakFile::getPreview - "
+ << "unsupported peak length format (" << m_format << ")"
+ << endl;
+#endif
+
+ return m_lastPreviewCache;
+ }
+
+ float *hiValues = new float[m_channels];
+ float *loValues = new float[m_channels];
+
+ for (int i = 0; i < width; i++) {
+
+ peakNumber = startPeak + int(double(i) * step);
+ int nextPeakNumber = startPeak + int(double(i + 1) * step);
+
+ // Seek to value
+ //
+ if (!m_peakCache.length()) {
+
+ if (scanToPeak(peakNumber) == false) {
+#ifdef DEBUG_PEAKFILE
+ std::cout << "PeakFile::getPreview: scanToPeak(" << peakNumber << ") failed" << std::endl;
+#endif
+
+ m_lastPreviewCache.push_back(0.0f);
+ }
+ }
+#ifdef DEBUG_PEAKFILE
+ std::cout << "PeakFile::getPreview: step is " << step << ", format * pointsPerValue * chans is " << (m_format * m_pointsPerValue * m_channels) << std::endl;
+ std::cout << "i = " << i << ", peakNumber = " << peakNumber << ", nextPeakNumber = " << nextPeakNumber << std::endl;
+#endif
+
+ for (int ch = 0; ch < m_channels; ch++) {
+ hiValues[ch] = 0.0f;
+ loValues[ch] = 0.0f;
+ }
+
+ // Get peak value over channels
+ //
+ for (int k = 0; peakNumber < nextPeakNumber; ++k) {
+
+ for (int ch = 0; ch < m_channels; ch++) {
+
+ if (!m_peakCache.length()) {
+
+ try {
+ peakData = getBytes(m_inFile, m_format * m_pointsPerValue);
+ } catch (BadSoundFileException e) {
+ // Problem with the get - probably an EOF
+ // return the results so far.
+ //
+#ifdef DEBUG_PEAKFILE
+ std::cout << "PeakFile::getPreview - \"" << e.getMessage() << "\"\n"
+ << endl;
+#endif
+
+ goto done;
+ }
+#ifdef DEBUG_PEAKFILE
+ std::cout << "PeakFile::getPreview - "
+ << "read from file" << std::endl;
+#endif
+
+ } else {
+
+ int valueNum = peakNumber * m_channels + ch;
+ int charNum = valueNum * m_format * m_pointsPerValue;
+ int charLength = m_format * m_pointsPerValue;
+
+ // Get peak value from the cached string if
+ // the value is valid.
+ //
+ if (charNum + charLength <= m_peakCache.length()) {
+ peakData = m_peakCache.substr(charNum, charLength);
+#ifdef DEBUG_PEAKFILE
+
+ std::cout << "PeakFile::getPreview - "
+ << "hit peakCache" << std::endl;
+#endif
+
+ }
+ }
+
+
+ if (peakData.length() != (unsigned int)(m_format *
+ m_pointsPerValue)) {
+ // We didn't get the whole peak block - return what
+ // we've got so far
+ //
+#ifdef DEBUG_PEAKFILE
+ std::cout << "PeakFile::getPreview - "
+ << "failed to get complete peak block"
+ << endl;
+#endif
+
+ goto done;
+ }
+
+ int intDivisor = int(divisor);
+ int inValue =
+ getIntegerFromLittleEndian(peakData.substr(0, m_format));
+
+ while (inValue > intDivisor) {
+ inValue -= (1 << (m_format * 8));
+ }
+
+#ifdef DEBUG_PEAKFILE
+ std::cout << "found potential hivalue " << inValue << std::endl;
+#endif
+
+ if (k == 0 || inValue > hiValues[ch]) {
+ hiValues[ch] = float(inValue);
+ }
+
+ if (m_pointsPerValue == 2) {
+
+ inValue =
+ getIntegerFromLittleEndian(
+ peakData.substr(m_format, m_format));
+
+ while (inValue > intDivisor) {
+ inValue -= (1 << (m_format * 8));
+ }
+
+ if (k == 0 || inValue < loValues[ch]) {
+ loValues[ch] = inValue;
+ }
+ }
+ }
+
+ ++peakNumber;
+ }
+
+ for (int ch = 0; ch < m_channels; ++ch) {
+
+ float value = hiValues[ch] / divisor;
+
+#ifdef DEBUG_PEAKFILE_BRIEF
+
+ std::cout << "VALUE = " << hiValues[ch] / divisor << std::endl;
+#endif
+
+ if (showMinima) {
+ m_lastPreviewCache.push_back(loValues[ch] / divisor);
+ } else {
+ value = fabs(value);
+ if (m_pointsPerValue == 2) {
+ value = std::max(value, fabsf(loValues[ch] / divisor));
+ }
+ m_lastPreviewCache.push_back(value);
+ }
+ }
+ }
+
+done:
+ resetStream();
+ delete[] hiValues;
+ delete[] loValues;
+
+ // We have a good preview in the cache so store our parameters
+ //
+ m_lastPreviewStartTime = startTime;
+ m_lastPreviewEndTime = endTime;
+ m_lastPreviewWidth = width;
+ m_lastPreviewShowMinima = showMinima;
+
+#ifdef DEBUG_PEAKFILE_BRIEF
+
+ std::cout << "Returning " << m_lastPreviewCache.size() << " items" << std::endl;
+#endif
+
+ return m_lastPreviewCache;
+}
+
+int
+PeakFile::getPeak(const RealTime &time)
+{
+ double frames = ((time.sec * 1000000.0) + time.usec()) *
+ m_audioFile->getSampleRate() / 1000000.0;
+ return int(frames / double(m_blockSize));
+}
+
+RealTime
+PeakFile::getTime(int peak)
+{
+ int usecs = int((double)peak * (double)m_blockSize *
+ double(1000000.0) / double(m_audioFile->getSampleRate()));
+ return RealTime(usecs / 1000000, (usecs % 1000000) * 1000);
+}
+
+// Get pairs of split points for areas that exceed a percentage
+// threshold
+//
+std::vector<SplitPointPair>
+PeakFile::getSplitPoints(const RealTime &startTime,
+ const RealTime &endTime,
+ int threshold,
+ const RealTime &minLength)
+{
+ std::vector<SplitPointPair> points;
+ std::string peakData;
+
+ int startPeak = getPeak(startTime);
+ int endPeak = getPeak(endTime);
+
+ if (endPeak < startPeak)
+ return std::vector<SplitPointPair>();
+
+ scanToPeak(startPeak);
+
+ float divisor = 0.0f;
+ switch (m_format) {
+ case 1:
+ divisor = SAMPLE_MAX_8BIT;
+ break;
+
+ case 2:
+ divisor = SAMPLE_MAX_16BIT;
+ break;
+
+ default:
+ return points;
+ }
+
+ float value;
+ float fThreshold = float(threshold) / 100.0;
+ bool belowThreshold = true;
+ RealTime startSplit = RealTime::zeroTime;
+ bool inSplit = false;
+
+ for (int i = startPeak; i < endPeak; i++) {
+ value = 0.0;
+
+ for (int ch = 0; ch < m_channels; ch++) {
+ try {
+ peakData = getBytes(m_inFile, m_format * m_pointsPerValue);
+ } catch (BadSoundFileException e) {
+ std::cerr << "PeakFile::getSplitPoints: "
+ << e.getMessage() << std::endl;
+ break;
+ }
+
+ if (peakData.length() == (unsigned int)(m_format *
+ m_pointsPerValue)) {
+ int peakValue =
+ getIntegerFromLittleEndian(peakData.substr(0, m_format));
+
+ value += fabs(float(peakValue) / divisor);
+ }
+ }
+
+ value /= float(m_channels);
+
+ if (belowThreshold) {
+ if (value > fThreshold) {
+ startSplit = getTime(i);
+ inSplit = true;
+ belowThreshold = false;
+ }
+ } else {
+ if (value < fThreshold && getTime(i) - startSplit > minLength) {
+ // insert values
+ if (inSplit) {
+ points.push_back(SplitPointPair(startSplit, getTime(i)));
+ }
+ inSplit = false;
+ belowThreshold = true;
+ }
+ }
+ }
+
+ // if we've got a split point open the close it
+ if (inSplit) {
+ points.push_back(SplitPointPair(startSplit,
+ getTime(endPeak)));
+ }
+
+ return points;
+}
+
+
+}
+
+
+#include "PeakFile.moc"