/** * used by the cacheAudioEventsForMeasure-method, this collects * all AudioEvents in the requested measure for entry into the BulkCacher * * @param bufferPosition {int} the desired measures buffers start pointer * @param bufferEnd {int} the desired measures buffers end pointer * * @return {std::vector<BaseCacheableAudioEvent*>} */ std::vector<BaseCacheableAudioEvent*>* collectCacheableSequencerEvents( int bufferPosition, int bufferEnd ) { std::vector<BaseCacheableAudioEvent*>* events = new std::vector<BaseCacheableAudioEvent*>(); for ( int i = 0, l = instruments.size(); i < l; ++i ) { std::vector<BaseAudioEvent*>* audioEvents = instruments.at( i )->getEvents(); int amount = audioEvents->size(); for ( int j = 0; j < amount; j++ ) { BaseAudioEvent* audioEvent = audioEvents->at( j ); // if event is an instance of BaseCacheableAudioEvent add it to the list if ( dynamic_cast<BaseCacheableAudioEvent*>( audioEvent ) != NULL ) { int sampleStart = audioEvent->getSampleStart(); int sampleEnd = audioEvent->getSampleEnd(); if (( sampleStart >= bufferPosition && sampleStart <= bufferEnd ) || ( sampleStart < bufferPosition && sampleEnd >= bufferPosition )) { if ( !audioEvent->isDeletable()) events->push_back(( BaseCacheableAudioEvent* ) audioEvent ); } } } } return events; }
TEST( BaseAudioEvent, PositionInSamples ) { BaseAudioEvent* audioEvent = new BaseAudioEvent(); int sampleLength = randomInt( 512, 8192 ); int sampleStart = randomInt( 0, sampleLength / 2 ); int expectedEnd = sampleStart + ( sampleLength - 1 ); audioEvent->setSampleStart ( sampleStart ); audioEvent->setSampleLength( sampleLength ); EXPECT_EQ( sampleStart, audioEvent->getSampleStart() ); EXPECT_EQ( expectedEnd, audioEvent->getSampleEnd() ); EXPECT_EQ( sampleLength, audioEvent->getSampleLength() ); // test whether values in seconds have updated accordingly int SAMPLE_RATE = 44100; float expectedStartPosition = BufferUtility::bufferToSeconds( sampleStart, SAMPLE_RATE ); float expectedEndPosition = BufferUtility::bufferToSeconds( expectedEnd, SAMPLE_RATE ); float expectedDuration = expectedEndPosition - expectedStartPosition; EXPECT_FLOAT_EQ( expectedStartPosition, audioEvent->getStartPosition() ); EXPECT_FLOAT_EQ( expectedEndPosition, audioEvent->getEndPosition() ); EXPECT_FLOAT_EQ( expectedDuration, audioEvent->getDuration() ); // test auto sanitation of properties audioEvent->setSampleEnd( expectedEnd * 2 ); EXPECT_EQ( expectedEnd, audioEvent->getSampleEnd() ) << "expected sample end not to exceed the range set by the sample start and length properties"; sampleLength /= 2; audioEvent->setSampleLength( sampleLength ); expectedEnd = sampleStart + ( sampleLength - 1 ); EXPECT_EQ( expectedEnd, audioEvent->getSampleEnd() ) << "expected sample end not to exceed the range set by the sample start and updated length properties"; // test non sanitation of properties for loopeable events audioEvent->setLoopeable( true ); expectedEnd *= 2; audioEvent->setSampleEnd( expectedEnd ); EXPECT_EQ( expectedEnd, audioEvent->getSampleEnd() ) << "expected sample end to exceed the range set by the sample start and length properties for loopeable event"; sampleLength /= 2; audioEvent->setSampleLength( sampleLength ); EXPECT_EQ( expectedEnd, audioEvent->getSampleEnd() ) << "expected sample end to exceed the range set by the sample start and updated length properties for loopeable event"; deleteAudioEvent( audioEvent ); }
/** * used by the getAudioEvents-method of the sequencer, this validates * the present AudioEvents against the requested position * and updates and flushes the removal queue * * @param instrument {BaseInstrument*} instrument to gather events from * @param bufferPosition {int} the current buffers start pointer * @param bufferEnd {int} the current buffers end pointer */ void collectSequencedEvents( BaseInstrument* instrument, int bufferPosition, int bufferEnd ) { if ( !instrument->hasEvents() ) return; AudioChannel* channel = instrument->audioChannel; std::vector<BaseAudioEvent*>* audioEvents = instrument->getEvents(); // removal queue std::vector<BaseAudioEvent*> removes; // channel has an internal loop (e.g. drum machine) ? recalculate requested // buffer position by subtracting all measures above the first if ( channel->maxBufferPosition > 0 ) { int samplesPerBar = AudioEngine::samples_per_bar; while ( bufferPosition >= channel->maxBufferPosition ) { bufferPosition -= samplesPerBar; bufferEnd -= samplesPerBar; } } int i = 0, amount = audioEvents->size(); for ( i; i < amount; i++ ) { BaseAudioEvent* audioEvent = audioEvents->at( i ); if ( audioEvent->isEnabled() ) { int sampleStart = audioEvent->getSampleStart(); int sampleEnd = audioEvent->getSampleEnd(); if ( audioEvent->isLoopeable() || ( sampleStart >= bufferPosition && sampleStart <= bufferEnd ) || ( sampleStart < bufferPosition && sampleEnd >= bufferPosition )) { if ( !audioEvent->isDeletable()) channel->addEvent( audioEvent ); else removes.push_back( audioEvent ); } } } // removal queue filled ? process it so we can safely // remove "deleted" AudioEvents without errors occurring if ( removes.size() > 0 ) { int i = 0; for ( i; i < removes.size(); i++ ) { BaseAudioEvent* audioEvent = removes[ i ]; instrument->removeEvent( audioEvent, false ); } } }
TEST( BaseAudioEvent, MixBufferLoopeableEvent ) { BaseAudioEvent* audioEvent = new BaseAudioEvent(); int sourceSize = 16; AudioBuffer* sourceBuffer = new AudioBuffer( 1, sourceSize ); SAMPLE_TYPE* rawBuffer = sourceBuffer->getBufferForChannel( 0 ); fillAudioBuffer( sourceBuffer ); audioEvent->setBuffer( sourceBuffer, false ); audioEvent->setLoopeable( true ); audioEvent->setSampleLength( 16 * 4 ); // thus will loop 4 times audioEvent->positionEvent ( 0, 16, 0 ); // create an output buffer at a size smaller than the source buffer length int outputSize = ( int )(( double ) sourceSize * .4 ); AudioBuffer* targetBuffer = new AudioBuffer( sourceBuffer->amountOfChannels, outputSize ); int minBufferPos = audioEvent->getSampleStart(); int bufferPos = minBufferPos; int maxBufferPos = audioEvent->getSampleEnd(); // test the seamless mixing over multiple iterations for ( ; bufferPos < maxBufferPos; bufferPos += outputSize ) { // mix buffer contents targetBuffer->silenceBuffers(); bool loopStarted = bufferPos + ( outputSize - 1 ) > maxBufferPos; int loopOffset = ( maxBufferPos - bufferPos ) + 1; audioEvent->mixBuffer( targetBuffer, bufferPos, minBufferPos, maxBufferPos, loopStarted, loopOffset, false ); // assert results SAMPLE_TYPE* mixedBuffer = targetBuffer->getBufferForChannel( 0 ); for ( int i = 0; i < outputSize; ++i ) { int compareOffset = ( bufferPos + i ) % sourceSize; EXPECT_EQ( rawBuffer[ compareOffset ], mixedBuffer[ i ] ) << "expected mixed buffer contents to equal the source contents at mixed offset " << i << " for source offset " << compareOffset; } } delete targetBuffer; delete sourceBuffer; delete audioEvent; }
TEST( BaseAudioEvent, PositionInSeconds ) { BaseAudioEvent* audioEvent = new BaseAudioEvent(); float startPosition = randomFloat( 0, 10 ); float endPosition = startPosition + randomFloat( 0, 10 ); int SAMPLE_RATE = 44100; float expectedDuration = endPosition - startPosition; int expectedSampleStart = BufferUtility::secondsToBuffer( startPosition, SAMPLE_RATE ); int expectedSampleEnd = BufferUtility::secondsToBuffer( endPosition, SAMPLE_RATE ); int expectedSampleLength = ( expectedSampleEnd - expectedSampleStart ) - 1; audioEvent->setStartPosition( startPosition ); EXPECT_FLOAT_EQ( startPosition, audioEvent->getStartPosition() ); EXPECT_FLOAT_EQ( startPosition, audioEvent->getEndPosition() ) << "expected end position to equal start position (hasn't been explicitly set yet)"; EXPECT_FLOAT_EQ( 0, audioEvent->getDuration()) << "expected zero duration (duration nor end haven't been explicitly set yet)"; EXPECT_EQ( 0, audioEvent->getSampleLength()) << "expected zero sample length (duration nor end haven't been explicitly set yet)"; EXPECT_EQ( expectedSampleStart, audioEvent->getSampleStart() ) << "expected sample start to have been updated after setting start position"; audioEvent->setEndPosition( endPosition ); EXPECT_FLOAT_EQ( startPosition, audioEvent->getStartPosition() ); EXPECT_FLOAT_EQ( endPosition, audioEvent->getEndPosition() ); EXPECT_FLOAT_EQ( expectedDuration, audioEvent->getDuration() ); EXPECT_EQ( expectedSampleEnd, audioEvent->getSampleEnd()) << "expected sample end to have been updated after setting end position"; EXPECT_EQ( expectedSampleLength, audioEvent->getSampleLength()) << "expected sample length to have been updated after setting end position"; expectedDuration /= 2; float expectedEndPosition = startPosition + expectedDuration; audioEvent->setDuration( expectedDuration ); EXPECT_FLOAT_EQ( expectedDuration, audioEvent->getDuration() ); EXPECT_FLOAT_EQ( expectedEndPosition, audioEvent->getEndPosition()) << "expected end position to have corrected after updating of duration"; deleteAudioEvent( audioEvent ); }
TEST( BaseAudioEvent, PositionEvent ) { BaseAudioEvent* audioEvent = new BaseAudioEvent(); AudioEngine::samples_per_bar = randomInt( 11025, 88200 ); int sampleLength = randomInt( 24, 8192 ); audioEvent->setSampleLength( sampleLength ); int startMeasure = randomInt( 0, 15 ); int subdivisions = randomInt( 4, 128 ); int offset = randomInt( 0, 64 ); audioEvent->positionEvent( startMeasure, subdivisions, offset ); int expectedSampleStart = ( startMeasure * AudioEngine::samples_per_bar ) + ( offset * AudioEngine::samples_per_bar / subdivisions ); int expectedSampleEnd = expectedSampleStart + sampleLength - 1; EXPECT_EQ( expectedSampleStart, audioEvent->getSampleStart() ); EXPECT_EQ( expectedSampleEnd, audioEvent->getSampleEnd() ); deleteAudioEvent( audioEvent ); }