changeset 1098:329ddaf7415d simple-fft-model

Store temporary audio files in memory if we have plenty of it
author Chris Cannam
date Mon, 15 Jun 2015 14:35:37 +0100
parents abc309f507ae
children 0c351e061945
files base/StorageAdviser.cpp data/fileio/AudioFileReaderFactory.cpp data/fileio/AudioFileReaderFactory.h data/fileio/AudioFileSizeEstimator.cpp data/fileio/AudioFileSizeEstimator.h data/fileio/CodedAudioFileReader.cpp data/fileio/FileSource.h svcore.pro
diffstat 8 files changed, 195 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/base/StorageAdviser.cpp	Mon Jun 15 12:38:50 2015 +0100
+++ b/base/StorageAdviser.cpp	Mon Jun 15 14:35:37 2015 +0100
@@ -22,7 +22,7 @@
 
 #include <iostream>
 
-//#define DEBUG_STORAGE_ADVISER 1
+#define DEBUG_STORAGE_ADVISER 1
 
 size_t StorageAdviser::m_discPlanned = 0;
 size_t StorageAdviser::m_memoryPlanned = 0;
@@ -36,9 +36,9 @@
 			  size_t maximumSize)
 {
 #ifdef DEBUG_STORAGE_ADVISER
-    SVDEBUG << "StorageAdviser::recommend: Criteria " << criteria 
-              << ", minimumSize " << minimumSize
-              << ", maximumSize " << maximumSize << endl;
+    cerr << "StorageAdviser::recommend: Criteria " << criteria 
+         << ", minimumSize " << minimumSize
+         << ", maximumSize " << maximumSize << endl;
 #endif
 
     if (m_baseRecommendation != NoRecommendation) {
@@ -181,6 +181,10 @@
         }
     }
 
+#ifdef DEBUG_STORAGE_ADVISER
+    cerr << "StorageAdviser: returning recommendation " << recommendation << endl;
+#endif
+    
     return Recommendation(recommendation);
 }
 
--- a/data/fileio/AudioFileReaderFactory.cpp	Mon Jun 15 12:38:50 2015 +0100
+++ b/data/fileio/AudioFileReaderFactory.cpp	Mon Jun 15 14:35:37 2015 +0100
@@ -21,6 +21,9 @@
 #include "MP3FileReader.h"
 #include "QuickTimeFileReader.h"
 #include "CoreAudioFileReader.h"
+#include "AudioFileSizeEstimator.h"
+
+#include "base/StorageAdviser.h"
 
 #include <QString>
 #include <QFileInfo>
@@ -98,9 +101,22 @@
 
     AudioFileReader *reader = 0;
 
+    sv_frame_t estimatedSamples = 
+        AudioFileSizeEstimator::estimate(source, targetRate);
+    
     CodedAudioFileReader::CacheMode cacheMode =
         CodedAudioFileReader::CacheInTemporaryFile;
 
+    if (estimatedSamples > 0) {
+        size_t kb = (estimatedSamples * sizeof(float)) / 1024;
+        StorageAdviser::Recommendation rec =
+            StorageAdviser::recommend(StorageAdviser::SpeedCritical, kb, kb);
+        if (rec == StorageAdviser::UseMemory ||
+            rec == StorageAdviser::PreferMemory) {
+            cacheMode = CodedAudioFileReader::CacheInMemory;
+        }
+    }
+    
     CodedAudioFileReader::DecodeMode decodeMode =
         (threading ?
          CodedAudioFileReader::DecodeThreaded :
--- a/data/fileio/AudioFileReaderFactory.h	Mon Jun 15 12:38:50 2015 +0100
+++ b/data/fileio/AudioFileReaderFactory.h	Mon Jun 15 14:35:37 2015 +0100
@@ -13,8 +13,8 @@
     COPYING included with this distribution for more information.
 */
 
-#ifndef _AUDIO_FILE_READER_FACTORY_H_
-#define _AUDIO_FILE_READER_FACTORY_H_
+#ifndef AUDIO_FILE_READER_FACTORY_H
+#define AUDIO_FILE_READER_FACTORY_H
 
 #include <QString>
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/fileio/AudioFileSizeEstimator.cpp	Mon Jun 15 14:35:37 2015 +0100
@@ -0,0 +1,101 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    
+    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 "AudioFileSizeEstimator.h"
+
+#include "WavFileReader.h"
+
+#include <QFile>
+
+sv_frame_t
+AudioFileSizeEstimator::estimate(FileSource source,
+				 sv_samplerate_t targetRate)
+{
+    sv_frame_t estimate = 0;
+    
+    // Most of our file readers don't know the sample count until
+    // after they've finished decoding. This is an exception:
+
+    WavFileReader *reader = new WavFileReader(source);
+    if (reader->isOK() &&
+	reader->getChannelCount() > 0 &&
+	reader->getFrameCount() > 0) {
+	sv_frame_t samples =
+	    reader->getFrameCount() * reader->getChannelCount();
+	sv_samplerate_t rate = reader->getSampleRate();
+	if (targetRate != 0.0 && targetRate != rate) {
+	    samples = sv_frame_t(double(samples) * targetRate / rate);
+	}
+	delete reader;
+	estimate = samples;
+    }
+
+    if (estimate == 0) {
+
+	// The remainder just makes an estimate based on the file size
+	// and extension. We don't even know its sample rate at this
+	// point, so the following is a wild guess.
+	
+	double rateRatio = 1.0;
+	if (targetRate != 0.0) {
+	    rateRatio = targetRate / 44100.0;
+	}
+    
+	QString extension = source.getExtension();
+
+	source.waitForData();
+	if (!source.isOK()) return 0;
+
+	sv_frame_t sz = 0;
+	{
+	    QFile f(source.getLocalFilename());
+	    if (f.open(QFile::ReadOnly)) {
+		cerr << "opened file, size is "  << f.size() << endl;
+		sz = f.size();
+		f.close();
+	    }
+	}
+
+	if (extension == "ogg" || extension == "oga" ||
+	    extension == "m4a" || extension == "mp3" ||
+	    extension == "wma") {
+
+	    // Usually a lossy file. Compression ratios can vary
+	    // dramatically, but don't usually exceed about 20x compared
+	    // to 16-bit PCM (e.g. a 128kbps mp3 has 11x ratio over WAV at
+	    // 44.1kHz). We can estimate the number of samples to be file
+	    // size x 20, divided by 2 as we're comparing with 16-bit PCM.
+
+	    estimate = sv_frame_t(double(sz) * 10 * rateRatio);
+	}
+
+	if (extension == "flac") {
+	
+	    // FLAC usually takes up a bit more than half the space of
+	    // 16-bit PCM. So the number of 16-bit samples is roughly the
+	    // same as the file size in bytes. As above, let's be
+	    // conservative.
+
+	    estimate = sv_frame_t(double(sz) * 1.2 * rateRatio);
+	}
+
+	cerr << "AudioFileSizeEstimator: for extension " << extension << ", estimate = " << estimate << endl;
+    
+    }
+
+    cerr << "estimate = " << estimate << endl;
+    
+    return estimate;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/fileio/AudioFileSizeEstimator.h	Mon Jun 15 14:35:37 2015 +0100
@@ -0,0 +1,49 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    
+    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.
+*/
+
+#ifndef AUDIO_FILE_SIZE_ESTIMATOR_H
+#define AUDIO_FILE_SIZE_ESTIMATOR_H
+
+#include "base/BaseTypes.h"
+#include "data/fileio/FileSource.h"
+
+/**
+ * Estimate the number of samples in an audio file. For many
+ * compressed files this returns only a very approximate estimate,
+ * based on a rough estimate of compression ratio. Initially we're
+ * only aiming for a conservative estimate for purposes like "will
+ * this file fit in memory?" (and if unsure, say no).
+ */
+class AudioFileSizeEstimator
+{
+public:
+    /**
+     * Return an estimate of the number of samples (across all
+     * channels) in the given audio file, once it has been decoded and
+     * (if applicable) resampled to the given rate.
+     *
+     * This function is intended to be reasonably fast -- it may open
+     * the file, but it should not do any decoding. (However, if the
+     * file source is remote, it will probably be downloaded in its
+     * entirety before anything can be estimated.)
+     *
+     * The returned value is an estimate, and is deliberately usually
+     * on the high side. If the estimator has no idea at all, this
+     * will return 0.
+     */
+    static sv_frame_t estimate(FileSource source,
+			       sv_samplerate_t targetRate = 0);
+};
+
+#endif
--- a/data/fileio/CodedAudioFileReader.cpp	Mon Jun 15 12:38:50 2015 +0100
+++ b/data/fileio/CodedAudioFileReader.cpp	Mon Jun 15 14:35:37 2015 +0100
@@ -21,6 +21,7 @@
 #include "base/Profiler.h"
 #include "base/Serialiser.h"
 #include "base/Resampler.h"
+#include "base/StorageAdviser.h"
 
 #include <stdint.h>
 #include <iostream>
@@ -59,7 +60,7 @@
     QMutexLocker locker(&m_cacheMutex);
 
     endSerialised();
-
+    
     if (m_cacheFileWritePtr) sf_close(m_cacheFileWritePtr);
 
     SVDEBUG << "CodedAudioFileReader::~CodedAudioFileReader: deleting cache file reader" << endl;
@@ -75,6 +76,12 @@
 
     delete m_resampler;
     delete[] m_resampleBuffer;
+
+    if (!m_data.empty()) {
+        StorageAdviser::notifyDoneAllocation
+            (StorageAdviser::MemoryAllocation,
+             (m_data.size() * sizeof(float)) / 1024);
+    }
 }
 
 void
@@ -294,9 +301,16 @@
     m_resampler = 0;
 
     if (m_cacheMode == CacheInTemporaryFile) {
+
         sf_close(m_cacheFileWritePtr);
         m_cacheFileWritePtr = 0;
         if (m_cacheFileReader) m_cacheFileReader->updateFrameCount();
+
+    } else {
+        // I know, I know, we already allocated it...
+        StorageAdviser::notifyPlannedAllocation
+            (StorageAdviser::MemoryAllocation,
+             (m_data.size() * sizeof(float)) / 1024);
     }
 }
 
--- a/data/fileio/FileSource.h	Mon Jun 15 12:38:50 2015 +0100
+++ b/data/fileio/FileSource.h	Mon Jun 15 14:35:37 2015 +0100
@@ -167,7 +167,8 @@
     QString getContentType() const;
 
     /**
-     * Return the file extension for this file, if any.
+     * Return the file extension for this file, if any. The returned
+     * extension is always lower-case.
      */
     QString getExtension() const;
 
--- a/svcore.pro	Mon Jun 15 12:38:50 2015 +0100
+++ b/svcore.pro	Mon Jun 15 14:35:37 2015 +0100
@@ -121,6 +121,7 @@
 HEADERS += data/fft/FFTapi.h \
            data/fileio/AudioFileReader.h \
            data/fileio/AudioFileReaderFactory.h \
+           data/fileio/AudioFileSizeEstimator.h \
            data/fileio/BZipFileDevice.h \
            data/fileio/CachedFile.h \
            data/fileio/CodedAudioFileReader.h \
@@ -179,6 +180,7 @@
 SOURCES += data/fft/FFTapi.cpp \
            data/fileio/AudioFileReader.cpp \
            data/fileio/AudioFileReaderFactory.cpp \
+           data/fileio/AudioFileSizeEstimator.cpp \
            data/fileio/BZipFileDevice.cpp \
            data/fileio/CachedFile.cpp \
            data/fileio/CodedAudioFileReader.cpp \