Mercurial > hg > svcore
diff data/fileio/MP3FileReader.cpp @ 148:1a42221a1522
* Reorganising code base. This revision will not compile.
author | Chris Cannam |
---|---|
date | Mon, 31 Jul 2006 11:49:58 +0000 |
parents | |
children | 4b2ea82fd0ed |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/MP3FileReader.cpp Mon Jul 31 11:49:58 2006 +0000 @@ -0,0 +1,227 @@ + +/* -*- 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 file copyright 2006 Chris Cannam. + + 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. +*/ + +#ifdef HAVE_MAD + +#include "MP3FileReader.h" +#include "base/System.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <iostream> + +#include <QApplication> +#include <QProgressDialog> + +MP3FileReader::MP3FileReader(QString path, bool showProgress, CacheMode mode) : + CodedAudioFileReader(mode), + m_path(path) +{ + m_frameCount = 0; + m_channelCount = 0; + m_sampleRate = 0; + m_fileSize = 0; + m_bitrateNum = 0; + m_bitrateDenom = 0; + m_frameCount = 0; + m_cancelled = false; + + struct stat stat; + if (::stat(path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) { + m_error = QString("File %1 does not exist.").arg(path); + return; + } + + m_fileSize = stat.st_size; + + int fd; + if ((fd = ::open(path.toLocal8Bit().data(), O_RDONLY, 0)) < 0) { + m_error = QString("Failed to open file %1 for reading.").arg(path); + return; + } + + unsigned char *filebuffer = 0; + + try { + filebuffer = new unsigned char[stat.st_size]; + } catch (...) { + m_error = QString("Out of memory"); + ::close(fd); + return; + } + + if (::read(fd, filebuffer, stat.st_size) < stat.st_size) { + m_error = QString("Failed to read file %1.").arg(path); + delete[] filebuffer; + ::close(fd); + return; + } + + ::close(fd); + + if (showProgress) { + m_progress = new QProgressDialog + (QObject::tr("Decoding MP3 file..."), + QObject::tr("Stop"), 0, 100); + m_progress->hide(); + } + + if (!decode(filebuffer, stat.st_size)) { + m_error = QString("Failed to decode file %1.").arg(path); + delete[] filebuffer; + return; + } + + if (isDecodeCacheInitialised()) finishDecodeCache(); + + if (showProgress) { + delete m_progress; + m_progress = 0; + } + + delete[] filebuffer; +} + +MP3FileReader::~MP3FileReader() +{ +} + +bool +MP3FileReader::decode(void *mm, size_t sz) +{ + DecoderData data; + struct mad_decoder decoder; + + data.start = (unsigned char const *)mm; + data.length = (unsigned long)sz; + data.reader = this; + + mad_decoder_init(&decoder, &data, input, 0, 0, output, error, 0); + mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC); + mad_decoder_finish(&decoder); + + return true; +} + +enum mad_flow +MP3FileReader::input(void *dp, struct mad_stream *stream) +{ + DecoderData *data = (DecoderData *)dp; + + if (!data->length) return MAD_FLOW_STOP; + mad_stream_buffer(stream, data->start, data->length); + data->length = 0; + + return MAD_FLOW_CONTINUE; +} + +enum mad_flow +MP3FileReader::output(void *dp, + struct mad_header const *header, + struct mad_pcm *pcm) +{ + DecoderData *data = (DecoderData *)dp; + return data->reader->accept(header, pcm); +} + +enum mad_flow +MP3FileReader::accept(struct mad_header const *header, + struct mad_pcm *pcm) +{ + int channels = pcm->channels; + int frames = pcm->length; + + if (header) { + m_bitrateNum += header->bitrate; + m_bitrateDenom ++; + } + + if (frames < 1) return MAD_FLOW_CONTINUE; + + if (m_channelCount == 0) { + m_channelCount = channels; + m_sampleRate = pcm->samplerate; + } + + if (m_bitrateDenom > 0) { + double bitrate = m_bitrateNum / m_bitrateDenom; + double duration = double(m_fileSize * 8) / bitrate; + double elapsed = double(m_frameCount) / m_sampleRate; + double percent = ((elapsed * 100.0) / duration); + int progress = int(percent); + if (progress < 1) progress = 1; + if (progress > 99) progress = 99; + if (progress > m_progress->value()) { + m_progress->setValue(progress); + m_progress->show(); + m_progress->raise(); + qApp->processEvents(); + if (m_progress->wasCanceled()) { + m_cancelled = true; + } + } + } + + if (m_cancelled) return MAD_FLOW_STOP; + + m_frameCount += frames; + + if (!isDecodeCacheInitialised()) { + initialiseDecodeCache(); + } + + for (int i = 0; i < frames; ++i) { + + for (int ch = 0; ch < channels; ++ch) { + mad_fixed_t sample = 0; + if (ch < int(sizeof(pcm->samples) / sizeof(pcm->samples[0]))) { + sample = pcm->samples[ch][i]; + } + float fsample = float(sample) / float(MAD_F_ONE); + addSampleToDecodeCache(fsample); + } + + if (! (i & 0xffff)) { + // periodically munlock to ensure we don't exhaust real memory + // if running with memory locked down + MUNLOCK_SAMPLEBLOCK(m_data); + } + } + + if (frames > 0) { + MUNLOCK_SAMPLEBLOCK(m_data); + } + + return MAD_FLOW_CONTINUE; +} + +enum mad_flow +MP3FileReader::error(void *dp, + struct mad_stream *stream, + struct mad_frame *) +{ + DecoderData *data = (DecoderData *)dp; + + fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n", + stream->error, mad_stream_errorstr(stream), + stream->this_frame - data->start); + + return MAD_FLOW_CONTINUE; +} + +#endif