123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- /**
- * Copyright (c) 2011 Nokia Corporation.
- *
- * Part of the Qt GameEnabler.
- */
- #include "GEAudioBuffer.h"
- #include <math.h>
- #include <QFile>
- #include "GEAudioBufferPlayInstance.h"
- #include "GEAudioMixer.h"
- #include "trace.h"
- using namespace GE;
- /*!
- Header for wav data
- */
- struct SWavHeader {
- char chunkID[4];
- unsigned int chunkSize;
- char format[4];
- unsigned char subchunk1id[4];
- unsigned int subchunk1size;
- unsigned short audioFormat;
- unsigned short nofChannels;
- unsigned int sampleRate;
- unsigned int byteRate;
- unsigned short blockAlign;
- unsigned short bitsPerSample;
- unsigned char subchunk2id[4];
- unsigned int subchunk2size;
- };
- /*!
- * \class AudioBuffer
- * \brief A class to hold audio information (a buffer).
- */
- /*!
- Constructor.
- */
- AudioBuffer::AudioBuffer(QObject *parent /* = 0 */)
- : QObject(parent),
- m_sampleFunction(0),
- m_data(0),
- m_dataLength(0),
- m_nofChannels(0),
- m_bitsPerSample(0),
- m_signedData(false),
- m_samplesPerSec(0)
- {
- }
- /*!
- Destructor.
- */
- AudioBuffer::~AudioBuffer()
- {
- // Deallocate the data.
- reallocate(0);
- }
- /*!
- (Re)allocates the audio buffer according to \a length.
- */
- void AudioBuffer::reallocate(int length)
- {
- if (m_data) {
- delete [] ((char*)m_data);
- }
- m_dataLength = length;
- if (m_dataLength > 0) {
- m_data = new char[m_dataLength];
- }
- else {
- m_data = 0;
- }
- }
- /*!
- Loads a .wav file from file with \a fileName. Note that this method can be
- used for loading .wav from Qt resources as well. If \a parent is given, it
- is set as the parent of the constructed buffer.
- Returns a new buffer if successful, NULL otherwise.
- */
- AudioBuffer *AudioBuffer::loadWav(QString fileName, QObject *parent /* = 0 */)
- {
- QFile wavFile(fileName);
- if (wavFile.open(QIODevice::ReadOnly)) {
- AudioBuffer *buffer = loadWav(wavFile, parent);
- if (!buffer) {
- DEBUG_INFO("Failed to load data from " << fileName << "!");
- }
- wavFile.close();
- return buffer;
- }
- DEBUG_INFO("Failed to open " << fileName << ": " << wavFile.errorString());
- return 0;
- }
- /*!
- Protected method, called from AudioBuffer::loadWav(QString, QObject*).
- Loads a .wav file from a preopened \a wavFile. If \a parent is given, it is
- set as the parent of the constructed buffer.
- Returns a new buffer if successful, NULL otherwise.
- */
- AudioBuffer *AudioBuffer::loadWav(QFile &wavFile, QObject *parent /* = 0 */)
- {
- if (!wavFile.isOpen()) {
- // The file is not open!
- DEBUG_INFO("The given file must be opened before calling this method!");
- return 0;
- }
- SWavHeader header;
- wavFile.read(header.chunkID, 4);
- if (header.chunkID[0] != 'R' || header.chunkID[1] != 'I' ||
- header.chunkID[2] != 'F' || header.chunkID[3] != 'F') {
- // Incorrect header
- return 0;
- }
- wavFile.read((char*)&header.chunkSize, 4);
- wavFile.read((char*)&header.format, 4);
- if (header.format[0] != 'W' || header.format[1] != 'A' ||
- header.format[2] != 'V' || header.format[3] != 'E') {
- // Incorrect header
- return 0;
- }
- wavFile.read((char*)&header.subchunk1id, 4);
- if (header.subchunk1id[0] != 'f' || header.subchunk1id[1] != 'm' ||
- header.subchunk1id[2] != 't' || header.subchunk1id[3] != ' ') {
- // Incorrect header
- return 0;
- }
- wavFile.read((char*)&header.subchunk1size, 4);
- wavFile.read((char*)&header.audioFormat, 2);
- wavFile.read((char*)&header.nofChannels, 2);
- wavFile.read((char*)&header.sampleRate, 4);
- wavFile.read((char*)&header.byteRate, 4);
- wavFile.read((char*)&header.blockAlign, 2);
- wavFile.read((char*)&header.bitsPerSample, 2);
- while (1) {
- if (wavFile.read((char*)&header.subchunk2id, 4) != 4)
- return 0;
- if (wavFile.read((char*)&header.subchunk2size, 4) != 4)
- return 0;
- if (header.subchunk2id[0] == 'd' && header.subchunk2id[1] == 'a' &&
- header.subchunk2id[2] == 't' && header.subchunk2id[3] == 'a') {
- // Found the data chunk.
- break;
- }
- // This was not the data-chunk. Skip it.
- if (header.subchunk2size < 1) {
- // Error in file!
- return 0;
- }
- char *unused = new char[header.subchunk2size];
- wavFile.read(unused, header.subchunk2size);
- delete [] unused;
- }
- // The data follows.
- if (header.subchunk2size < 1)
- return 0;
- // Construct the buffer.
- AudioBuffer *buffer = new AudioBuffer(parent);
- buffer->m_nofChannels = header.nofChannels;
- buffer->m_bitsPerSample = header.bitsPerSample;
- buffer->m_samplesPerSec = header.sampleRate;
- buffer->m_signedData = 0; // Where to look for this?
- buffer->reallocate(header.subchunk2size);
- wavFile.read((char*)buffer->m_data, header.subchunk2size);
- // Select a good sampling function.
- if (!setSampleFunction(*buffer)) {
- // Failed to resolve the sample function!
- delete buffer;
- return 0;
- }
- return buffer;
- }
- /*!
- Loads a .wav file from a preopened file handle, \a wavFile. If \a parent is
- given, it is set as the parent of the constructed buffer.
- Returns a new buffer if successful, NULL otherwise.
- */
- AudioBuffer *AudioBuffer::loadWav(FILE *wavFile, QObject *parent /* = 0 */)
- {
- if (!wavFile) {
- // Invalid file handle!
- return 0;
- }
- // Read the header.
- SWavHeader header;
- fread(header.chunkID, 4, 1, wavFile);
- if (header.chunkID[0] != 'R' || header.chunkID[1] != 'I' ||
- header.chunkID[2] != 'F' || header.chunkID[3] != 'F') {
- // Incorrect header
- return 0;
- }
- fread(&header.chunkSize, 4, 1, wavFile);
- fread(header.format, 4, 1, wavFile);
- if (header.format[0] != 'W' || header.format[1] != 'A' ||
- header.format[2] != 'V' || header.format[3] != 'E') {
- // Incorrect header
- return 0;
- }
- fread(header.subchunk1id, 4, 1, wavFile);
- if (header.subchunk1id[0] != 'f' || header.subchunk1id[1] != 'm' ||
- header.subchunk1id[2] != 't' || header.subchunk1id[3] != ' ') {
- // Incorrect header
- return 0;
- }
- fread(&header.subchunk1size, 4, 1, wavFile);
- fread(&header.audioFormat, 2, 1, wavFile);
- fread(&header.nofChannels, 2, 1, wavFile);
- fread(&header.sampleRate, 4, 1, wavFile);
- fread(&header.byteRate, 4, 1, wavFile);
- fread(&header.blockAlign, 2, 1, wavFile);
- fread(&header.bitsPerSample, 2, 1, wavFile);
- fread(header.subchunk2id, 4, 1, wavFile);
- if (header.subchunk2id[0] != 'd' || header.subchunk2id[1] != 'a' ||
- header.subchunk2id[2] != 't' || header.subchunk2id[3] != 'a') {
- // Incorrect header
- return 0;
- }
- fread(&header.subchunk2size, 4, 1, wavFile);
- // The data follows.
- if (header.subchunk2size < 1)
- return 0;
- AudioBuffer *buffer = new AudioBuffer(parent);
- buffer->m_nofChannels = header.nofChannels;
- buffer->m_bitsPerSample = header.bitsPerSample;
- buffer->m_samplesPerSec = header.sampleRate;
- buffer->m_signedData = 0; // Where to look for this?
- buffer->reallocate(header.subchunk2size);
- fread(buffer->m_data, 1, header.subchunk2size, wavFile);
- // Select a good sampling function.
- if (!setSampleFunction(*buffer)) {
- // Failed to select the sampling function!
- delete buffer;
- return 0;
- }
- return buffer;
- }
- // Mix to mono versions.
- AUDIO_SAMPLE_TYPE AudioBuffer::sampleFunction8bitMono(AudioBuffer *buffer,
- int pos,
- int channel)
- {
- Q_UNUSED(channel);
- return (AUDIO_SAMPLE_TYPE)(((quint8*)(buffer->m_data))[pos] - 128) << 8;
- }
- AUDIO_SAMPLE_TYPE AudioBuffer::sampleFunction16bitMono(AudioBuffer *buffer,
- int pos,
- int channel)
- {
- Q_UNUSED(channel);
- return (AUDIO_SAMPLE_TYPE)(((quint16*)(buffer->m_data))[pos]);
- }
- AUDIO_SAMPLE_TYPE AudioBuffer::sampleFunction32bitMono(AudioBuffer *buffer,
- int pos,
- int channel)
- {
- Q_UNUSED(channel); // To prevent compiler warnings.
- return (((float*)(buffer->m_data))[pos * buffer->m_nofChannels]) * 65536.0f / 2.0f;
- }
- // Mix to stereo versions.
- AUDIO_SAMPLE_TYPE AudioBuffer::sampleFunction8bitStereo(AudioBuffer *buffer,
- int pos,
- int channel)
- {
- return ((AUDIO_SAMPLE_TYPE)
- (((quint8*)(buffer->m_data))[pos * buffer->m_nofChannels + channel]) << 8);
- }
- AUDIO_SAMPLE_TYPE AudioBuffer::sampleFunction16bitStereo(AudioBuffer *buffer,
- int pos,
- int channel)
- {
- return (AUDIO_SAMPLE_TYPE)
- (((quint16*)(buffer->m_data))[pos * buffer->m_nofChannels + channel]);
- }
- AUDIO_SAMPLE_TYPE AudioBuffer::sampleFunction32bitStereo(AudioBuffer *buffer,
- int pos,
- int channel)
- {
- return (((float*)(buffer->m_data))[pos * buffer->m_nofChannels +
- channel]) * 65536.0f / 2.0f;
- }
- /*!
- Constructs a new play instance and sets it as an audio source for \a mixer.
- Note that the mixer takes ownership of the constructed instance.
- Returns the constructed instance or NULL in case the mixer refused to add
- the instance.
- */
- AudioBufferPlayInstance *AudioBuffer::playWithMixer(AudioMixer &mixer)
- {
- AudioBufferPlayInstance *instance = new AudioBufferPlayInstance(this);
- if (!mixer.addAudioSource(instance)) {
- DEBUG_INFO("Failed to add the new audio source to mixer!");
- delete instance;
- return NULL;
- }
- return instance;
- }
- /*!
- Sets an appropriate sample function for \a buffer depending on the number of
- channels and the bit rate.
- Returns true if successful, false otherwise.
- */
- bool AudioBuffer::setSampleFunction(AudioBuffer &buffer)
- {
- buffer.m_sampleFunction = 0;
- if (buffer.m_nofChannels == 1) {
- if (buffer.m_bitsPerSample == 8)
- buffer.m_sampleFunction = sampleFunction8bitMono;
- if (buffer.m_bitsPerSample == 16)
- buffer.m_sampleFunction = sampleFunction16bitMono;
- if (buffer.m_bitsPerSample == 32)
- buffer.m_sampleFunction = sampleFunction32bitMono;
- } else {
- if (buffer.m_bitsPerSample == 8)
- buffer.m_sampleFunction = sampleFunction8bitStereo;
- if (buffer.m_bitsPerSample == 16)
- buffer.m_sampleFunction = sampleFunction16bitStereo;
- if (buffer.m_bitsPerSample == 32)
- buffer.m_sampleFunction = sampleFunction32bitStereo;
- }
- if (!buffer.m_sampleFunction) {
- // Unknown bit rate!
- DEBUG_INFO("Unknown bit rate:" << buffer.m_bitsPerSample);
- return false;
- }
- return true;
- }
|