SilentEye 0.4.1

sef/audio.cpp

Go to the documentation of this file.
00001 //  This file is part of SilentEye.
00002 //
00003 //  SilentEye is free software: you can redistribute it and/or modify
00004 //  it under the terms of the GNU General Public License as published by
00005 //  the Free Software Foundation, either version 3 of the License, or
00006 //  (at your option) any later version.
00007 //
00008 //  SilentEye is distributed in the hope that it will be useful,
00009 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011 //  GNU General Public License for more details.
00012 //
00013 //  You should have received a copy of the GNU General Public License
00014 //  along with SilentEye. If not, see <http://www.gnu.org/licenses/>.
00015 
00016 #include "audio.h"
00017 
00018 #include "moduleexception.h"
00019 
00020 namespace SilentEyeFramework {
00021 
00022     /*Audio::Audio(const QAudioFormat &format, qint64 dataLength)
00023         : m_format(format),  m_dataLength(dataLength)
00024     {
00025         setObjectName("Audio");
00026         m_type = AUDIO;
00027     }*/
00028 
00029     Audio::Audio(const QString& filePath)
00030         : Media(filePath)
00031     {
00032         setObjectName("Audio");
00033         m_type = AUDIO;
00034 
00035         readWaveHeader();
00036     }
00037 
00038     Audio::Audio(const Audio& aud)
00039         : Media(aud)
00040     {
00041         setObjectName("Audio");
00042         m_type = AUDIO;
00043 
00044         readWaveHeader();
00045     }
00046 
00047     Audio::Audio(Audio* aud)
00048         : Media(aud)
00049     {
00050         setObjectName("Audio");
00051         m_type = AUDIO;
00052 
00053         readWaveHeader();
00054     }
00055 
00056     Audio::~Audio()
00057     {
00058         // NOTHING TO DO
00059     }
00060 
00061     void Audio::readWaveHeader() {
00062         QFile file(m_filePath);
00063         if (!file.open(QIODevice::ReadOnly))
00064         {
00065             throw ModuleException("Cannot read selected file: " + m_filePath, file.errorString());
00066         }
00067 
00068         QDataStream stream(&file);
00069         stream.setByteOrder(QDataStream::BigEndian);
00070 
00071         // CHUNK DESCRIPTION
00072         quint32 len = 4;
00073         char* buffer = new char[len+1];
00074         stream.readRawData(buffer, len); // ChunkID
00075         buffer[len] = '\0';
00076         QString identifier(buffer);
00077         delete buffer;
00078 
00079         if (identifier == "RIFF")
00080         {
00081             m_byteOrder = QAudioFormat::BigEndian;
00082         }
00083         else if (identifier == "RIFX ")
00084         {
00085             m_byteOrder = QAudioFormat::LittleEndian;
00086         }
00087         else
00088         {
00089             throw ModuleException("Selected file is not in a valid/supported WAVE format.",
00090                                   "Identifier must be TIFF or RIFX (found: '" + identifier + "'')");
00091         }
00092         m_format.setByteOrder(m_byteOrder);
00093 
00094         stream.setByteOrder(QDataStream::LittleEndian);
00095         stream >> m_totalSize; // ChunkSize
00096 
00097         buffer = new char[len+1];
00098         stream.readRawData(buffer, len); // Format
00099         buffer[len] = '\0';
00100         if ("WAVE" != QString(buffer))
00101         {
00102             delete buffer;
00103             throw ModuleException("Selected file is not in a valid WAVE format.",
00104                                   "Format must be \"WAVE\"!");
00105         }
00106         delete buffer;
00107 
00108         // SUB CHUNK FMT
00109         buffer = new char[len+1];
00110         stream.readRawData(buffer, len); // Subchunk1ID
00111         buffer[len] = '\0';
00112         if("fmt " != QString(buffer))
00113         {
00114             throw ModuleException("Selected file is not in a valid WAVE format",
00115                                   "Subchunk1ID must be \"fmt \"!");
00116             delete buffer;
00117         }
00118         delete buffer;
00119 
00120         stream >> m_subFmtSize;
00121         stream >> m_audioFormat;
00122         if (m_audioFormat != 1 || m_subFmtSize != 16)
00123         {
00124             throw ModuleException("Selected file is not in a valid/supported WAVE format.",
00125                                   "Audio format must be 1 for PCM!");
00126         }
00127         m_format.setCodec("audio/pcm");
00128         stream >> m_numChannels;
00129         m_format.setChannels(m_numChannels);
00130         stream >> m_sampleRate;
00131         m_format.setFrequency(m_sampleRate);
00132         stream >> m_byteRate;
00133         stream >> m_blockAlign;
00134         stream >> m_bitPerSample;
00135         m_format.setSampleSize(m_bitPerSample);
00136         if (m_bitPerSample == 8) {
00137             m_format.setSampleType(QAudioFormat::UnSignedInt);
00138         } else if (m_bitPerSample == 16) {
00139             m_format.setSampleType(QAudioFormat::SignedInt);
00140         } else {
00141             throw ModuleException("Selected file is not in a valid/supported WAVE format.",
00142                                   "BitsPerSample must be 8 or 16!");
00143         }
00144 
00145 
00146         // SUB CHUNK DATA
00147         len = 4;
00148         buffer = new char[len+1];
00149         stream.readRawData(buffer, len);
00150         buffer[len] = '\0';
00151         if("data" != QString(buffer))
00152         {
00153             throw ModuleException("Selected file is not in a valid/supported WAVE format.",
00154                                   "Subchunk2ID mus be \"data\"!");
00155             delete buffer;
00156         }
00157         delete buffer;
00158 
00159         stream >> m_subDataSize;
00160 
00161         file.close();
00162 
00163         m_duration = (m_subDataSize / (m_bitPerSample/8.0)) / (m_sampleRate * m_numChannels);
00164         m_bitRate = floor((m_subDataSize * 8.0) / m_duration);
00165     }
00166 
00167     QAudioFormat Audio::format() const
00168     {
00169         return m_format;
00170     }
00171 
00172     double Audio::duration()
00173     {
00174         return m_duration;
00175     }
00176 
00177     quint32 Audio::bitRate()
00178     {
00179         return m_bitRate;
00180     }
00181 
00182     qint32 Audio::sampleSize() const {
00183         return floor((m_subDataSize * 8.0) / (m_bitPerSample * m_numChannels));
00184     }
00185 
00186     bool Audio::openSamples()
00187     {
00188         if (m_file.isOpen()) {
00189             m_file.close();
00190         }
00191         m_file.setFileName(m_filePath);
00192         if(!m_file.open(QIODevice::ReadOnly)) {
00193             return false;
00194         }
00195         m_sampleStream.setDevice(&m_file);
00196         m_sampleStream.skipRawData(headerLength());
00197 
00198         if (m_byteOrder == QAudioFormat::BigEndian) {
00199             m_sampleStream.setByteOrder(QDataStream::BigEndian);
00200         } else {
00201             m_sampleStream.setByteOrder(QDataStream::LittleEndian);
00202         }
00203 
00204         return true;
00205     }
00206 
00207     bool Audio::hasNextSample()
00208     {
00209         if (!m_file.isOpen()) {
00210             return false;
00211         }
00212         return !m_sampleStream.atEnd();
00213     }
00214 
00215     QList<quint32> Audio::readSample()
00216     {
00217         QList<quint32> sample;
00218         for (int i=0; i<m_numChannels; i++) {
00219             if (m_bitPerSample == 8) {
00220                 quint8 val;
00221                 m_sampleStream >> val;
00222                 sample << val;
00223             } else if (m_bitPerSample == 16) {
00224                 quint16 val;
00225                 m_sampleStream >> val;
00226                 sample << val;
00227             }
00228         }
00229         return sample;
00230     }
00231 
00232     void Audio::skipSample(qint32 length) {
00233         int skip = length * m_numChannels * (m_bitPerSample/8.0);;
00234         m_sampleStream.skipRawData(skip);
00235     }
00236 
00237     bool Audio::closeSamples()
00238     {
00239         if (m_file.isOpen()) {
00240             m_file.close();
00241             m_sampleStream.resetStatus();
00242             return true;
00243         } else {
00244             return false;
00245         }
00246     }
00247 
00248     void Audio::writeWaveHeader(QDataStream& stream)
00249     {
00250         stream.setByteOrder(QDataStream::BigEndian);
00251 
00252         // CHUNK DESCRIPTION
00253         if (m_byteOrder == QAudioFormat::LittleEndian) {
00254             stream.writeRawData("RIFX", 4);
00255         } else {
00256             stream.writeRawData("RIFF", 4);
00257         }
00258 
00259         stream.setByteOrder(QDataStream::LittleEndian);
00260         stream << m_totalSize; // ChunkSize
00261 
00262         stream.writeRawData("WAVE", 4); // Format
00263 
00264         // SUB CHUNK FMT
00265         stream.writeRawData("fmt ", 4); // Subchunk1ID
00266 
00267         stream << m_subFmtSize;
00268         stream << m_audioFormat;
00269         stream << m_numChannels;
00270         stream << m_sampleRate;
00271         stream << m_byteRate;
00272         stream << m_blockAlign;
00273         stream << m_bitPerSample;
00274 
00275         // SUB CHUNK DATA
00276         stream.writeRawData("data", 4);
00277         stream << m_subDataSize;
00278     }
00279 }