123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- /*
- * Copyright (c) 2011 Nokia Corporation.
- */
- /**
- *
- * GE::GA AudioBuffer functionality
- * tuomo.hirvonen@digia.com
- *
- */
- #include <stdio.h>
- #include <memory.h>
- #include <math.h>
- #include "AudioBuffer.h"
- using namespace GF;
- #pragma pack(1)
- // size = 44?
- 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;
- };
- #pragma pack()
- AudioBuffer::AudioBuffer() {
- m_data = 0;
- m_dataLength = 0;
- m_sampleFunction = 0;
- };
- AudioBuffer::~AudioBuffer() {
- reallocate(0);
- }
- 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;
- };
- int readWrapper( void *target, const int length, const void *sbuf, const int sbuflen, int &pos )
- {
- memcpy( target, ((const char*)sbuf)+pos, length );
- pos+=length;
- return length;
- }
- AudioBuffer *AudioBuffer::loadWav( const void *d, unsigned int dlength )
- {
- // read the header.
- SWavHeader header;
- int p=0;
- readWrapper( &header.chunkID, 44, d,dlength, p);
- // Sanity check
- if (header.chunkID[0]!='R' || header.chunkID[1]!='I' || header.chunkID[2]!='F' || header.chunkID[3]!='F') return 0; // incorrect header
- if (header.format[0]!='W' || header.format[1]!='A' || header.format[2]!='V' || header.format[3]!='E') return 0; // incorrect header
- if (header.subchunk1id[0]!='f' || header.subchunk1id[1]!='m' || header.subchunk1id[2]!='t' || header.subchunk1id[3]!=' ') return 0; // incorrect header
- if (header.subchunk2id[0]!='d' || header.subchunk2id[1]!='a' || header.subchunk2id[2]!='t' || header.subchunk2id[3]!='a') return 0; // incorrect header
- // the data follows.
- if (header.subchunk2size<1) return 0;
- AudioBuffer *rval = new AudioBuffer;
- 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 );
- readWrapper( rval->m_data, header.subchunk2size, d,dlength, p);
- // 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;
- }
- AudioBuffer* AudioBuffer::loadWav( const char *fileName) {
- FILE *f = fopen( fileName, "rb");
- if (f==0) return 0;
- AudioBuffer *rval = loadWav(f);
- fclose(f);
- return rval;
- };
- AudioBuffer* AudioBuffer::loadWav( FILE *wavFile ) {
- fseek(wavFile, 0L, SEEK_END);
- int fileLength = ftell(wavFile);
- fseek(wavFile, 0L, SEEK_SET);
- char *fileData = new char[ fileLength ];
- fread( fileData, fileLength, 1, wavFile );
- AudioBuffer *rval = loadWav( fileData, fileLength );
- delete [] fileData;
- return rval;
- /*
- // DIRECT FILE READING.. COMMENTED FOR NOW.
- // 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;
- AudioBuffer *rval = new AudioBuffer;
- 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 AudioBuffer::sampleFunction8bitMono( AudioBuffer *abuffer, int pos, int channel ) {
- return (AUDIO_SAMPLE_TYPE)(((unsigned char*)(abuffer->m_data))[pos]-128)<<8;
- };
- AUDIO_SAMPLE_TYPE AudioBuffer::sampleFunction16bitMono( AudioBuffer *abuffer, int pos, int channel ) {
- return (AUDIO_SAMPLE_TYPE)(((short*)(abuffer->m_data))[pos]);
- };
- AUDIO_SAMPLE_TYPE AudioBuffer::sampleFunction8bitStereo( AudioBuffer *abuffer, int pos, int channel ) {
- return ((AUDIO_SAMPLE_TYPE)(((char*)(abuffer->m_data))[pos*abuffer->m_nofChannels + channel])<<8);
- };
- AUDIO_SAMPLE_TYPE AudioBuffer::sampleFunction16bitStereo( AudioBuffer *abuffer, int pos, int channel ) {
- return (AUDIO_SAMPLE_TYPE)(((short*)(abuffer->m_data))[pos*abuffer->m_nofChannels + channel]);
- };
- AudioBufferPlayInstance *AudioBuffer::playWithMixer( AudioMixer &mixer ) {
- AudioBufferPlayInstance *i = (AudioBufferPlayInstance*)mixer.addAudioSource( new AudioBufferPlayInstance( this ));
- return i;
- };
- AudioBufferPlayInstance::AudioBufferPlayInstance() {
- m_fixedPos = 0;
- m_fixedInc = 0;
- m_buffer = 0;
- m_fixedLeftVolume = 4096;
- m_fixedRightVolume = 4096;
- m_destroyWhenFinished = true;
- m_finished = false;
- };
- AudioBufferPlayInstance::AudioBufferPlayInstance( AudioBuffer *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 AudioBufferPlayInstance::playBuffer( AudioBuffer *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;
- };
- AudioBufferPlayInstance::~AudioBufferPlayInstance() {
- };
- void AudioBufferPlayInstance::stop() {
- m_buffer = 0;
- m_finished = true;
- };
- void AudioBufferPlayInstance::setSpeed( float speed ) {
- if (!m_buffer) return;
- m_fixedInc = (int)( ((float)m_buffer->getSamplesPerSec() * 4096.0f*speed) / (float)AUDIO_FREQUENCY );
- };
- void AudioBufferPlayInstance::setLeftVolume( float vol ) {
- m_fixedLeftVolume = (int)(4096.0f*vol);
- };
- void AudioBufferPlayInstance::setRightVolume( float vol ) {
- m_fixedRightVolume = (int)(4096.0f*vol);
- };
- bool AudioBufferPlayInstance::canBeDestroyed() {
- if (m_finished==true &&
- m_destroyWhenFinished==true) return true; else return false;
- };
- // doesnt do any bound-checking Must be checked before called.
- int AudioBufferPlayInstance::mixBlock( AudioBuffer *buffer, AUDIO_SAMPLE_TYPE *target, int samplesToMix ) {
- //qDebug() << "AudioBufferPlayInstance::mixBlock" << samplesToMix;
- SAMPLE_FUNCTION_TYPE sampleFunction = buffer->getSampleFunction();
- if (!sampleFunction) return 0; // unsupported sampletype
- AUDIO_SAMPLE_TYPE *t_target = target+samplesToMix*AUDIO_CHANNELS;
- int sourcepos;
- //int tempCounter = 0;
- if (buffer->getNofChannels() == 2) { // stereo
- while (target!=t_target) {
- sourcepos = m_fixedPos>>12;
- //target[0] = (((((sampleFunction)( buffer, sourcepos, 0) * (4096-(m_fixedPos&4095)) + (sampleFunction)( m_buffer, sourcepos+1, 0) * (m_fixedPos&4095) ) >> 12) * m_fixedLeftVolume) >> 12);
- //target[1] = (((((sampleFunction)( buffer, sourcepos, 1) * (4096-(m_fixedPos&4095)) + (sampleFunction)( m_buffer, sourcepos+1, 1) * (m_fixedPos&4095) ) >> 12) * m_fixedRightVolume) >> 12);
- // no resampling here
- target[0] = (((sampleFunction)( buffer, sourcepos, 0) * m_fixedLeftVolume) >> 12);
- target[1] = (((sampleFunction)( buffer, sourcepos, 1) * 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);
- temp = (sampleFunction)( buffer, sourcepos, 0);
- target[0] = ((temp*m_fixedLeftVolume)>>12);
- target[1] = ((temp*m_fixedRightVolume)>>12);
- m_fixedPos+=m_fixedInc;
- target+=2;
- //tempCounter++;
- };
- };
- return samplesToMix;
- };
- int AudioBufferPlayInstance::pullAudio( AUDIO_SAMPLE_TYPE *target, int sampleCount ) {
- //qDebug() << "AudioBufferPlayInstance::pullAudio" << sampleCount;
- if (!m_buffer) return 0; // no sample associated to mix..
- AudioBuffer *buffer = m_buffer; // use this for now
- int channelLength = ((buffer->getDataLength()) / (buffer->getNofChannels()*buffer->getBytesPerSample()))-2;
- int samplesToWritePerChannel = sampleCount/AUDIO_CHANNELS;
- int amount;
- int totalMixed = 0;
- while (samplesToWritePerChannel>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>samplesToWritePerChannel) {
- maxMixAmount=samplesToWritePerChannel;
- }
- if (maxMixAmount>0) {
- amount=mixBlock(buffer, target+totalMixed*2, maxMixAmount );
- if (amount==0)
- {
- break; // an error occured
- }
- 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;
- }
- }
- samplesToWritePerChannel-=amount;
- if (samplesToWritePerChannel<1) break;
- };
- return totalMixed*2;
- };
|