changeset 316:3a6725f285d6

* Make RemoteFile far more pervasive, and use it for local files as well so that we can handle both transparently. Make it shallow copy with reference counting, so it can be used by value without having to worry about the cache file lifetime. Use RemoteFile for MainWindow file-open functions, etc
author Chris Cannam
date Thu, 18 Oct 2007 15:31:20 +0000
parents 96ef9746c560
children c324d410b096
files data/fileio/AudioFileReader.h data/fileio/AudioFileReaderFactory.cpp data/fileio/AudioFileReaderFactory.h data/fileio/FileFinder.cpp data/fileio/MP3FileReader.cpp data/fileio/MP3FileReader.h data/fileio/OggVorbisFileReader.cpp data/fileio/OggVorbisFileReader.h data/fileio/QuickTimeFileReader.cpp data/fileio/QuickTimeFileReader.h data/fileio/RemoteFile.cpp data/fileio/RemoteFile.h data/fileio/ResamplingWavFileReader.cpp data/fileio/ResamplingWavFileReader.h data/fileio/WavFileReader.cpp data/fileio/WavFileReader.h data/model/WaveFileModel.cpp data/model/WaveFileModel.h data/model/WritableWaveFileModel.cpp
diffstat 19 files changed, 631 insertions(+), 264 deletions(-) [+]
line wrap: on
line diff
--- a/data/fileio/AudioFileReader.h	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/fileio/AudioFileReader.h	Thu Oct 18 15:31:20 2007 +0000
@@ -19,6 +19,8 @@
 #include <QString>
 #include "model/Model.h" // for SampleBlock
 
+#include "RemoteFile.h"
+
 class AudioFileReader : public QObject
 {
     Q_OBJECT
--- a/data/fileio/AudioFileReaderFactory.cpp	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/fileio/AudioFileReaderFactory.cpp	Thu Oct 18 15:31:20 2007 +0000
@@ -54,24 +54,25 @@
 }
 
 AudioFileReader *
-AudioFileReaderFactory::createReader(QString path, size_t targetRate)
+AudioFileReaderFactory::createReader(RemoteFile source, size_t targetRate)
 {
     QString err;
 
-    std::cerr << "AudioFileReaderFactory::createReader(\"" << path.toStdString() << "\"): Requested rate: " << targetRate << std::endl;
+    std::cerr << "AudioFileReaderFactory::createReader(\"" << source.getLocation().toStdString() << "\"): Requested rate: " << targetRate << std::endl;
+
+    if (!source.isOK() || !source.isAvailable()) {
+        std::cerr << "AudioFileReaderFactory::createReader(\"" << source.getLocation().toStdString() << "\": Source unavailable" << std::endl;
+        return 0;
+    }
 
     AudioFileReader *reader = 0;
 
-    // First try to construct a preferred reader based on the
-    // extension.  If we can't identify one or it fails to load the
-    // file, fall back to trying all readers in no particular order.
+    // Try to construct a preferred reader based on the extension or
+    // MIME type.
 
-    QString ext = QFileInfo(path).suffix().toLower();
-    std::set<QString> extensions;
+    if (WavFileReader::supports(source)) {
 
-    WavFileReader::getSupportedExtensions(extensions);
-    if (extensions.find(ext) != extensions.end()) {
-        reader = new WavFileReader(path);
+        reader = new WavFileReader(source);
 
         if (targetRate != 0 &&
             reader->isOK() &&
@@ -81,7 +82,7 @@
 
             delete reader;
             reader = new ResamplingWavFileReader
-                (path,
+                (source,
                  ResamplingWavFileReader::ResampleThreaded,
                  ResamplingWavFileReader::CacheInTemporaryFile,
                  targetRate);
@@ -91,11 +92,9 @@
 #ifdef HAVE_OGGZ
 #ifdef HAVE_FISHSOUND
     if (!reader) {
-        extensions.clear();
-        OggVorbisFileReader::getSupportedExtensions(extensions);
-        if (extensions.find(ext) != extensions.end()) {
+        if (OggVorbisFileReader::supports(source)) {
             reader = new OggVorbisFileReader
-                (path, 
+                (source,
                  OggVorbisFileReader::DecodeThreaded,
                  OggVorbisFileReader::CacheInTemporaryFile,
                  targetRate);
@@ -106,11 +105,9 @@
 
 #ifdef HAVE_MAD
     if (!reader) {
-        extensions.clear();
-        MP3FileReader::getSupportedExtensions(extensions);
-        if (extensions.find(ext) != extensions.end()) {
+        if (MP3FileReader::supports(source)) {
             reader = new MP3FileReader
-                (path,
+                (source,
                  MP3FileReader::DecodeThreaded,
                  MP3FileReader::CacheInTemporaryFile,
                  targetRate);
@@ -120,11 +117,9 @@
 
 #ifdef HAVE_QUICKTIME
     if (!reader) {
-        extensions.clear();
-        QuickTimeFileReader::getSupportedExtensions(extensions);
-        if (extensions.find(ext) != extensions.end()) {
+        if (QuickTimeFileReader::supports(source)) {
             reader = new QuickTimeFileReader
-                (path,
+                (source,
                  QuickTimeFileReader::DecodeThreaded,
                  QuickTimeFileReader::CacheInTemporaryFile,
                  targetRate);
@@ -133,20 +128,24 @@
 #endif
 
     if (reader) {
-        if (reader->isOK()) return reader;
+        if (reader->isOK()) {
+            std::cerr << "AudioFileReaderFactory: Reader is OK" << std::endl;
+            return reader;
+        }
+        std::cerr << "AudioFileReaderFactory: Preferred reader for "
+                  << "url \"" << source.getLocation().toStdString()
+                  << "\" (content type \""
+                  << source.getContentType().toStdString() << "\") failed";
+
         if (reader->getError() != "") {
-            std::cerr << "AudioFileReaderFactory: Preferred reader for "
-                      << "extension \"" << ext.toStdString() << "\" failed: \""
-                      << reader->getError().toStdString() << "\"" << std::endl;
-        } else {
-            std::cerr << "AudioFileReaderFactory: Preferred reader for "
-                      << "extension \"" << ext.toStdString() << "\" failed"
-                      << std::endl;
-        }            
+            std::cerr << ": \"" << reader->getError().toStdString() << "\"";
+        }
+        std::cerr << std::endl;
         delete reader;
         reader = 0;
     }
 
+    std::cerr << "AudioFileReaderFactory: No reader" << std::endl;
     return reader;
 }
 
--- a/data/fileio/AudioFileReaderFactory.h	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/fileio/AudioFileReaderFactory.h	Thu Oct 18 15:31:20 2007 +0000
@@ -18,6 +18,8 @@
 
 #include <QString>
 
+#include "RemoteFile.h"
+
 class AudioFileReader;
 
 class AudioFileReaderFactory
@@ -42,7 +44,7 @@
      *
      * Caller owns the returned object and must delete it after use.
      */
-    static AudioFileReader *createReader(QString path, size_t targetRate = 0);
+    static AudioFileReader *createReader(RemoteFile source, size_t targetRate = 0);
 };
 
 #endif
--- a/data/fileio/FileFinder.cpp	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/fileio/FileFinder.cpp	Thu Oct 18 15:31:20 2007 +0000
@@ -380,11 +380,11 @@
 {
     if (QFileInfo(location).exists()) return location;
 
-    if (RemoteFile::canHandleScheme(QUrl(location))) {
-        RemoteFile rf(location);
-        bool available = rf.isAvailable();
-        rf.deleteLocalFile();
-        if (available) return location;
+    if (RemoteFile::isRemote(location)) {
+        if (RemoteFile(location).isAvailable()) {
+            std::cerr << "FileFinder::find: ok, it's available... returning" << std::endl;
+            return location;
+        }
     }
 
     QString foundAt = "";
@@ -411,19 +411,17 @@
     QString fileName;
     QString resolved;
 
-    if (RemoteFile::canHandleScheme(QUrl(location))) {
+    if (RemoteFile::isRemote(location)) {
         fileName = QUrl(location).path().section('/', -1, -1,
                                                  QString::SectionSkipEmpty);
     } else {
         fileName = QFileInfo(location).fileName();
     }
 
-    if (RemoteFile::canHandleScheme(QUrl(relativeTo))) {
+    if (RemoteFile::isRemote(relativeTo)) {
         resolved = QUrl(relativeTo).resolved(fileName).toString();
-        RemoteFile rf(resolved);
-        if (!rf.isAvailable()) resolved = "";
+        if (!RemoteFile(resolved).isAvailable()) resolved = "";
         std::cerr << "resolved: " << resolved.toStdString() << std::endl;
-        rf.deleteLocalFile();
     } else {
         resolved = QFileInfo(relativeTo).dir().filePath(fileName);
         if (!QFileInfo(resolved).exists() ||
@@ -481,8 +479,7 @@
                  QLineEdit::Normal, "", &ok);
 
             if (ok && path != "") {
-                RemoteFile rf(path);
-                if (rf.isAvailable()) {
+                if (RemoteFile(path).isAvailable()) {
                     done = true;
                 } else {
                     QMessageBox::critical
@@ -490,7 +487,6 @@
                          tr("URL \"%1\" could not be opened").arg(path));
                     path = "";
                 }
-                rf.deleteLocalFile();
             }
             break;
         }
--- a/data/fileio/MP3FileReader.cpp	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/fileio/MP3FileReader.cpp	Thu Oct 18 15:31:20 2007 +0000
@@ -34,10 +34,11 @@
 #include <QFileInfo>
 #include <QProgressDialog>
 
-MP3FileReader::MP3FileReader(QString path, DecodeMode decodeMode, 
+MP3FileReader::MP3FileReader(RemoteFile source, DecodeMode decodeMode, 
                              CacheMode mode, size_t targetRate) :
     CodedAudioFileReader(mode, targetRate),
-    m_path(path),
+    m_source(source),
+    m_path(source.getLocalFilename()),
     m_decodeThread(0)
 {
     m_channelCount = 0;
@@ -51,20 +52,20 @@
     m_progress = 0;
 
     struct stat stat;
-    if (::stat(path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) {
-	m_error = QString("File %1 does not exist.").arg(path);
+    if (::stat(m_path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) {
+	m_error = QString("File %1 does not exist.").arg(m_path);
 	return;
     }
 
     m_fileSize = stat.st_size;
 
     int fd = -1;
-    if ((fd = ::open(path.toLocal8Bit().data(), O_RDONLY
+    if ((fd = ::open(m_path.toLocal8Bit().data(), O_RDONLY
 #ifdef _WIN32
                      | O_BINARY
 #endif
                      , 0)) < 0) {
-	m_error = QString("Failed to open file %1 for reading.").arg(path);
+	m_error = QString("Failed to open file %1 for reading.").arg(m_path);
 	return;
     }	
 
@@ -86,7 +87,7 @@
         sz = ::read(fd, m_filebuffer + offset, m_fileSize - offset);
         if (sz < 0) {
             m_error = QString("Read error for file %1 (after %2 bytes)")
-                .arg(path).arg(offset);
+                .arg(m_path).arg(offset);
             delete[] m_filebuffer;
             ::close(fd);
             return;
@@ -106,12 +107,12 @@
     if (decodeMode == DecodeAtOnce) {
 
 	m_progress = new QProgressDialog
-	    (QObject::tr("Decoding %1...").arg(QFileInfo(path).fileName()),
+	    (QObject::tr("Decoding %1...").arg(QFileInfo(m_path).fileName()),
 	     QObject::tr("Stop"), 0, 100);
 	m_progress->hide();
 
         if (!decode(m_filebuffer, m_fileSize)) {
-            m_error = QString("Failed to decode file %1.").arg(path);
+            m_error = QString("Failed to decode file %1.").arg(m_path);
         }
         
         delete[] m_filebuffer;
@@ -394,4 +395,26 @@
     extensions.insert("mp3");
 }
 
+bool
+MP3FileReader::supportsExtension(QString extension)
+{
+    std::set<QString> extensions;
+    getSupportedExtensions(extensions);
+    return (extensions.find(extension.toLower()) != extensions.end());
+}
+
+bool
+MP3FileReader::supportsContentType(QString type)
+{
+    return (type == "audio/mpeg");
+}
+
+bool
+MP3FileReader::supports(RemoteFile &source)
+{
+    return (supportsExtension(source.getExtension()) ||
+            supportsContentType(source.getContentType()));
+}
+
+
 #endif
--- a/data/fileio/MP3FileReader.h	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/fileio/MP3FileReader.h	Thu Oct 18 15:31:20 2007 +0000
@@ -35,7 +35,7 @@
         DecodeThreaded // decode in a background thread after construction
     };
 
-    MP3FileReader(QString path,
+    MP3FileReader(RemoteFile source,
                   DecodeMode decodeMode,
                   CacheMode cacheMode,
                   size_t targetRate = 0);
@@ -46,7 +46,10 @@
     virtual QString getTitle() const { return m_title; }
     
     static void getSupportedExtensions(std::set<QString> &extensions);
-    
+    static bool supportsExtension(QString ext);
+    static bool supportsContentType(QString type);
+    static bool supports(RemoteFile &source);
+
     virtual int getDecodeCompletion() const { return m_completion; }
 
     virtual bool isUpdating() const {
@@ -54,6 +57,7 @@
     }
 
 protected:
+    RemoteFile m_source;
     QString m_path;
     QString m_error;
     QString m_title;
--- a/data/fileio/OggVorbisFileReader.cpp	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/fileio/OggVorbisFileReader.cpp	Thu Oct 18 15:31:20 2007 +0000
@@ -32,12 +32,13 @@
 
 static int instances = 0;
 
-OggVorbisFileReader::OggVorbisFileReader(QString path,
+OggVorbisFileReader::OggVorbisFileReader(RemoteFile source,
                                          DecodeMode decodeMode,
                                          CacheMode mode,
                                          size_t targetRate) :
     CodedAudioFileReader(mode, targetRate),
-    m_path(path),
+    m_source(source),
+    m_path(source.getLocalFilename()),
     m_progress(0),
     m_fileSize(0),
     m_bytesRead(0),
@@ -49,15 +50,15 @@
     m_channelCount = 0;
     m_fileRate = 0;
 
-    std::cerr << "OggVorbisFileReader::OggVorbisFileReader(" << path.toLocal8Bit().data() << "): now have " << (++instances) << " instances" << std::endl;
+    std::cerr << "OggVorbisFileReader::OggVorbisFileReader(" << m_path.toLocal8Bit().data() << "): now have " << (++instances) << " instances" << std::endl;
 
     Profiler profiler("OggVorbisFileReader::OggVorbisFileReader", true);
 
-    QFileInfo info(path);
+    QFileInfo info(m_path);
     m_fileSize = info.size();
 
-    if (!(m_oggz = oggz_open(path.toLocal8Bit().data(), OGGZ_READ))) {
-	m_error = QString("File %1 is not an OGG file.").arg(path);
+    if (!(m_oggz = oggz_open(m_path.toLocal8Bit().data(), OGGZ_READ))) {
+	m_error = QString("File %1 is not an OGG file.").arg(m_path);
 	return;
     }
 
@@ -70,7 +71,7 @@
     if (decodeMode == DecodeAtOnce) {
 
 	m_progress = new QProgressDialog
-	    (QObject::tr("Decoding %1...").arg(QFileInfo(path).fileName()),
+	    (QObject::tr("Decoding %1...").arg(QFileInfo(m_path).fileName()),
 	     QObject::tr("Stop"), 0, 100);
 	m_progress->hide();
 
@@ -201,5 +202,26 @@
     extensions.insert("ogg");
 }
 
+bool
+OggVorbisFileReader::supportsExtension(QString extension)
+{
+    std::set<QString> extensions;
+    getSupportedExtensions(extensions);
+    return (extensions.find(extension.toLower()) != extensions.end());
+}
+
+bool
+OggVorbisFileReader::supportsContentType(QString type)
+{
+    return (type == "application/ogg");
+}
+
+bool
+OggVorbisFileReader::supports(RemoteFile &source)
+{
+    return (supportsExtension(source.getExtension()) ||
+            supportsContentType(source.getContentType()));
+}
+
 #endif
 #endif
--- a/data/fileio/OggVorbisFileReader.h	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/fileio/OggVorbisFileReader.h	Thu Oct 18 15:31:20 2007 +0000
@@ -37,7 +37,7 @@
         DecodeThreaded // decode in a background thread after construction
     };
 
-    OggVorbisFileReader(QString path,
+    OggVorbisFileReader(RemoteFile source,
                         DecodeMode decodeMode,
                         CacheMode cacheMode,
                         size_t targetRate = 0);
@@ -48,6 +48,9 @@
     virtual QString getTitle() const { return m_title; }
     
     static void getSupportedExtensions(std::set<QString> &extensions);
+    static bool supportsExtension(QString ext);
+    static bool supportsContentType(QString type);
+    static bool supports(RemoteFile &source);
 
     virtual int getDecodeCompletion() const { return m_completion; }
 
@@ -56,6 +59,7 @@
     }
 
 protected:
+    RemoteFile m_source;
     QString m_path;
     QString m_error;
     QString m_title;
--- a/data/fileio/QuickTimeFileReader.cpp	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/fileio/QuickTimeFileReader.cpp	Thu Oct 18 15:31:20 2007 +0000
@@ -48,12 +48,13 @@
 };
 
 
-QuickTimeFileReader::QuickTimeFileReader(QString path,
+QuickTimeFileReader::QuickTimeFileReader(RemoteFile source,
                                          DecodeMode decodeMode,
                                          CacheMode mode,
                                          size_t targetRate) :
     CodedAudioFileReader(mode, targetRate),
-    m_path(path),
+    m_source(source),
+    m_path(source.getLocalFilename()),
     m_d(new D),
     m_progress(0),
     m_cancelled(false),
@@ -65,7 +66,7 @@
 
     Profiler profiler("QuickTimeFileReader::QuickTimeFileReader", true);
 
-std::cerr << "QuickTimeFileReader: path is \"" << path.toStdString() << "\"" << std::endl;
+std::cerr << "QuickTimeFileReader: path is \"" << m_path.toStdString() << "\"" << std::endl;
 
     long QTversion;
 
@@ -90,8 +91,8 @@
 
     CFURLRef url = CFURLCreateFromFileSystemRepresentation
         (kCFAllocatorDefault,
-         (const UInt8 *)path.toLocal8Bit().data(),
-         (CFIndex)path.length(),
+         (const UInt8 *)m_path.toLocal8Bit().data(),
+         (CFIndex)m_path.length(),
          false);
 
 
@@ -216,7 +217,7 @@
     if (decodeMode == DecodeAtOnce) {
 
 	m_progress = new QProgressDialog
-	    (QObject::tr("Decoding %1...").arg(QFileInfo(path).fileName()),
+	    (QObject::tr("Decoding %1...").arg(QFileInfo(m_path).fileName()),
 	     QObject::tr("Stop"), 0, 100);
 	m_progress->hide();
 
@@ -335,5 +336,32 @@
     extensions.insert("wav");
 }
 
+bool
+QuickTimeFileReader::supportsExtension(QString extension)
+{
+    std::set<QString> extensions;
+    getSupportedExtensions(extensions);
+    return (extensions.find(extension.toLower()) != extensions.end());
+}
+
+bool
+QuickTimeFileReader::supportsContentType(QString type)
+{
+    return (type == "audio/x-aiff" ||
+            type == "audio/x-wav" ||
+            type == "audio/mpeg" ||
+            type == "audio/basic" ||
+            type == "audio/x-aac" ||
+            type == "video/mp4" ||
+            type == "video/quicktime");
+}
+
+bool
+QuickTimeFileReader::supports(RemoteFile &source)
+{
+    return (supportsExtension(source.getExtension()) ||
+            supportsContentType(source.getContentType()));
+}
+
 #endif
 
--- a/data/fileio/QuickTimeFileReader.h	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/fileio/QuickTimeFileReader.h	Thu Oct 18 15:31:20 2007 +0000
@@ -37,7 +37,7 @@
         DecodeThreaded // decode in a background thread after construction
     };
 
-    QuickTimeFileReader(QString path,
+    QuickTimeFileReader(RemoteFile source,
                         DecodeMode decodeMode,
                         CacheMode cacheMode,
                         size_t targetRate = 0);
@@ -47,6 +47,9 @@
     virtual QString getTitle() const { return m_title; }
     
     static void getSupportedExtensions(std::set<QString> &extensions);
+    static bool supportsExtension(QString ext);
+    static bool supportsContentType(QString type);
+    static bool supports(RemoteFile &source);
 
     virtual int getDecodeCompletion() const { return m_completion; }
 
@@ -55,6 +58,7 @@
     }
 
 protected:
+    RemoteFile m_source;
     QString m_path;
     QString m_error;
     QString m_title;
--- a/data/fileio/RemoteFile.cpp	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/fileio/RemoteFile.cpp	Thu Oct 18 15:31:20 2007 +0000
@@ -42,153 +42,300 @@
 QMutex
 RemoteFile::m_mapMutex;
 
-RemoteFile::RemoteFile(QUrl url) :
+RemoteFile::RemoteFile(QString fileOrUrl, bool showProgress) :
+    m_url(fileOrUrl),
+    m_ftp(0),
+    m_http(0),
+    m_localFile(0),
+    m_ok(false),
+    m_lastStatus(0),
+    m_remote(isRemote(fileOrUrl)),
+    m_done(false),
+    m_leaveLocalFile(false),
+    m_progressDialog(0),
+    m_progressShowTimer(this),
+    m_refCounted(false)
+{
+    std::cerr << "RemoteFile::RemoteFile(" << fileOrUrl.toStdString() << ")" << std::endl;
+
+    if (!canHandleScheme(m_url)) {
+        std::cerr << "RemoteFile::RemoteFile: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl;
+        m_errorString = tr("Unsupported scheme in URL");
+        return;
+    }
+
+    init(showProgress);
+
+    if (isRemote() &&
+        (fileOrUrl.contains('%') ||
+         fileOrUrl.contains("--"))) { // for IDNA
+
+        waitForStatus();
+
+        if (!isAvailable()) {
+            // The URL was created on the assumption that the string
+            // was human-readable.  Let's try again, this time
+            // assuming it was already encoded.
+            std::cerr << "RemoteFile::RemoteFile: Failed to retrieve URL \""
+                      << fileOrUrl.toStdString() 
+                      << "\" as human-readable URL; "
+                      << "trying again treating it as encoded URL"
+                      << std::endl;
+            m_url.setEncodedUrl(fileOrUrl.toAscii());
+            init(showProgress);
+        }
+    }
+}
+
+RemoteFile::RemoteFile(QUrl url, bool showProgress) :
     m_url(url),
     m_ftp(0),
     m_http(0),
     m_localFile(0),
     m_ok(false),
     m_lastStatus(0),
+    m_remote(isRemote(url.toString())),
     m_done(false),
+    m_leaveLocalFile(false),
     m_progressDialog(0),
     m_progressShowTimer(this),
-    m_referenced(false)
+    m_refCounted(false)
 {
-    if (!canHandleScheme(url)) {
-        std::cerr << "RemoteFile::RemoteFile: ERROR: Unsupported scheme in URL \"" << url.toString().toStdString() << "\"" << std::endl;
+    std::cerr << "RemoteFile::RemoteFile(" << url.toString().toStdString() << ") [as url]" << std::endl;
+
+    if (!canHandleScheme(m_url)) {
+        std::cerr << "RemoteFile::RemoteFile: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl;
+        m_errorString = tr("Unsupported scheme in URL");
         return;
     }
 
-    QMutexLocker locker(&m_mapMutex);
+    init(showProgress);
+}
 
-    std::cerr << "RemoteFile::RemoteFile: refcount is " << m_refCountMap[m_url] << std::endl;
+RemoteFile::RemoteFile(const RemoteFile &rf) :
+    QObject(),
+    m_url(rf.m_url),
+    m_ftp(0),
+    m_http(0),
+    m_localFile(0),
+    m_ok(rf.m_ok),
+    m_lastStatus(rf.m_lastStatus),
+    m_remote(rf.m_remote),
+    m_done(false),
+    m_leaveLocalFile(false),
+    m_progressDialog(0),
+    m_progressShowTimer(0),
+    m_refCounted(false)
+{
+    std::cerr << "RemoteFile::RemoteFile(" << m_url.toString().toStdString() << ") [copy ctor]" << std::endl;
 
-    if (m_refCountMap[m_url] > 0) {
-        m_refCountMap[m_url]++;
-        m_localFilename = m_remoteLocalMap[m_url];
-        std::cerr << "raising it" << std::endl;
-        m_ok = true;
-        m_done = true;
-        m_referenced = true;
+    if (!canHandleScheme(m_url)) {
+        std::cerr << "RemoteFile::RemoteFile: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl;
+        m_errorString = tr("Unsupported scheme in URL");
         return;
     }
 
-    m_localFilename = createLocalFile(url);
+    if (!isRemote()) {
+        m_localFilename = rf.m_localFilename;
+    } else {
+        QMutexLocker locker(&m_mapMutex);
+        std::cerr << "RemoteFile::RemoteFile(copy ctor): ref count is "
+                  << m_refCountMap[m_url] << std::endl;
+        if (m_refCountMap[m_url] > 0) {
+            m_refCountMap[m_url]++;
+            std::cerr << "raised it to " << m_refCountMap[m_url] << std::endl;
+            m_localFilename = m_remoteLocalMap[m_url];
+            m_refCounted = true;
+        } else {
+            m_ok = false;
+            m_lastStatus = 404;
+        }
+    }
+
+    m_done = true;
+}
+
+RemoteFile::~RemoteFile()
+{
+    std::cerr << "RemoteFile(" << m_url.toString().toStdString() << ")::~RemoteFile" << std::endl;
+
+    cleanup();
+
+    if (isRemote() && !m_leaveLocalFile) deleteCacheFile();
+}
+
+void
+RemoteFile::init(bool showProgress)
+{
+    if (!isRemote()) {
+        m_localFilename = m_url.toLocalFile();
+        m_ok = true;
+        if (!QFileInfo(m_localFilename).exists()) {
+            m_lastStatus = 404;
+        } else {
+            m_lastStatus = 200;
+        }
+        m_done = true;
+        return;
+    }
+
+    if (createCacheFile()) {
+        std::cerr << "RemoteFile::init: Already have this one" << std::endl;
+        m_ok = true;
+        if (!QFileInfo(m_localFilename).exists()) {
+            m_lastStatus = 404;
+        } else {
+            m_lastStatus = 200;
+        }
+        m_done = true;
+        return;
+    }
+
     if (m_localFilename == "") return;
     m_localFile = new QFile(m_localFilename);
     m_localFile->open(QFile::WriteOnly);
 
-    QString scheme = url.scheme().toLower();
+    QString scheme = m_url.scheme().toLower();
+
+    std::cerr << "RemoteFile::init: Don't have local copy of \""
+              << m_url.toString().toStdString() << "\", retrieving" << std::endl;
 
     if (scheme == "http") {
-
-        m_ok = true;
-        m_http = new QHttp(url.host(), url.port(80));
-        connect(m_http, SIGNAL(done(bool)), this, SLOT(done(bool)));
-        connect(m_http, SIGNAL(dataReadProgress(int, int)),
-                this, SLOT(dataReadProgress(int, int)));
-        connect(m_http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)),
-                this, SLOT(httpResponseHeaderReceived(const QHttpResponseHeader &)));
-
-        // I don't quite understand this.  url.path() returns a path
-        // without percent encoding; for example, spaces appear as
-        // literal spaces.  This generally won't work if sent to the
-        // server directly.  You can retrieve a correctly encoded URL
-        // from QUrl using url.toEncoded(), but that gives you the
-        // whole URL; there doesn't seem to be any way to retrieve
-        // only an encoded path.  Furthermore there doesn't seem to be
-        // any way to convert a retrieved path into an encoded path
-        // without explicitly specifying that you don't want the path
-        // separators ("/") to be encoded.  (Besides being painful to
-        // manage, I don't see how this can work correctly in any case
-        // where a percent-encoded "/" is supposed to appear within a
-        // path element?)  There also seems to be no way to retrieve
-        // the path plus query string, i.e. everything that I need to
-        // send to the HTTP server.  And no way for QHttp to take a
-        // QUrl argument.  I'm obviously missing something.
-
-        // So, two ways to do this: query the bits from the URL,
-        // encode them individually, and glue them back together
-        // again...
-        /*
-        QString path = QUrl::toPercentEncoding(url.path(), "/");
-        QList<QPair<QString, QString> > query = url.queryItems();
-        if (!query.empty()) {
-            QStringList q2;
-            for (QList<QPair<QString, QString> >::iterator i = query.begin();
-                 i != query.end(); ++i) {
-                q2.push_back(QString("%1=%3")
-                             .arg(QString(QUrl::toPercentEncoding(i->first)))
-                             .arg(QString(QUrl::toPercentEncoding(i->second))));
-            }
-            path = QString("%1%2%3")
-                .arg(path).arg("?")
-                .arg(q2.join("&"));
-        }
-        */
-
-        // ...or, much simpler but relying on knowledge about the
-        // scheme://host/path/path/query etc format of the URL, we can
-        // get the whole URL ready-encoded and then split it on "/" as
-        // appropriate...
-        
-        QString path = "/" + QString(url.toEncoded()).section('/', 3);
-
-        std::cerr << "RemoteFile: path is \""
-                  << path.toStdString() << "\"" << std::endl;
-
-        m_http->get(path, m_localFile);
-
+        initHttp();
     } else if (scheme == "ftp") {
-
-        m_ok = true;
-        m_ftp = new QFtp;
-        connect(m_ftp, SIGNAL(done(bool)), this, SLOT(done(bool)));
-        connect(m_ftp, SIGNAL(commandFinished(int, bool)),
-                this, SLOT(ftpCommandFinished(int, bool)));
-        connect(m_ftp, SIGNAL(dataTransferProgress(qint64, qint64)),
-                this, SLOT(dataTransferProgress(qint64, qint64)));
-        m_ftp->connectToHost(url.host(), url.port(21));
-
-        QString username = url.userName();
-        if (username == "") {
-            username = "anonymous";
-        }
-
-        QString password = url.password();
-        if (password == "") {
-            password = QString("%1@%2").arg(getenv("USER")).arg(getenv("HOST"));
-        }
-
-        m_ftp->login(username, password);
-
-        QString dirpath = url.path().section('/', 0, -2);
-        QString filename = url.path().section('/', -1);
-
-        if (dirpath == "") dirpath = "/";
-        m_ftp->cd(dirpath);
-        m_ftp->get(filename, m_localFile);
+        initFtp();
+    } else {
+        m_remote = false;
+        m_ok = false;
     }
 
     if (m_ok) {
+        
+        QMutexLocker locker(&m_mapMutex);
+
+        if (m_refCountMap[m_url] > 0) {
+            // someone else has been doing the same thing at the same time,
+            // but has got there first
+            cleanup();
+            m_refCountMap[m_url]++;
+            std::cerr << "RemoteFile::init: Another RemoteFile has got there first, abandoning our download and using theirs" << std::endl;
+            m_localFilename = m_remoteLocalMap[m_url];
+            m_refCounted = true;
+            m_ok = true;
+            if (!QFileInfo(m_localFilename).exists()) {
+                m_lastStatus = 404;
+            }
+            m_done = true;
+            return;
+        }
 
         m_remoteLocalMap[m_url] = m_localFilename;
         m_refCountMap[m_url]++;
-        m_referenced = true;
+        m_refCounted = true;
 
-        m_progressDialog = new QProgressDialog(tr("Downloading %1...").arg(url.toString()), tr("Cancel"), 0, 100);
-        m_progressDialog->hide();
-        connect(&m_progressShowTimer, SIGNAL(timeout()),
-                this, SLOT(showProgressDialog()));
-        connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(cancelled()));
-        m_progressShowTimer.setSingleShot(true);
-        m_progressShowTimer.start(2000);
+        if (showProgress) {
+            m_progressDialog = new QProgressDialog(tr("Downloading %1...").arg(m_url.toString()), tr("Cancel"), 0, 100);
+            m_progressDialog->hide();
+            connect(&m_progressShowTimer, SIGNAL(timeout()),
+                    this, SLOT(showProgressDialog()));
+            connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(cancelled()));
+            m_progressShowTimer.setSingleShot(true);
+            m_progressShowTimer.start(2000);
+        }
     }
 }
 
-RemoteFile::~RemoteFile()
+void
+RemoteFile::initHttp()
 {
-    cleanup();
+    m_ok = true;
+    m_http = new QHttp(m_url.host(), m_url.port(80));
+    connect(m_http, SIGNAL(done(bool)), this, SLOT(done(bool)));
+    connect(m_http, SIGNAL(dataReadProgress(int, int)),
+            this, SLOT(dataReadProgress(int, int)));
+    connect(m_http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)),
+            this, SLOT(httpResponseHeaderReceived(const QHttpResponseHeader &)));
+
+    // I don't quite understand this.  url.path() returns a path
+    // without percent encoding; for example, spaces appear as
+    // literal spaces.  This generally won't work if sent to the
+    // server directly.  You can retrieve a correctly encoded URL
+    // from QUrl using url.toEncoded(), but that gives you the
+    // whole URL; there doesn't seem to be any way to retrieve
+    // only an encoded path.  Furthermore there doesn't seem to be
+    // any way to convert a retrieved path into an encoded path
+    // without explicitly specifying that you don't want the path
+    // separators ("/") to be encoded.  (Besides being painful to
+    // manage, I don't see how this can work correctly in any case
+    // where a percent-encoded "/" is supposed to appear within a
+    // path element?)  There also seems to be no way to retrieve
+    // the path plus query string, i.e. everything that I need to
+    // send to the HTTP server.  And no way for QHttp to take a
+    // QUrl argument.  I'm obviously missing something.
+
+    // So, two ways to do this: query the bits from the URL,
+    // encode them individually, and glue them back together
+    // again...
+/*
+    QString path = QUrl::toPercentEncoding(m_url.path(), "/");
+    QList<QPair<QString, QString> > query = m_url.queryItems();
+    if (!query.empty()) {
+        QStringList q2;
+        for (QList<QPair<QString, QString> >::iterator i = query.begin();
+             i != query.end(); ++i) {
+            q2.push_back(QString("%1=%3")
+                         .arg(QString(QUrl::toPercentEncoding(i->first)))
+                         .arg(QString(QUrl::toPercentEncoding(i->second))));
+        }
+        path = QString("%1%2%3")
+            .arg(path).arg("?")
+            .arg(q2.join("&"));
+    }
+*/
+
+    // ...or, much simpler but relying on knowledge about the
+    // scheme://host/path/path/query etc format of the URL, we can
+    // get the whole URL ready-encoded and then split it on "/" as
+    // appropriate...
+        
+    QString path = "/" + QString(m_url.toEncoded()).section('/', 3);
+
+    std::cerr << "RemoteFile: path is \""
+              << path.toStdString() << "\"" << std::endl;
+        
+    m_http->get(path, m_localFile);
+}
+
+void
+RemoteFile::initFtp()
+{
+    m_ok = true;
+    m_ftp = new QFtp;
+    connect(m_ftp, SIGNAL(done(bool)), this, SLOT(done(bool)));
+    connect(m_ftp, SIGNAL(commandFinished(int, bool)),
+            this, SLOT(ftpCommandFinished(int, bool)));
+    connect(m_ftp, SIGNAL(dataTransferProgress(qint64, qint64)),
+            this, SLOT(dataTransferProgress(qint64, qint64)));
+    m_ftp->connectToHost(m_url.host(), m_url.port(21));
+    
+    QString username = m_url.userName();
+    if (username == "") {
+        username = "anonymous";
+    }
+    
+    QString password = m_url.password();
+    if (password == "") {
+        password = QString("%1@%2").arg(getenv("USER")).arg(getenv("HOST"));
+    }
+    
+    m_ftp->login(username, password);
+    
+    QString dirpath = m_url.path().section('/', 0, -2);
+    QString filename = m_url.path().section('/', -1);
+    
+    if (dirpath == "") dirpath = "/";
+    m_ftp->cd(dirpath);
+    m_ftp->get(filename, m_localFile);
 }
 
 void
@@ -216,22 +363,22 @@
 bool
 RemoteFile::isRemote(QString fileOrUrl)
 {
-    return (fileOrUrl.startsWith("http:") || fileOrUrl.startsWith("ftp:"));
+    QString scheme = QUrl(fileOrUrl).scheme().toLower();
+    return (scheme == "http" || scheme == "ftp");
 }
 
 bool
 RemoteFile::canHandleScheme(QUrl url)
 {
     QString scheme = url.scheme().toLower();
-    return (scheme == "http" || scheme == "ftp");
+    return (scheme == "http" || scheme == "ftp" ||
+            scheme == "file" || scheme == "");
 }
 
 bool
 RemoteFile::isAvailable()
 {
-    while (m_ok && (!m_done && m_lastStatus == 0)) {
-        QApplication::processEvents();
-    }
+    waitForStatus();
     bool available = true;
     if (!m_ok) available = false;
     else available = (m_lastStatus / 100 == 2);
@@ -241,13 +388,28 @@
 }
 
 void
-RemoteFile::wait()
+RemoteFile::waitForStatus()
+{
+    while (m_ok && (!m_done && m_lastStatus == 0)) {
+//        std::cerr << "waitForStatus: processing (last status " << m_lastStatus << ")" << std::endl;
+        QApplication::processEvents();
+    }
+}
+
+void
+RemoteFile::waitForData()
 {
     while (m_ok && !m_done) {
         QApplication::processEvents();
     }
 }
 
+void
+RemoteFile::setLeaveLocalFile(bool leave)
+{
+    m_leaveLocalFile = leave;
+}
+
 bool
 RemoteFile::isOK() const
 {
@@ -260,6 +422,18 @@
     return m_done;
 }
 
+bool
+RemoteFile::isRemote() const
+{
+    return m_remote;
+}
+
+QString
+RemoteFile::getLocation() const
+{
+    return m_url.toString();
+}
+
 QString
 RemoteFile::getLocalFilename() const
 {
@@ -267,6 +441,22 @@
 }
 
 QString
+RemoteFile::getContentType() const
+{
+    return m_contentType;
+}
+
+QString
+RemoteFile::getExtension() const
+{
+    if (m_localFilename != "") {
+        return QFileInfo(m_localFilename).suffix().toLower();
+    } else {
+        return QFileInfo(m_url.toLocalFile()).suffix().toLower();
+    }
+}
+
+QString
 RemoteFile::getErrorString() const
 {
     return m_errorString;
@@ -339,8 +529,9 @@
 void
 RemoteFile::cancelled()
 {
-    deleteLocalFile();
     m_done = true;
+    cleanup();
+
     m_ok = false;
     m_errorString = tr("Download cancelled");
 }
@@ -380,7 +571,8 @@
     }
 
     if (error) {
-        deleteLocalFile();
+        std::cerr << "RemoteFile::done: error is " << error << ", deleting cache file" << std::endl;
+        deleteCacheFile();
     }
 
     m_ok = !error;
@@ -389,21 +581,29 @@
 }
 
 void
-RemoteFile::deleteLocalFile()
+RemoteFile::deleteCacheFile()
 {
-//    std::cerr << "RemoteFile::deleteLocalFile" << std::endl;
+    std::cerr << "RemoteFile::deleteCacheFile(\"" << m_localFilename.toStdString() << "\")" << std::endl;
 
     cleanup();
 
-    if (m_localFilename == "") return;
+    if (m_localFilename == "") {
+        return;
+    }
 
-    if (m_referenced) {
+    if (!isRemote()) {
+        std::cerr << "not a cache file" << std::endl;
+        return;
+    }
+
+    if (m_refCounted) {
 
         QMutexLocker locker(&m_mapMutex);
-        m_referenced = false;
+        m_refCounted = false;
 
         if (m_refCountMap[m_url] > 0) {
             m_refCountMap[m_url]--;
+            std::cerr << "reduced ref count to " << m_refCountMap[m_url] << std::endl;
             if (m_refCountMap[m_url] > 0) {
                 m_done = true;
                 return;
@@ -414,8 +614,9 @@
     m_fileCreationMutex.lock();
 
     if (!QFile(m_localFilename).remove()) {
-        std::cerr << "RemoteFile::deleteLocalFile: ERROR: Failed to delete file \"" << m_localFilename.toStdString() << "\"" << std::endl;
+        std::cerr << "RemoteFile::deleteCacheFile: ERROR: Failed to delete file \"" << m_localFilename.toStdString() << "\"" << std::endl;
     } else {
+        std::cerr << "RemoteFile::deleteCacheFile: Deleted cache file \"" << m_localFilename.toStdString() << "\"" << std::endl;
         m_localFilename = "";
     }
 
@@ -430,19 +631,33 @@
     if (m_progressDialog) m_progressDialog->show();
 }
 
-QString
-RemoteFile::createLocalFile(QUrl url)
+bool
+RemoteFile::createCacheFile()
 {
+    {
+        QMutexLocker locker(&m_mapMutex);
+
+        std::cerr << "RemoteFile::createCacheFile: refcount is " << m_refCountMap[m_url] << std::endl;
+
+        if (m_refCountMap[m_url] > 0) {
+            m_refCountMap[m_url]++;
+            m_localFilename = m_remoteLocalMap[m_url];
+            std::cerr << "raised it to " << m_refCountMap[m_url] << std::endl;
+            m_refCounted = true;
+            return true;
+        }
+    }
+
     QDir dir;
     try {
         dir = TempDirectory::getInstance()->getSubDirectoryPath("download");
     } catch (DirectoryCreationFailed f) {
-        std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl;
+        std::cerr << "RemoteFile::createCacheFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl;
         return "";
     }
 
-    QString filepart = url.path().section('/', -1, -1,
-                                          QString::SectionSkipEmpty);
+    QString filepart = m_url.path().section('/', -1, -1,
+                                            QString::SectionSkipEmpty);
 
     QString extension = filepart.section('.', -1);
     QString base = filepart;
@@ -461,17 +676,18 @@
 
     QString filepath(dir.filePath(filename));
 
-    std::cerr << "RemoteFile::createLocalFile: URL is \"" << url.toString().toStdString() << "\", dir is \"" << dir.path().toStdString() << "\", base \"" << base.toStdString() << "\", extension \"" << extension.toStdString() << "\", filebase \"" << filename.toStdString() << "\", filename \"" << filepath.toStdString() << "\"" << std::endl;
+    std::cerr << "RemoteFile::createCacheFile: URL is \"" << m_url.toString().toStdString() << "\", dir is \"" << dir.path().toStdString() << "\", base \"" << base.toStdString() << "\", extension \"" << extension.toStdString() << "\", filebase \"" << filename.toStdString() << "\", filename \"" << filepath.toStdString() << "\"" << std::endl;
 
-    m_fileCreationMutex.lock();
+    QMutexLocker fcLocker(&m_fileCreationMutex);
+
     ++m_count;
 
     if (QFileInfo(filepath).exists() ||
         !QFile(filepath).open(QFile::WriteOnly)) {
 
-        std::cerr << "RemoteFile::createLocalFile: Failed to create local file \""
+        std::cerr << "RemoteFile::createCacheFile: Failed to create local file \""
                   << filepath.toStdString() << "\" for URL \""
-                  << url.toString().toStdString() << "\" (or file already exists): appending suffix instead" << std::endl;
+                  << m_url.toString().toStdString() << "\" (or file already exists): appending suffix instead" << std::endl;
 
 
         if (extension == "") {
@@ -484,20 +700,19 @@
         if (QFileInfo(filepath).exists() ||
             !QFile(filepath).open(QFile::WriteOnly)) {
 
-            std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create local file \""
+            std::cerr << "RemoteFile::createCacheFile: ERROR: Failed to create local file \""
                       << filepath.toStdString() << "\" for URL \""
-                      << url.toString().toStdString() << "\" (or file already exists)" << std::endl;
+                      << m_url.toString().toStdString() << "\" (or file already exists)" << std::endl;
 
-            m_fileCreationMutex.unlock();
             return "";
         }
     }
 
-    m_fileCreationMutex.unlock();
+    std::cerr << "RemoteFile::createCacheFile: url "
+              << m_url.toString().toStdString() << " -> local filename "
+              << filepath.toStdString() << std::endl;
+    
+    m_localFilename = filepath;
 
-    std::cerr << "RemoteFile::createLocalFile: url "
-              << url.toString().toStdString() << " -> local filename "
-              << filepath.toStdString() << std::endl;
-
-    return filepath;
+    return false;
 }
--- a/data/fileio/RemoteFile.h	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/fileio/RemoteFile.h	Thu Oct 18 15:31:20 2007 +0000
@@ -34,23 +34,30 @@
     Q_OBJECT
 
 public:
-    RemoteFile(QUrl url);
+    RemoteFile(QString fileOrUrl, bool showProgress = true);
+    RemoteFile(QUrl url, bool showProgress = true);
+    RemoteFile(const RemoteFile &);
+
     virtual ~RemoteFile();
 
     bool isAvailable();
 
-    void wait();
+    void waitForStatus();
+    void waitForData();
+
+    void setLeaveLocalFile(bool leave);
 
     bool isOK() const;
     bool isDone() const;
+    bool isRemote() const;
 
+    QString getLocation() const;
+    QString getLocalFilename() const;
     QString getContentType() const;
+    QString getExtension() const;
 
-    QString getLocalFilename() const;
     QString getErrorString() const;
 
-    void deleteLocalFile();
-
     static bool isRemote(QString fileOrUrl);
     static bool canHandleScheme(QUrl url);
 
@@ -68,6 +75,8 @@
     void cancelled();
 
 protected:
+    RemoteFile &operator=(const RemoteFile &); // not provided
+
     QUrl m_url;
     QFtp *m_ftp;
     QHttp *m_http;
@@ -77,7 +86,9 @@
     QString m_contentType;
     bool m_ok;
     int m_lastStatus;
+    bool m_remote;
     bool m_done;
+    bool m_leaveLocalFile;
     QProgressDialog *m_progressDialog;
     QTimer m_progressShowTimer;
 
@@ -86,11 +97,18 @@
     static RemoteRefCountMap m_refCountMap;
     static RemoteLocalMap m_remoteLocalMap;
     static QMutex m_mapMutex;
-    bool m_referenced;
+    bool m_refCounted;
+
+    void init(bool showProgress);
+    void initHttp();
+    void initFtp();
 
     void cleanup();
 
-    QString createLocalFile(QUrl url);
+    // Create a local file for m_url.  If it already existed, return true.
+    // The local filename is stored in m_localFilename.
+    bool createCacheFile();
+    void deleteCacheFile();
 
     static QMutex m_fileCreationMutex;
     static int m_count;
--- a/data/fileio/ResamplingWavFileReader.cpp	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/fileio/ResamplingWavFileReader.cpp	Thu Oct 18 15:31:20 2007 +0000
@@ -22,12 +22,13 @@
 #include <QFileInfo>
 #include <QApplication>
 
-ResamplingWavFileReader::ResamplingWavFileReader(QString path,
+ResamplingWavFileReader::ResamplingWavFileReader(RemoteFile source,
 						 ResampleMode resampleMode,
 						 CacheMode mode,
 						 size_t targetRate) :
     CodedAudioFileReader(mode, targetRate),
-    m_path(path),
+    m_source(source),
+    m_path(source.getLocalFilename()),
     m_cancelled(false),
     m_processed(0),
     m_completion(0),
@@ -39,11 +40,11 @@
     m_fileRate = 0;
 
     std::cerr << "ResamplingWavFileReader::ResamplingWavFileReader(\""
-              << path.toStdString() << "\"): rate " << targetRate << std::endl;
+              << m_path.toStdString() << "\"): rate " << targetRate << std::endl;
 
     Profiler profiler("ResamplingWavFileReader::ResamplingWavFileReader", true);
 
-    m_original = new WavFileReader(path);
+    m_original = new WavFileReader(m_path);
     if (!m_original->isOK()) {
         m_error = m_original->getError();
         return;
@@ -57,7 +58,7 @@
     if (resampleMode == ResampleAtOnce) {
 
 	m_progress = new QProgressDialog
-	    (QObject::tr("Resampling %1...").arg(QFileInfo(path).fileName()),
+	    (QObject::tr("Resampling %1...").arg(QFileInfo(m_path).fileName()),
 	     QObject::tr("Stop"), 0, 100);
 	m_progress->hide();
 
@@ -167,4 +168,22 @@
     WavFileReader::getSupportedExtensions(extensions);
 }
 
+bool
+ResamplingWavFileReader::supportsExtension(QString extension)
+{
+    return WavFileReader::supportsExtension(extension);
+}
 
+bool
+ResamplingWavFileReader::supportsContentType(QString type)
+{
+    return WavFileReader::supportsContentType(type);
+}
+
+bool
+ResamplingWavFileReader::supports(RemoteFile &source)
+{
+    return WavFileReader::supports(source);
+}
+
+
--- a/data/fileio/ResamplingWavFileReader.h	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/fileio/ResamplingWavFileReader.h	Thu Oct 18 15:31:20 2007 +0000
@@ -33,15 +33,17 @@
         ResampleThreaded // resample in a background thread after construction
     };
 
-    ResamplingWavFileReader(QString path,
+    ResamplingWavFileReader(RemoteFile source,
                             ResampleMode resampleMode,
                             CacheMode cacheMode,
                             size_t targetRate = 0);
     virtual ~ResamplingWavFileReader();
 
     virtual QString getError() const { return m_error; }
-
     static void getSupportedExtensions(std::set<QString> &extensions);
+    static bool supportsExtension(QString ext);
+    static bool supportsContentType(QString type);
+    static bool supports(RemoteFile &source);
 
     virtual int getDecodeCompletion() const { return m_completion; }
 
@@ -50,6 +52,7 @@
     }
 
 protected:
+    RemoteFile m_source;
     QString m_path;
     QString m_error;
     bool m_cancelled;
--- a/data/fileio/WavFileReader.cpp	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/fileio/WavFileReader.cpp	Thu Oct 18 15:31:20 2007 +0000
@@ -18,10 +18,12 @@
 #include <iostream>
 
 #include <QMutexLocker>
+#include <QFileInfo>
 
-WavFileReader::WavFileReader(QString path, bool fileUpdating) :
+WavFileReader::WavFileReader(RemoteFile source, bool fileUpdating) :
     m_file(0),
-    m_path(path),
+    m_source(source),
+    m_path(source.getLocalFilename()),
     m_buffer(0),
     m_bufsiz(0),
     m_lastStart(0),
@@ -171,6 +173,7 @@
     if (sf_command(0, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(count))) {
         extensions.insert("wav");
         extensions.insert("aiff");
+        extensions.insert("aifc");
         extensions.insert("aif");
         return;
     }
@@ -179,7 +182,32 @@
     for (int i = 0; i < count; ++i) {
         info.format = i;
         if (!sf_command(0, SFC_GET_FORMAT_MAJOR, &info, sizeof(info))) {
-            extensions.insert(info.extension);
+            extensions.insert(QString(info.extension).toLower());
         }
     }
 }
+
+bool
+WavFileReader::supportsExtension(QString extension)
+{
+    std::set<QString> extensions;
+    getSupportedExtensions(extensions);
+    return (extensions.find(extension.toLower()) != extensions.end());
+}
+
+bool
+WavFileReader::supportsContentType(QString type)
+{
+    return (type == "audio/x-wav" ||
+            type == "audio/x-aiff" ||
+            type == "audio/basic");
+}
+
+bool
+WavFileReader::supports(RemoteFile &source)
+{
+    return (supportsExtension(source.getExtension()) ||
+            supportsContentType(source.getContentType()));
+}
+
+
--- a/data/fileio/WavFileReader.h	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/fileio/WavFileReader.h	Thu Oct 18 15:31:20 2007 +0000
@@ -26,7 +26,7 @@
 class WavFileReader : public AudioFileReader
 {
 public:
-    WavFileReader(QString path, bool fileUpdating = false);
+    WavFileReader(RemoteFile source, bool fileUpdating = false);
     virtual ~WavFileReader();
 
     virtual QString getError() const { return m_error; }
@@ -39,6 +39,9 @@
 				      SampleBlock &frames) const;
     
     static void getSupportedExtensions(std::set<QString> &extensions);
+    static bool supportsExtension(QString ext);
+    static bool supportsContentType(QString type);
+    static bool supports(RemoteFile &source);
 
     virtual int getDecodeCompletion() const { return 100; }
 
@@ -51,6 +54,7 @@
     SF_INFO m_fileInfo;
     SNDFILE *m_file;
 
+    RemoteFile m_source;
     QString m_path;
     QString m_error;
 
--- a/data/model/WaveFileModel.cpp	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/model/WaveFileModel.cpp	Thu Oct 18 15:31:20 2007 +0000
@@ -39,8 +39,9 @@
 PowerOfSqrtTwoZoomConstraint
 WaveFileModel::m_zoomConstraint;
 
-WaveFileModel::WaveFileModel(QString path, size_t targetRate) :
-    m_path(path),
+WaveFileModel::WaveFileModel(RemoteFile source, size_t targetRate) :
+    m_source(source),
+    m_path(source.getLocation()),
     m_myReader(true),
     m_startFrame(0),
     m_fillThread(0),
@@ -48,31 +49,22 @@
     m_lastFillExtent(0),
     m_exiting(false)
 {
-    m_reader = AudioFileReaderFactory::createReader(path, targetRate);
-    if (m_reader) std::cerr << "WaveFileModel::WaveFileModel: reader rate: " << m_reader->getSampleRate() << std::endl;
+    m_source.waitForData();
+    if (m_source.isOK()) {
+        m_reader = AudioFileReaderFactory::createReader(m_source, targetRate);
+        if (m_reader) {
+            std::cerr << "WaveFileModel::WaveFileModel: reader rate: "
+                      << m_reader->getSampleRate() << std::endl;
+        }
+    }
     if (m_reader) setObjectName(m_reader->getTitle());
-    if (objectName() == "") setObjectName(QFileInfo(path).fileName());
+    if (objectName() == "") setObjectName(QFileInfo(m_path).fileName());
     if (isOK()) fillCache();
 }
 
-WaveFileModel::WaveFileModel(QString path, QString originalLocation, size_t targetRate) :
-    m_path(originalLocation),
-    m_myReader(true),
-    m_startFrame(0),
-    m_fillThread(0),
-    m_updateTimer(0),
-    m_lastFillExtent(0),
-    m_exiting(false)
-{
-    m_reader = AudioFileReaderFactory::createReader(path, targetRate);
-    if (m_reader) std::cerr << "WaveFileModel::WaveFileModel: reader rate: " << m_reader->getSampleRate() << std::endl;
-    if (m_reader) setObjectName(m_reader->getTitle());
-    if (objectName() == "") setObjectName(QFileInfo(originalLocation).fileName());
-    if (isOK()) fillCache();
-}
-
-WaveFileModel::WaveFileModel(QString path, AudioFileReader *reader) :
-    m_path(path),
+WaveFileModel::WaveFileModel(RemoteFile source, AudioFileReader *reader) :
+    m_source(source),
+    m_path(source.getLocation()),
     m_myReader(false),
     m_startFrame(0),
     m_fillThread(0),
@@ -82,7 +74,7 @@
 {
     m_reader = reader;
     if (m_reader) setObjectName(m_reader->getTitle());
-    if (objectName() == "") setObjectName(QFileInfo(path).fileName());
+    if (objectName() == "") setObjectName(QFileInfo(m_path).fileName());
     fillCache();
 }
 
@@ -131,7 +123,7 @@
 Model *
 WaveFileModel::clone() const
 {
-    WaveFileModel *model = new WaveFileModel(m_path);
+    WaveFileModel *model = new WaveFileModel(m_source);
     return model;
 }
 
--- a/data/model/WaveFileModel.h	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/model/WaveFileModel.h	Thu Oct 18 15:31:20 2007 +0000
@@ -20,6 +20,8 @@
 #include <QMutex>
 #include <QTimer>
 
+#include "data/fileio/RemoteFile.h"
+
 #include "RangeSummarisableTimeValueModel.h"
 #include "PowerOfSqrtTwoZoomConstraint.h"
 
@@ -32,9 +34,8 @@
     Q_OBJECT
 
 public:
-    WaveFileModel(QString path, size_t targetRate = 0);
-    WaveFileModel(QString path, QString originalLocation, size_t targetRate = 0);
-    WaveFileModel(QString originalLocation, AudioFileReader *reader);
+    WaveFileModel(RemoteFile source, size_t targetRate = 0);
+    WaveFileModel(RemoteFile source, AudioFileReader *reader);
     ~WaveFileModel();
 
     bool isOK() const;
@@ -102,7 +103,8 @@
     };
          
     void fillCache();
-    
+
+    RemoteFile m_source;
     QString m_path;
     AudioFileReader *m_reader;
     bool m_myReader;
--- a/data/model/WritableWaveFileModel.cpp	Thu Oct 18 10:24:26 2007 +0000
+++ b/data/model/WritableWaveFileModel.cpp	Thu Oct 18 15:31:20 2007 +0000
@@ -60,7 +60,9 @@
         return;
     }
 
-    m_reader = new WavFileReader(m_writer->getPath(), true);
+    RemoteFile source(m_writer->getPath());
+
+    m_reader = new WavFileReader(source, true);
     if (!m_reader->getError().isEmpty()) {
         std::cerr << "WritableWaveFileModel: Error in creating wave file reader" << std::endl;
         delete m_reader;
@@ -68,7 +70,7 @@
         return;
     }
     
-    m_model = new WaveFileModel(m_writer->getPath(), m_reader);
+    m_model = new WaveFileModel(source, m_reader);
     if (!m_model->isOK()) {
         std::cerr << "WritableWaveFileModel: Error in creating wave file model" << std::endl;
         delete m_model;