/* * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include // parselib includes #include #include // local includes #include "OneShotSampleSource.h" #include "SimpleMultiPlayer.h" static const char* TAG = "SimpleMultiPlayer"; using namespace oboe; using namespace parselib; namespace iolib { constexpr int32_t kBufferSizeInBursts = 2; // Use 2 bursts as the buffer size (double buffer) SimpleMultiPlayer::SimpleMultiPlayer() : mChannelCount(0), mOutputReset(false) {} DataCallbackResult SimpleMultiPlayer::onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) { StreamState streamState = oboeStream->getState(); if (streamState != StreamState::Open && streamState != StreamState::Started) { __android_log_print(ANDROID_LOG_ERROR, TAG, " streamState:%d", streamState); } if (streamState == StreamState::Disconnected) { __android_log_print(ANDROID_LOG_ERROR, TAG, " streamState::Disconnected"); } memset(audioData, 0, numFrames * mChannelCount * sizeof(float)); // OneShotSampleSource* sources = mSampleSources.get(); for(int32_t index = 0; index < mNumSampleBuffers; index++) { if (mSampleSources[index]->isPlaying()) { mSampleSources[index]->mixAudio((float*)audioData, mChannelCount, numFrames); } } return DataCallbackResult::Continue; } void SimpleMultiPlayer::onErrorAfterClose(AudioStream *oboeStream, Result error) { __android_log_print(ANDROID_LOG_INFO, TAG, "==== onErrorAfterClose() error:%d", error); resetAll(); if (openStream() && startStream()) { mOutputReset = true; } } void SimpleMultiPlayer::onErrorBeforeClose(AudioStream *, Result error) { __android_log_print(ANDROID_LOG_INFO, TAG, "==== onErrorBeforeClose() error:%d", error); } bool SimpleMultiPlayer::openStream() { __android_log_print(ANDROID_LOG_INFO, TAG, "openStream()"); // Create an audio stream AudioStreamBuilder builder; builder.setChannelCount(mChannelCount); // we will resample source data to device rate, so take default sample rate builder.setCallback(this); builder.setPerformanceMode(PerformanceMode::LowLatency); builder.setSharingMode(SharingMode::Exclusive); builder.setSampleRateConversionQuality(SampleRateConversionQuality::Medium); Result result = builder.openStream(mAudioStream); if (result != Result::OK){ __android_log_print( ANDROID_LOG_ERROR, TAG, "openStream failed. Error: %s", convertToText(result)); return false; } // Reduce stream latency by setting the buffer size to a multiple of the burst size // Note: this will fail with ErrorUnimplemented if we are using a callback with OpenSL ES // See oboe::AudioStreamBuffered::setBufferSizeInFrames result = mAudioStream->setBufferSizeInFrames( mAudioStream->getFramesPerBurst() * kBufferSizeInBursts); if (result != Result::OK) { __android_log_print( ANDROID_LOG_WARN, TAG, "setBufferSizeInFrames failed. Error: %s", convertToText(result)); } mSampleRate = mAudioStream->getSampleRate(); return true; } bool SimpleMultiPlayer::startStream() { Result result = mAudioStream->requestStart(); if (result != Result::OK){ __android_log_print( ANDROID_LOG_ERROR, TAG, "requestStart failed. Error: %s", convertToText(result)); return false; } return true; } void SimpleMultiPlayer::setupAudioStream(int32_t channelCount) { __android_log_print(ANDROID_LOG_INFO, TAG, "setupAudioStream()"); mChannelCount = channelCount; openStream(); } void SimpleMultiPlayer::teardownAudioStream() { __android_log_print(ANDROID_LOG_INFO, TAG, "teardownAudioStream()"); // tear down the player if (mAudioStream) { mAudioStream->stop(); mAudioStream->close(); mAudioStream.reset(); } } void SimpleMultiPlayer::addSampleSource(SampleSource* source, SampleBuffer* buffer) { buffer->resampleData(mSampleRate); mSampleBuffers.push_back(buffer); mSampleSources.push_back(source); mNumSampleBuffers++; } void SimpleMultiPlayer::unloadSampleData() { __android_log_print(ANDROID_LOG_INFO, TAG, "unloadSampleData()"); resetAll(); for (int32_t bufferIndex = 0; bufferIndex < mNumSampleBuffers; bufferIndex++) { delete mSampleBuffers[bufferIndex]; delete mSampleSources[bufferIndex]; } mSampleBuffers.clear(); mSampleSources.clear(); mNumSampleBuffers = 0; } void SimpleMultiPlayer::triggerDown(int32_t index) { if (index < mNumSampleBuffers) { mSampleSources[index]->setPlayMode(); } } void SimpleMultiPlayer::triggerUp(int32_t index) { if (index < mNumSampleBuffers) { mSampleSources[index]->setStopMode(); } } void SimpleMultiPlayer::resetAll() { for (int32_t bufferIndex = 0; bufferIndex < mNumSampleBuffers; bufferIndex++) { mSampleSources[bufferIndex]->setStopMode(); } } void SimpleMultiPlayer::setPan(int index, float pan) { mSampleSources[index]->setPan(pan); } float SimpleMultiPlayer::getPan(int index) { return mSampleSources[index]->getPan(); } void SimpleMultiPlayer::setGain(int index, float gain) { mSampleSources[index]->setGain(gain); } float SimpleMultiPlayer::getGain(int index) { return mSampleSources[index]->getGain(); } }