/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the demonstration applications of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include "GEAudioBuffer.h" using namespace GE; 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; }; CAudioBuffer::CAudioBuffer() { m_data = 0; m_dataLength = 0; m_sampleFunction = 0; }; CAudioBuffer::~CAudioBuffer() { reallocate(0); } void CAudioBuffer::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; }; CAudioBuffer* CAudioBuffer::loadWav( QString fileName ) { QFile *wavFile = new QFile( fileName ); if (wavFile->open(QIODevice::ReadOnly)) { SWavHeader header; wavFile->read( header.chunkID, 4 ); if (header.chunkID[0]!='R' || header.chunkID[1]!='I' || header.chunkID[2]!='F' || header.chunkID[3]!='F') return 0; // incorrect header 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') return 0; // incorrect header wavFile->read( (char*)&header.subchunk1id,4 ); if (header.subchunk1id[0]!='f' || header.subchunk1id[1]!='m' || header.subchunk1id[2]!='t' || header.subchunk1id[3]!=' ') return 0; // incorrect header 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 ); qDebug() << fileName << " opened"; while (1) { if (wavFile->read( (char*)&header.subchunk2id,4 ) != 4) return 0; if (wavFile->read( (char*)&header.subchunk2size,4 ) != 4) return 0; //int deb_size = header.subchunk2size; //char tes[4]; //memcpy(tes, header.subchunk2id, 4 ); //if (header.subchunk2id[0]!='d' || header.subchunk2id[1]!='a' || header.subchunk2id[2]!='t' || header.subchunk2id[3]!='a') return 0; // incorrect header if (header.subchunk2id[0]=='d' && header.subchunk2id[1]=='a' && header.subchunk2id[2]=='t' && header.subchunk2id[3]=='a') break; // found the data, chunk // this was not the data-chunk. skip it if (header.subchunk2size<1) return 0; // error in file char *unused = new char[header.subchunk2size]; wavFile->read( unused, header.subchunk2size ); delete [] unused; } // the data follows. if (header.subchunk2size<1) return 0; CAudioBuffer *rval = new CAudioBuffer; rval->m_nofChannels = header.nofChannels; rval->m_bitsPerSample = header.bitsPerSample; rval->m_samplesPerSec = header.sampleRate; rval->m_signedData = 0; // where to know this? rval->reallocate( header.subchunk2size ); wavFile->read( (char*)rval->m_data, header.subchunk2size ); // choose a good sampling function. rval->m_sampleFunction = 0; if (rval->m_nofChannels==1) { if (rval->m_bitsPerSample == 8) rval->m_sampleFunction = sampleFunction8bitMono; if (rval->m_bitsPerSample == 16) rval->m_sampleFunction = sampleFunction16bitMono; } else { if (rval->m_bitsPerSample == 8) rval->m_sampleFunction = sampleFunction8bitStereo; if (rval->m_bitsPerSample == 16) rval->m_sampleFunction = sampleFunction16bitStereo; } return rval; } else { qDebug() << fileName << " NOT opened"; return 0; } delete wavFile; }; CAudioBuffer* CAudioBuffer::loadWav( FILE *wavFile ) { // 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') return 0; // incorrect header 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') return 0; // incorrect header fread( header.subchunk1id, 4, 1, wavFile ); if (header.subchunk1id[0]!='f' || header.subchunk1id[1]!='m' || header.subchunk1id[2]!='t' || header.subchunk1id[3]!=' ') return 0; // incorrect header 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') return 0; // incorrect header fread( &header.subchunk2size, 4, 1, wavFile ); // the data follows. if (header.subchunk2size<1) return 0; CAudioBuffer *rval = new CAudioBuffer; rval->m_nofChannels = header.nofChannels; rval->m_bitsPerSample = header.bitsPerSample; rval->m_samplesPerSec = header.sampleRate; rval->m_signedData = 0; // where to know this? rval->reallocate( header.subchunk2size ); fread( rval->m_data, 1, header.subchunk2size, wavFile ); // choose a good sampling function. rval->m_sampleFunction = 0; if (rval->m_nofChannels==1) { if (rval->m_bitsPerSample == 8) rval->m_sampleFunction = sampleFunction8bitMono; if (rval->m_bitsPerSample == 16) rval->m_sampleFunction = sampleFunction16bitMono; } else { if (rval->m_bitsPerSample == 8) rval->m_sampleFunction = sampleFunction8bitStereo; if (rval->m_bitsPerSample == 16) rval->m_sampleFunction = sampleFunction16bitStereo; } return rval; }; AUDIO_SAMPLE_TYPE CAudioBuffer::sampleFunction8bitMono( CAudioBuffer *abuffer, int pos, int channel ) { return (AUDIO_SAMPLE_TYPE)(((unsigned char*)(abuffer->m_data))[pos]-128)<<8; }; AUDIO_SAMPLE_TYPE CAudioBuffer::sampleFunction16bitMono( CAudioBuffer *abuffer, int pos, int channel ) { return (AUDIO_SAMPLE_TYPE)(((short*)(abuffer->m_data))[pos]); }; AUDIO_SAMPLE_TYPE CAudioBuffer::sampleFunction8bitStereo( CAudioBuffer *abuffer, int pos, int channel ) { return ((AUDIO_SAMPLE_TYPE)(((char*)(abuffer->m_data))[pos*abuffer->m_nofChannels + channel])<<8); }; AUDIO_SAMPLE_TYPE CAudioBuffer::sampleFunction16bitStereo( CAudioBuffer *abuffer, int pos, int channel ) { return (AUDIO_SAMPLE_TYPE)(((short*)(abuffer->m_data))[pos*abuffer->m_nofChannels + channel]); }; CAudioBufferPlayInstance *CAudioBuffer::playWithMixer( CAudioMixer &mixer ) { CAudioBufferPlayInstance *i = (CAudioBufferPlayInstance*)mixer.addAudioSource( new CAudioBufferPlayInstance( this )); return i; }; CAudioBufferPlayInstance::CAudioBufferPlayInstance() { m_fixedPos = 0; m_fixedInc = 0; m_buffer = 0; m_fixedLeftVolume = 4096; m_fixedRightVolume = 4096; m_destroyWhenFinished = true; m_finished = false; }; CAudioBufferPlayInstance::CAudioBufferPlayInstance( CAudioBuffer *startPlaying ) { m_fixedPos = 0; m_fixedInc = 0; m_fixedLeftVolume = 4096; m_fixedRightVolume = 4096; m_destroyWhenFinished = true; m_finished = false; playBuffer( startPlaying, 1.0f, 1.0f ); }; void CAudioBufferPlayInstance::playBuffer( CAudioBuffer *startPlaying, float volume, float speed, int loopTimes ) { m_buffer = startPlaying; m_fixedLeftVolume = (int)(4096.0f*volume); m_fixedRightVolume = m_fixedLeftVolume; m_fixedPos = 0; //m_fixedInc = ( startPlaying->getSamplesPerSec() * (int)(4096.0f*speed)) / AUDIO_FREQUENCY; setSpeed( speed ); m_loopTimes = loopTimes; }; CAudioBufferPlayInstance::~CAudioBufferPlayInstance() { }; void CAudioBufferPlayInstance::stop() { m_buffer = 0; m_finished = true; }; void CAudioBufferPlayInstance::setSpeed( float speed ) { if (!m_buffer) return; m_fixedInc = (int)( ((float)m_buffer->getSamplesPerSec() * 4096.0f*speed) / (float)AUDIO_FREQUENCY ); }; void CAudioBufferPlayInstance::setLeftVolume( float vol ) { m_fixedLeftVolume = (int)(4096.0f*vol); }; void CAudioBufferPlayInstance::setRightVolume( float vol ) { m_fixedRightVolume = (int)(4096.0f*vol); }; bool CAudioBufferPlayInstance::canBeDestroyed() { if (m_finished==true && m_destroyWhenFinished==true) return true; else return false; }; // Does not do any bound-checking. Must be checked before called. int CAudioBufferPlayInstance::mixBlock( AUDIO_SAMPLE_TYPE *target, int samplesToMix ) { SAMPLE_FUNCTION_TYPE sampleFunction = m_buffer->getSampleFunction(); if (!sampleFunction) return 0; // unsupported sampletype AUDIO_SAMPLE_TYPE *t_target = target+samplesToMix*2; int sourcepos; //int tempCounter = 0; if (m_buffer->getNofChannels() == 2) { // stereo while (target!=t_target) { sourcepos = m_fixedPos>>12; target[0] = (((((sampleFunction)( m_buffer, sourcepos, 0) * (4096-(m_fixedPos&4095)) + (sampleFunction)( m_buffer, sourcepos+1, 0) * (m_fixedPos&4095) ) >> 12) * m_fixedLeftVolume) >> 12); target[1] = (((((sampleFunction)( m_buffer, sourcepos, 1) * (4096-(m_fixedPos&4095)) + (sampleFunction)( m_buffer, sourcepos+1, 1) * (m_fixedPos&4095) ) >> 12) * m_fixedRightVolume) >> 12); m_fixedPos+=m_fixedInc; target+=2; //tempCounter++; }; } else { // mono int temp; while (target!=t_target) { sourcepos = m_fixedPos>>12; temp = (((sampleFunction)( m_buffer, sourcepos, 0 ) * (4096-(m_fixedPos&4095)) + (sampleFunction)( m_buffer, sourcepos+1, 0 ) * (m_fixedPos&4095) ) >> 12); target[0] = ((temp*m_fixedLeftVolume)>>12); target[1] = ((temp*m_fixedRightVolume)>>12); m_fixedPos+=m_fixedInc; target+=2; //tempCounter++; }; }; return samplesToMix; }; int CAudioBufferPlayInstance::pullAudio( AUDIO_SAMPLE_TYPE *target, int bufferLength ) { if (!m_buffer) return 0; // no sample associated to mix.. int channelLength = ((m_buffer->getDataLength()) / (m_buffer->getNofChannels()*m_buffer->getBytesPerSample()))-2; int samplesToWrite = bufferLength/2; int amount; int totalMixed = 0; while (samplesToWrite>0) { int samplesLeft = channelLength - (m_fixedPos>>12); int maxMixAmount = (int)(((long long int)(samplesLeft)<<12) / m_fixedInc ); // This is how much we can mix at least //int maxMixAmount = (int)((float)samplesLeft / ((float)m_fixedInc/4096.0f)); //if (maxMixAmount<1) maxMixAmount = 1; // NOTE, THIS MIGHT CAUSE PROBLEMS. NEEDS CHECKING if (maxMixAmount>samplesToWrite) { maxMixAmount=samplesToWrite; } if (maxMixAmount > 0) { amount=mixBlock(target+totalMixed * 2, maxMixAmount); if (amount == 0) { // Error! break; } totalMixed+=amount; } else { amount = 0; m_fixedPos = channelLength<<12; } // sample is ended,.. check the looping variables and see what to do. if ((m_fixedPos>>12)>=channelLength) { m_fixedPos -= (channelLength<<12); if (m_loopTimes>0) m_loopTimes--; if (m_loopTimes==0) { stop(); return totalMixed; } } samplesToWrite-=amount; if (samplesToWrite<1) break; }; return totalMixed*2; };