TEST( BaseAudioEvent, AddRemoveSequencer )
{
    BaseInstrument* instrument = new BaseInstrument();
    BaseAudioEvent* audioEvent = new BaseAudioEvent( instrument );

    // expect AudioEvent not be in any of the event queues of the instrument after construction

    bool found = false;
    for ( int i = 0; i < instrument->getEvents()->size(); ++i )
    {
        if ( instrument->getEvents()->at( i ) == audioEvent )
            found = true;
    }

    ASSERT_FALSE( found )
        << "expected event not to be present in the event list after construction";

    found = false;
    for ( int i = 0; i < instrument->getLiveEvents()->size(); ++i )
    {
        if ( instrument->getLiveEvents()->at( i ) == audioEvent )
            found = true;
    }

    ASSERT_FALSE( found )
        << "expected event not to be present in the live event list after construction";

    // 1. add the event to the sequencer

    audioEvent->addToSequencer();

    // expect AudioEvent to be in the sequenced event list, not the live list

    found = false;
    for ( int i = 0; i < instrument->getEvents()->size(); ++i )
    {
        if ( instrument->getEvents()->at( i ) == audioEvent )
            found = true;
    }

    ASSERT_TRUE( found )
        << "expected event to be present in the event list after addition";

    found = false;
    for ( int i = 0; i < instrument->getLiveEvents()->size(); ++i )
    {
        if ( instrument->getLiveEvents()->at( i ) == audioEvent )
            found = true;
    }

    ASSERT_FALSE( found )
        << "expected event not to be present in the live event list after addition";

    // 2. remove the event from the sequencer

    audioEvent->removeFromSequencer();

    // expect AudioEvent not to be in the sequenced event list anymore

    found = false;
    for ( int i = 0; i < instrument->getEvents()->size(); ++i )
    {
        if ( instrument->getEvents()->at( i ) == audioEvent )
            found = true;
    }

    ASSERT_FALSE( found )
        << "expected event not to be present in the event list after removal";

    // 3. add live event to the sequencer

    audioEvent->isSequenced = false;
    audioEvent->addToSequencer();

    // expect AudioEvent to be in the live event list, not the sequenced list

    found = false;
    for ( int i = 0; i < instrument->getEvents()->size(); ++i )
    {
        if ( instrument->getEvents()->at( i ) == audioEvent )
            found = true;
    }

    ASSERT_FALSE( found )
        << "expected live event not to be present in the sequenced event list after addition";

    found = false;
    for ( int i = 0; i < instrument->getLiveEvents()->size(); ++i )
    {
        if ( instrument->getLiveEvents()->at( i ) == audioEvent )
            found = true;
    }

    ASSERT_TRUE( found )
        << "expected live event to be present in the live event list after addition";

    // 4. remove live event from sequencer

    audioEvent->removeFromSequencer();

    // expect AudioEvent not be in any of the event queues of the instrument after removal

    found = false;
    for ( int i = 0; i < instrument->getEvents()->size(); ++i )
    {
        if ( instrument->getEvents()->at( i ) == audioEvent )
            found = true;
    }

    ASSERT_FALSE( found )
        << "expected event not to be present in the event list after removal";

    found = false;
    for ( int i = 0; i < instrument->getLiveEvents()->size(); ++i )
    {
        if ( instrument->getLiveEvents()->at( i ) == audioEvent )
            found = true;
    }

    ASSERT_FALSE( found )
        << "expected event not to be present in the live event list after removal";

    delete audioEvent;
    delete instrument;
}
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, MixBuffer )
{
    BaseAudioEvent* audioEvent = new BaseAudioEvent();

    int sampleLength = randomInt( 8, 24 );
    int sampleStart  = randomInt( 0, ( int )( sampleLength / 2 ));

    audioEvent->setSampleStart ( sampleStart );
    audioEvent->setSampleLength( sampleLength );

    int sampleEnd = audioEvent->getSampleEnd();

    AudioBuffer* buffer = fillAudioBuffer( new AudioBuffer( randomInt( 1, 4 ), sampleLength ));
    audioEvent->setBuffer( buffer, true );

    float volume = randomFloat();
    audioEvent->setVolume( volume );

    //std::cout << " ss: " << sampleStart << " se: " << sampleEnd << " sl: " << sampleLength << " ch: " << buffer->amountOfChannels;

    // create a temporary buffer to write output in, ensure it is smaller than the event buffer
    AudioBuffer* targetBuffer = new AudioBuffer( buffer->amountOfChannels, randomInt( 2, 4 ));
    int buffersToWrite        = targetBuffer->bufferSize;

    ASSERT_FALSE( bufferHasContent( targetBuffer ))
        << "expected target buffer to be silent after creation, but it has content";

    // test 1. mix without loopable range

    int maxBufferPos = sampleLength * 2; // use a "loop range" larger than the size of the events length
    int minBufferPos = randomInt( 0, maxBufferPos / 2 );
    int bufferPos    = randomInt( minBufferPos, maxBufferPos - 1 );
    bool loopStarted = false;
    int loopOffset   = 0;

    // if the random bufferPosition wasn't within the events sampleStart and sampleEnd range, we expect no content

    bool expectContent = ( bufferPos >= sampleStart && bufferPos <= sampleEnd ) ||
                         (( bufferPos + buffersToWrite ) >= sampleStart && ( bufferPos + buffersToWrite ) <= sampleEnd );

    //std::cout << " expected content: " << expectContent << " for buffer size: " << buffersToWrite;
    //std::cout << " min: " << minBufferPos << " max: " << maxBufferPos << " cur: " << bufferPos;

    audioEvent->mixBuffer( targetBuffer, bufferPos, minBufferPos, maxBufferPos, loopStarted, loopOffset, false );

    // validate buffer contents after mixing

    if ( expectContent )
    {
        for ( int c = 0, ca = targetBuffer->amountOfChannels; c < ca; ++c )
        {
            SAMPLE_TYPE* buffer       = targetBuffer->getBufferForChannel( c );
            SAMPLE_TYPE* sourceBuffer = audioEvent->getBuffer()->getBufferForChannel( c );
            SAMPLE_TYPE expectedSample;

            for ( int i = 0; i < buffersToWrite; ++i )
            {
                int r = i + bufferPos; // read pointer for the source buffer

                if ( r >= maxBufferPos && !loopStarted )
                    r -= ( maxBufferPos - minBufferPos );

                if ( r >= sampleStart && r <= sampleEnd )
                {
                    r -= sampleStart; // substract audioEvent start position
                    expectedSample = sourceBuffer[ r ] * volume;
                }
                else {
                    expectedSample = 0.0;
                }
                SAMPLE_TYPE sample = buffer[ i ];

                EXPECT_EQ( expectedSample, sample )
                    << "expected mixed sample at " << i << " to be equal to the calculated expected sample at read offset " << r;
            }
        }

    }
    else {
        ASSERT_FALSE( bufferHasContent( targetBuffer ))
            << "expected target buffer to contain no content after mixing for an out-of-range buffer position";
    }

    // test 2. mixing within a loopable range (implying sequencer is starting a loop)

    targetBuffer->silenceBuffers();

    ASSERT_FALSE( bufferHasContent( targetBuffer ))
        << "expected target buffer to be silent after silencing, but it still has content";

    bufferPos     = randomInt( minBufferPos, maxBufferPos - 1 );
    loopStarted   = true;
    loopOffset    = ( maxBufferPos - bufferPos ) + 1;

    // pre calculate at which buffer iterator the looping will commence
    // loopStartIteratorPosition describes at which sequencer position the loop starts
    // loopStartWritePointer describes at which position in the targetBuffer the loop is written to
    // amountOfLoopedWrites is the amount of samples written in the loop
    // loopStartReadPointer describes at which position the samples from the source audioEvent will be read when loop starts
    // loopStartReadPointerEnd describes the last position the samples from the source audioEvent will be read for the amount of loop writes

    int loopStartIteratorPosition = maxBufferPos + 1;
    int loopStartWritePointer     = loopOffset;
    int loopStartReadPointer      = minBufferPos;
    int amountOfLoopedWrites      = ( bufferPos + buffersToWrite ) - loopStartIteratorPosition;
    int loopStartReadPointerEnd   = ( loopStartReadPointer + amountOfLoopedWrites ) - 1;

    expectContent = ( bufferPos >= sampleStart && bufferPos <= sampleEnd ) ||
                    (( bufferPos + buffersToWrite ) >= sampleStart && ( bufferPos + buffersToWrite ) <= sampleEnd ) ||
                    ( loopStartIteratorPosition > maxBufferPos && (
                        ( loopStartReadPointer >= sampleStart && loopStartReadPointer <= sampleEnd ) ||
                        ( loopStartReadPointerEnd >= sampleStart && loopStartReadPointerEnd <= sampleEnd )));

    audioEvent->mixBuffer( targetBuffer, bufferPos, minBufferPos, maxBufferPos, loopStarted, loopOffset, false );

    //std::cout << " expected content: " << expectContent << " for buffer size: " << buffersToWrite;
    //std::cout << " min: " << minBufferPos << " max: " << maxBufferPos << " cur: " << bufferPos << " loop offset: " << loopOffset;

    if ( expectContent )
    {
        for ( int c = 0, ca = targetBuffer->amountOfChannels; c < ca; ++c )
        {
            SAMPLE_TYPE* buffer       = targetBuffer->getBufferForChannel( c );
            SAMPLE_TYPE* sourceBuffer = audioEvent->getBuffer()->getBufferForChannel( c );

            for ( int i = 0; i < buffersToWrite; ++i )
            {
                SAMPLE_TYPE expectedSample = 0.0;

                int r = i + bufferPos; // read pointer for the source buffer

                if ( i >= loopOffset )
                    r = minBufferPos + ( i - loopOffset );

                if ( r >= sampleStart && r <= sampleEnd )
                {
                    r -= sampleStart; // substract audioEvent start position
                    expectedSample = sourceBuffer[ r ] * volume;
                }
                SAMPLE_TYPE sample = buffer[ i ];

                EXPECT_EQ( expectedSample, sample )
                    << "expected mixed sample at " << i << " to be equal to the calculated expected sample at read "
                    << "offset " << r << " ( sanitized from " << ( i + bufferPos ) << " )";
            }
        }
    }
    else {
        ASSERT_FALSE( bufferHasContent( targetBuffer ))
            << "expected output buffer to contain no content after mixing for an out-of-range buffer position";
    }
    delete audioEvent;
    delete targetBuffer;
    delete buffer;
}
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 );
}