/*
* Copyright (C) 2012 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 <Log.h>
#include "audio/Buffer.h"
#include "audio/AudioLocal.h"
bool AudioLocal::prepare(AudioHardware::SamplingRate samplingRate, int gain, int /*mode*/)
{
LOGV("prepare");
// gain control not necessary in MobilePre as there is no control.
// This means audio source itself should be adjusted to control volume
if (mState == EStNone) {
if (run() != android::NO_ERROR) {
LOGE("AudioLocal cannot run");
// cannot run thread
return false;
}
mState = EStCreated;
} else if (mState == EStRunning) {
// wrong usage. first stop!
return false;
}
mClientCompletionWait.tryWait(); // this will reset semaphore to 0 if it is 1.
mSamplingRate = samplingRate;
return issueCommandAndWaitForCompletion(ECmInitialize);
}
bool AudioLocal::startPlaybackOrRecord(android::sp<Buffer>& buffer, int numberRepetition)
{
LOGV("startPlaybackOrRecord");
if (mState != EStInitialized) {
LOGE("startPlaybackOrRecord while not initialized");
// wrong state
return false;
}
mBuffer = buffer;
mNumberRepetition = numberRepetition;
mCurrentRepeat = 0;
return issueCommandAndWaitForCompletion(ECmRun);
}
bool AudioLocal::waitForCompletion()
{
int waitTimeInMsec = mBuffer->getSamples() / (mSamplingRate/1000);
waitTimeInMsec += COMMAND_WAIT_TIME_MSEC;
LOGD("waitForCompletion will wait for %d", waitTimeInMsec);
if (!mClientCompletionWait.timedWait(waitTimeInMsec)) {
LOGE("waitForCompletion time-out");
return false;
}
return mCompletionResult;
}
void AudioLocal::stopPlaybackOrRecord()
{
LOGV("stopPlaybackOrRecord");
if (mState == EStRunning) {
issueCommandAndWaitForCompletion(ECmStop);
}
if (mState != EStNone) { // thread alive
requestExit();
mCurrentCommand = ECmThreadStop;
mAudioThreadWait.post();
requestExitAndWait();
mState = EStNone;
}
}
bool AudioLocal::issueCommandAndWaitForCompletion(AudioCommand command)
{
mCurrentCommand = command;
mAudioThreadWait.post();
if (!mClientCommandWait.timedWait(COMMAND_WAIT_TIME_MSEC)) {
LOGE("issueCommandAndWaitForCompletion timeout cmd %d", command);
return false;
}
return mCommandResult;
}
AudioLocal::~AudioLocal()
{
LOGV("~AudioLocal");
}
AudioLocal::AudioLocal()
: mState(EStNone),
mCurrentCommand(ECmNone),
mClientCommandWait(0),
mClientCompletionWait(0),
mAudioThreadWait(0),
mCompletionResult(false)
{
LOGV("AudioLocal");
}
bool AudioLocal::threadLoop()
{
if (mCurrentCommand == ECmNone) {
if (mState == EStRunning) {
if (doPlaybackOrRecord(mBuffer)) {
// check exit condition
if (mBuffer->bufferHandled()) {
mCurrentRepeat++;
LOGV("repeat %d - %d", mCurrentRepeat, mNumberRepetition);
if (mCurrentRepeat == mNumberRepetition) {
LOGV("AudioLocal complete command");
mState = EStInitialized;
mCompletionResult = true;
mClientCompletionWait.post();
} else {
mBuffer->restart();
}
}
} else {
mState = EStInitialized;
//notify error
mCompletionResult = false;
mClientCompletionWait.post();
}
return true;
}
//LOGV("audio thread waiting");
mAudioThreadWait.wait();
//LOGV("audio thread waken up");
if (mCurrentCommand == ECmNone) {
return true; // continue to check exit condition
}
}
int pendingCommand = mCurrentCommand;
// now there is a command
switch (pendingCommand) {
case ECmInitialize:
mCommandResult = doPrepare(mSamplingRate, AudioHardware::SAMPLES_PER_ONE_GO);
if (mCommandResult) {
mState = EStInitialized;
}
break;
case ECmRun: {
mCommandResult = doPlaybackOrRecord(mBuffer);
if (mCommandResult) {
mState = EStRunning;
}
}
break;
case ECmStop:
doStop();
mState = EStCreated;
mCommandResult = true;
break;
case ECmThreadStop:
return false;
break;
default:
// this should not happen
ASSERT(false);
break;
}
mCurrentCommand = ECmNone;
mClientCommandWait.post();
return true;
}