Chris@148: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@148: Chris@148: /* Chris@148: Sonic Visualiser Chris@148: An audio file viewer and annotation editor. Chris@148: Centre for Digital Music, Queen Mary, University of London. Chris@202: This file copyright 2006 Chris Cannam and QMUL. Chris@148: Chris@148: This program is free software; you can redistribute it and/or Chris@148: modify it under the terms of the GNU General Public License as Chris@148: published by the Free Software Foundation; either version 2 of the Chris@148: License, or (at your option) any later version. See the file Chris@148: COPYING included with this distribution for more information. Chris@148: */ Chris@148: Chris@148: #include "WavFileReader.h" Chris@148: Chris@148: #include Chris@148: Chris@175: #include Chris@316: #include Chris@175: Chris@317: WavFileReader::WavFileReader(FileSource source, bool fileUpdating) : Chris@148: m_file(0), Chris@316: m_source(source), Chris@316: m_path(source.getLocalFilename()), Chris@823: m_seekable(false), Chris@148: m_buffer(0), Chris@148: m_bufsiz(0), Chris@148: m_lastStart(0), Chris@176: m_lastCount(0), Chris@176: m_updating(fileUpdating) Chris@148: { Chris@148: m_frameCount = 0; Chris@148: m_channelCount = 0; Chris@148: m_sampleRate = 0; Chris@148: Chris@148: m_fileInfo.format = 0; Chris@148: m_fileInfo.frames = 0; Chris@290: m_file = sf_open(m_path.toLocal8Bit(), SFM_READ, &m_fileInfo); Chris@148: Chris@187: if (!m_file || (!fileUpdating && m_fileInfo.channels <= 0)) { Chris@682: std::cerr << "WavFileReader::initialize: Failed to open file at \"" Chris@686: << m_path << "\" (" Chris@148: << sf_strerror(m_file) << ")" << std::endl; Chris@148: Chris@148: if (m_file) { Chris@290: m_error = QString("Couldn't load audio file '%1':\n%2") Chris@290: .arg(m_path).arg(sf_strerror(m_file)); Chris@148: } else { Chris@290: m_error = QString("Failed to open audio file '%1'") Chris@290: .arg(m_path); Chris@148: } Chris@148: return; Chris@148: } Chris@148: Chris@187: if (m_fileInfo.channels > 0) { Chris@823: Chris@187: m_frameCount = m_fileInfo.frames; Chris@187: m_channelCount = m_fileInfo.channels; Chris@187: m_sampleRate = m_fileInfo.samplerate; Chris@823: Chris@823: m_seekable = (m_fileInfo.seekable != 0); Chris@823: Chris@823: // Our m_seekable reports whether a file is rapidly seekable, Chris@823: // so things like Ogg don't qualify. We cautiously report Chris@823: // every file type of "at least" the historical period of Ogg Chris@823: // or FLAC as non-seekable. Chris@823: int type = m_fileInfo.format & SF_FORMAT_TYPEMASK; Chris@823: // std::cerr << "WavFileReader: format type is " << type << " (flac, ogg are " << SF_FORMAT_FLAC << ", " << SF_FORMAT_OGG << ")" << std::endl; Chris@823: if (type >= SF_FORMAT_FLAC || type >= SF_FORMAT_OGG) { Chris@823: // std::cerr << "WavFileReader: Recording as non-seekable" << std::endl; Chris@823: m_seekable = false; Chris@823: } Chris@187: } Chris@175: Chris@823: // std::cerr << "WavFileReader: Frame count " << m_frameCount << ", channel count " << m_channelCount << ", sample rate " << m_sampleRate << ", seekable " << m_seekable << std::endl; Chris@175: Chris@148: } Chris@148: Chris@148: WavFileReader::~WavFileReader() Chris@148: { Chris@148: if (m_file) sf_close(m_file); Chris@564: delete[] m_buffer; Chris@148: } Chris@148: Chris@148: void Chris@175: WavFileReader::updateFrameCount() Chris@175: { Chris@175: QMutexLocker locker(&m_mutex); Chris@175: Chris@175: size_t prevCount = m_fileInfo.frames; Chris@175: Chris@175: if (m_file) { Chris@175: sf_close(m_file); Chris@290: m_file = sf_open(m_path.toLocal8Bit(), SFM_READ, &m_fileInfo); Chris@175: if (!m_file || m_fileInfo.channels <= 0) { Chris@686: std::cerr << "WavFileReader::updateFrameCount: Failed to open file at \"" << m_path << "\" (" Chris@175: << sf_strerror(m_file) << ")" << std::endl; Chris@175: } Chris@175: } Chris@175: Chris@690: // SVDEBUG << "WavFileReader::updateFrameCount: now " << m_fileInfo.frames << endl; Chris@175: Chris@176: m_frameCount = m_fileInfo.frames; Chris@176: Chris@187: if (m_channelCount == 0) { Chris@187: m_channelCount = m_fileInfo.channels; Chris@187: m_sampleRate = m_fileInfo.samplerate; Chris@187: } Chris@187: Chris@258: if (m_frameCount != prevCount) { Chris@258: // std::cerr << "frameCountChanged" << std::endl; Chris@258: emit frameCountChanged(); Chris@258: } Chris@176: } Chris@176: Chris@176: void Chris@176: WavFileReader::updateDone() Chris@176: { Chris@176: updateFrameCount(); Chris@176: m_updating = false; Chris@175: } Chris@175: Chris@175: void Chris@148: WavFileReader::getInterleavedFrames(size_t start, size_t count, Chris@148: SampleBlock &results) const Chris@148: { Chris@175: if (count == 0) return; Chris@148: results.clear(); Chris@377: results.reserve(count * m_fileInfo.channels); Chris@175: Chris@175: QMutexLocker locker(&m_mutex); Chris@175: Chris@175: if (!m_file || !m_channelCount) { Chris@175: return; Chris@175: } Chris@148: Chris@148: if ((long)start >= m_fileInfo.frames) { Chris@690: // SVDEBUG << "WavFileReader::getInterleavedFrames: " << start Chris@687: // << " > " << m_fileInfo.frames << endl; Chris@148: return; Chris@148: } Chris@148: Chris@148: if (long(start + count) > m_fileInfo.frames) { Chris@148: count = m_fileInfo.frames - start; Chris@148: } Chris@148: Chris@148: sf_count_t readCount = 0; Chris@148: Chris@148: if (start != m_lastStart || count != m_lastCount) { Chris@148: Chris@148: if (sf_seek(m_file, start, SEEK_SET) < 0) { Chris@175: // std::cerr << "sf_seek failed" << std::endl; Chris@148: return; Chris@148: } Chris@148: Chris@148: if (count * m_fileInfo.channels > m_bufsiz) { Chris@148: // std::cerr << "WavFileReader: Reallocating buffer for " << count Chris@148: // << " frames, " << m_fileInfo.channels << " channels: " Chris@148: // << m_bufsiz << " floats" << std::endl; Chris@148: m_bufsiz = count * m_fileInfo.channels; Chris@148: delete[] m_buffer; Chris@148: m_buffer = new float[m_bufsiz]; Chris@148: } Chris@148: Chris@148: if ((readCount = sf_readf_float(m_file, m_buffer, count)) < 0) { Chris@175: // std::cerr << "sf_readf_float failed" << std::endl; Chris@148: return; Chris@148: } Chris@148: Chris@148: m_lastStart = start; Chris@148: m_lastCount = readCount; Chris@148: } Chris@148: Chris@148: for (size_t i = 0; i < count * m_fileInfo.channels; ++i) { Chris@232: if (i >= m_bufsiz) { Chris@232: std::cerr << "INTERNAL ERROR: WavFileReader::getInterleavedFrames: " << i << " >= " << m_bufsiz << std::endl; Chris@232: } Chris@148: results.push_back(m_buffer[i]); Chris@148: } Chris@148: Chris@148: return; Chris@148: } Chris@148: Chris@157: void Chris@290: WavFileReader::getSupportedExtensions(std::set &extensions) Chris@157: { Chris@157: int count; Chris@157: Chris@157: if (sf_command(0, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(count))) { Chris@157: extensions.insert("wav"); Chris@157: extensions.insert("aiff"); Chris@316: extensions.insert("aifc"); Chris@157: extensions.insert("aif"); Chris@157: return; Chris@157: } Chris@157: Chris@157: SF_FORMAT_INFO info; Chris@157: for (int i = 0; i < count; ++i) { Chris@157: info.format = i; Chris@157: if (!sf_command(0, SFC_GET_FORMAT_MAJOR, &info, sizeof(info))) { Chris@783: QString ext = QString(info.extension).toLower(); Chris@783: extensions.insert(ext); Chris@783: if (ext == "oga") { Chris@783: // libsndfile is awfully proper, it says it only Chris@783: // supports .oga but lots of Ogg audio files in the Chris@783: // wild are .ogg and it will accept that Chris@783: extensions.insert("ogg"); Chris@783: } Chris@157: } Chris@157: } Chris@157: } Chris@316: Chris@316: bool Chris@316: WavFileReader::supportsExtension(QString extension) Chris@316: { Chris@316: std::set extensions; Chris@316: getSupportedExtensions(extensions); Chris@316: return (extensions.find(extension.toLower()) != extensions.end()); Chris@316: } Chris@316: Chris@316: bool Chris@316: WavFileReader::supportsContentType(QString type) Chris@316: { Chris@316: return (type == "audio/x-wav" || Chris@316: type == "audio/x-aiff" || Chris@316: type == "audio/basic"); Chris@316: } Chris@316: Chris@316: bool Chris@317: WavFileReader::supports(FileSource &source) Chris@316: { Chris@316: return (supportsExtension(source.getExtension()) || Chris@316: supportsContentType(source.getContentType())); Chris@316: } Chris@316: Chris@316: