|  | /* //device/servers/AudioFlinger/AudioDumpInterface.cpp | 
|  | ** | 
|  | ** Copyright 2008, 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. | 
|  | */ | 
|  |  | 
|  | #define LOG_TAG "AudioFlingerDump" | 
|  | //#define LOG_NDEBUG 0 | 
|  |  | 
|  | #include <stdint.h> | 
|  | #include <sys/types.h> | 
|  | #include <utils/Log.h> | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include "AudioDumpInterface.h" | 
|  |  | 
|  | namespace android { | 
|  |  | 
|  | // ---------------------------------------------------------------------------- | 
|  |  | 
|  | AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw) | 
|  | : mFirstHwOutput(true), mPolicyCommands(String8("")), mFileName(String8("")) | 
|  | { | 
|  | if(hw == 0) { | 
|  | LOGE("Dump construct hw = 0"); | 
|  | } | 
|  | mFinalInterface = hw; | 
|  | LOGV("Constructor %p, mFinalInterface %p", this, mFinalInterface); | 
|  | } | 
|  |  | 
|  |  | 
|  | AudioDumpInterface::~AudioDumpInterface() | 
|  | { | 
|  | for (size_t i = 0; i < mOutputs.size(); i++) { | 
|  | closeOutputStream((AudioStreamOut *)mOutputs[i]); | 
|  | } | 
|  | if(mFinalInterface) delete mFinalInterface; | 
|  | } | 
|  |  | 
|  |  | 
|  | AudioStreamOut* AudioDumpInterface::openOutputStream( | 
|  | uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) | 
|  | { | 
|  | AudioStreamOut* outFinal = NULL; | 
|  | int lFormat = AudioSystem::PCM_16_BIT; | 
|  | uint32_t lChannels = AudioSystem::CHANNEL_OUT_STEREO; | 
|  | uint32_t lRate = 44100; | 
|  |  | 
|  |  | 
|  | if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices) || mFirstHwOutput) { | 
|  | outFinal = mFinalInterface->openOutputStream(devices, format, channels, sampleRate, status); | 
|  | if (outFinal != 0) { | 
|  | lFormat = outFinal->format(); | 
|  | lChannels = outFinal->channels(); | 
|  | lRate = outFinal->sampleRate(); | 
|  | if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) { | 
|  | mFirstHwOutput = false; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | if (format != 0 && *format != 0) { | 
|  | lFormat = *format; | 
|  | } else { | 
|  | lFormat = AudioSystem::PCM_16_BIT; | 
|  | } | 
|  | if (channels != 0 && *channels != 0) { | 
|  | lChannels = *channels; | 
|  | } else { | 
|  | lChannels = AudioSystem::CHANNEL_OUT_STEREO; | 
|  | } | 
|  | if (sampleRate != 0 && *sampleRate != 0) { | 
|  | lRate = *sampleRate; | 
|  | } else { | 
|  | lRate = 44100; | 
|  | } | 
|  | if (status) *status = NO_ERROR; | 
|  | } | 
|  | LOGV("openOutputStream(), outFinal %p", outFinal); | 
|  |  | 
|  | AudioStreamOutDump *dumOutput = new AudioStreamOutDump(this, mOutputs.size(), outFinal, | 
|  | devices, lFormat, lChannels, lRate); | 
|  | mOutputs.add(dumOutput); | 
|  |  | 
|  | return dumOutput; | 
|  | } | 
|  |  | 
|  | void AudioDumpInterface::closeOutputStream(AudioStreamOut* out) | 
|  | { | 
|  | AudioStreamOutDump *dumpOut = (AudioStreamOutDump *)out; | 
|  |  | 
|  | if (mOutputs.indexOf(dumpOut) < 0) { | 
|  | LOGW("Attempt to close invalid output stream"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | LOGV("closeOutputStream() output %p", out); | 
|  |  | 
|  | dumpOut->standby(); | 
|  | if (dumpOut->finalStream() != NULL) { | 
|  | mFinalInterface->closeOutputStream(dumpOut->finalStream()); | 
|  | mFirstHwOutput = true; | 
|  | } | 
|  |  | 
|  | mOutputs.remove(dumpOut); | 
|  | delete dumpOut; | 
|  | } | 
|  |  | 
|  | AudioStreamIn* AudioDumpInterface::openInputStream(uint32_t devices, int *format, uint32_t *channels, | 
|  | uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics) | 
|  | { | 
|  | AudioStreamIn* inFinal = NULL; | 
|  | int lFormat = AudioSystem::PCM_16_BIT; | 
|  | uint32_t lChannels = AudioSystem::CHANNEL_IN_MONO; | 
|  | uint32_t lRate = 8000; | 
|  |  | 
|  |  | 
|  | if (mInputs.size() == 0) { | 
|  | inFinal = mFinalInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics); | 
|  | if (inFinal == 0) return 0; | 
|  |  | 
|  | lFormat = inFinal->format(); | 
|  | lChannels = inFinal->channels(); | 
|  | lRate = inFinal->sampleRate(); | 
|  | } else { | 
|  | if (format != 0 && *format != 0) lFormat = *format; | 
|  | if (channels != 0 && *channels != 0) lChannels = *channels; | 
|  | if (sampleRate != 0 && *sampleRate != 0) lRate = *sampleRate; | 
|  | if (status) *status = NO_ERROR; | 
|  | } | 
|  | LOGV("openInputStream(), inFinal %p", inFinal); | 
|  |  | 
|  | AudioStreamInDump *dumInput = new AudioStreamInDump(this, mInputs.size(), inFinal, | 
|  | devices, lFormat, lChannels, lRate); | 
|  | mInputs.add(dumInput); | 
|  |  | 
|  | return dumInput; | 
|  | } | 
|  | void AudioDumpInterface::closeInputStream(AudioStreamIn* in) | 
|  | { | 
|  | AudioStreamInDump *dumpIn = (AudioStreamInDump *)in; | 
|  |  | 
|  | if (mInputs.indexOf(dumpIn) < 0) { | 
|  | LOGW("Attempt to close invalid input stream"); | 
|  | return; | 
|  | } | 
|  | dumpIn->standby(); | 
|  | if (dumpIn->finalStream() != NULL) { | 
|  | mFinalInterface->closeInputStream(dumpIn->finalStream()); | 
|  | } | 
|  |  | 
|  | mInputs.remove(dumpIn); | 
|  | delete dumpIn; | 
|  | } | 
|  |  | 
|  |  | 
|  | status_t AudioDumpInterface::setParameters(const String8& keyValuePairs) | 
|  | { | 
|  | AudioParameter param = AudioParameter(keyValuePairs); | 
|  | String8 value; | 
|  | int valueInt; | 
|  | LOGV("setParameters %s", keyValuePairs.string()); | 
|  |  | 
|  | if (param.get(String8("test_cmd_file_name"), value) == NO_ERROR) { | 
|  | mFileName = value; | 
|  | param.remove(String8("test_cmd_file_name")); | 
|  | } | 
|  | if (param.get(String8("test_cmd_policy"), value) == NO_ERROR) { | 
|  | Mutex::Autolock _l(mLock); | 
|  | param.remove(String8("test_cmd_policy")); | 
|  | mPolicyCommands = param.toString(); | 
|  | LOGV("test_cmd_policy command %s written", mPolicyCommands.string()); | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | if (mFinalInterface != 0 ) return mFinalInterface->setParameters(keyValuePairs); | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | String8 AudioDumpInterface::getParameters(const String8& keys) | 
|  | { | 
|  | AudioParameter param = AudioParameter(keys); | 
|  | AudioParameter response; | 
|  | String8 value; | 
|  |  | 
|  | //    LOGV("getParameters %s", keys.string()); | 
|  | if (param.get(String8("test_cmd_policy"), value) == NO_ERROR) { | 
|  | Mutex::Autolock _l(mLock); | 
|  | if (mPolicyCommands.length() != 0) { | 
|  | response = AudioParameter(mPolicyCommands); | 
|  | response.addInt(String8("test_cmd_policy"), 1); | 
|  | } else { | 
|  | response.addInt(String8("test_cmd_policy"), 0); | 
|  | } | 
|  | param.remove(String8("test_cmd_policy")); | 
|  | //        LOGV("test_cmd_policy command %s read", mPolicyCommands.string()); | 
|  | } | 
|  |  | 
|  | if (param.get(String8("test_cmd_file_name"), value) == NO_ERROR) { | 
|  | response.add(String8("test_cmd_file_name"), mFileName); | 
|  | param.remove(String8("test_cmd_file_name")); | 
|  | } | 
|  |  | 
|  | String8 keyValuePairs = response.toString(); | 
|  |  | 
|  | if (param.size() && mFinalInterface != 0 ) { | 
|  | keyValuePairs += ";"; | 
|  | keyValuePairs += mFinalInterface->getParameters(param.toString()); | 
|  | } | 
|  |  | 
|  | return keyValuePairs; | 
|  | } | 
|  |  | 
|  |  | 
|  | // ---------------------------------------------------------------------------- | 
|  |  | 
|  | AudioStreamOutDump::AudioStreamOutDump(AudioDumpInterface *interface, | 
|  | int id, | 
|  | AudioStreamOut* finalStream, | 
|  | uint32_t devices, | 
|  | int format, | 
|  | uint32_t channels, | 
|  | uint32_t sampleRate) | 
|  | : mInterface(interface), mId(id), | 
|  | mSampleRate(sampleRate), mFormat(format), mChannels(channels), mLatency(0), mDevice(devices), | 
|  | mBufferSize(1024), mFinalStream(finalStream), mOutFile(0), mFileCount(0) | 
|  | { | 
|  | LOGV("AudioStreamOutDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream); | 
|  | } | 
|  |  | 
|  |  | 
|  | AudioStreamOutDump::~AudioStreamOutDump() | 
|  | { | 
|  | LOGV("AudioStreamOutDump destructor"); | 
|  | Close(); | 
|  | } | 
|  |  | 
|  | ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes) | 
|  | { | 
|  | ssize_t ret; | 
|  |  | 
|  | if (mFinalStream) { | 
|  | ret = mFinalStream->write(buffer, bytes); | 
|  | } else { | 
|  | usleep((bytes * 1000000) / frameSize() / sampleRate()); | 
|  | ret = bytes; | 
|  | } | 
|  | if(!mOutFile) { | 
|  | if (mInterface->fileName() != "") { | 
|  | char name[255]; | 
|  | sprintf(name, "%s_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount); | 
|  | mOutFile = fopen(name, "wb"); | 
|  | LOGV("Opening dump file %s, fh %p", name, mOutFile); | 
|  | } | 
|  | } | 
|  | if (mOutFile) { | 
|  | fwrite(buffer, bytes, 1, mOutFile); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | status_t AudioStreamOutDump::standby() | 
|  | { | 
|  | LOGV("AudioStreamOutDump standby(), mOutFile %p, mFinalStream %p", mOutFile, mFinalStream); | 
|  |  | 
|  | Close(); | 
|  | if (mFinalStream != 0 ) return mFinalStream->standby(); | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | uint32_t AudioStreamOutDump::sampleRate() const | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->sampleRate(); | 
|  | return mSampleRate; | 
|  | } | 
|  |  | 
|  | size_t AudioStreamOutDump::bufferSize() const | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->bufferSize(); | 
|  | return mBufferSize; | 
|  | } | 
|  |  | 
|  | uint32_t AudioStreamOutDump::channels() const | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->channels(); | 
|  | return mChannels; | 
|  | } | 
|  | int AudioStreamOutDump::format() const | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->format(); | 
|  | return mFormat; | 
|  | } | 
|  | uint32_t AudioStreamOutDump::latency() const | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->latency(); | 
|  | return 0; | 
|  | } | 
|  | status_t AudioStreamOutDump::setVolume(float left, float right) | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->setVolume(left, right); | 
|  | return NO_ERROR; | 
|  | } | 
|  | status_t AudioStreamOutDump::setParameters(const String8& keyValuePairs) | 
|  | { | 
|  | LOGV("AudioStreamOutDump::setParameters %s", keyValuePairs.string()); | 
|  |  | 
|  | if (mFinalStream != 0 ) { | 
|  | return mFinalStream->setParameters(keyValuePairs); | 
|  | } | 
|  |  | 
|  | AudioParameter param = AudioParameter(keyValuePairs); | 
|  | String8 value; | 
|  | int valueInt; | 
|  | status_t status = NO_ERROR; | 
|  |  | 
|  | if (param.getInt(String8("set_id"), valueInt) == NO_ERROR) { | 
|  | mId = valueInt; | 
|  | } | 
|  |  | 
|  | if (param.getInt(String8("format"), valueInt) == NO_ERROR) { | 
|  | if (mOutFile == 0) { | 
|  | mFormat = valueInt; | 
|  | } else { | 
|  | status = INVALID_OPERATION; | 
|  | } | 
|  | } | 
|  | if (param.getInt(String8("channels"), valueInt) == NO_ERROR) { | 
|  | if (valueInt == AudioSystem::CHANNEL_OUT_STEREO || valueInt == AudioSystem::CHANNEL_OUT_MONO) { | 
|  | mChannels = valueInt; | 
|  | } else { | 
|  | status = BAD_VALUE; | 
|  | } | 
|  | } | 
|  | if (param.getInt(String8("sampling_rate"), valueInt) == NO_ERROR) { | 
|  | if (valueInt > 0 && valueInt <= 48000) { | 
|  | if (mOutFile == 0) { | 
|  | mSampleRate = valueInt; | 
|  | } else { | 
|  | status = INVALID_OPERATION; | 
|  | } | 
|  | } else { | 
|  | status = BAD_VALUE; | 
|  | } | 
|  | } | 
|  | return status; | 
|  | } | 
|  |  | 
|  | String8 AudioStreamOutDump::getParameters(const String8& keys) | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->getParameters(keys); | 
|  |  | 
|  | AudioParameter param = AudioParameter(keys); | 
|  | return param.toString(); | 
|  | } | 
|  |  | 
|  | status_t AudioStreamOutDump::dump(int fd, const Vector<String16>& args) | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->dump(fd, args); | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | void AudioStreamOutDump::Close() | 
|  | { | 
|  | if(mOutFile) { | 
|  | fclose(mOutFile); | 
|  | mOutFile = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | status_t AudioStreamOutDump::getRenderPosition(uint32_t *dspFrames) | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->getRenderPosition(dspFrames); | 
|  | return INVALID_OPERATION; | 
|  | } | 
|  |  | 
|  | // ---------------------------------------------------------------------------- | 
|  |  | 
|  | AudioStreamInDump::AudioStreamInDump(AudioDumpInterface *interface, | 
|  | int id, | 
|  | AudioStreamIn* finalStream, | 
|  | uint32_t devices, | 
|  | int format, | 
|  | uint32_t channels, | 
|  | uint32_t sampleRate) | 
|  | : mInterface(interface), mId(id), | 
|  | mSampleRate(sampleRate), mFormat(format), mChannels(channels), mDevice(devices), | 
|  | mBufferSize(1024), mFinalStream(finalStream), mInFile(0) | 
|  | { | 
|  | LOGV("AudioStreamInDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream); | 
|  | } | 
|  |  | 
|  |  | 
|  | AudioStreamInDump::~AudioStreamInDump() | 
|  | { | 
|  | Close(); | 
|  | } | 
|  |  | 
|  | ssize_t AudioStreamInDump::read(void* buffer, ssize_t bytes) | 
|  | { | 
|  | if (mFinalStream) { | 
|  | return mFinalStream->read(buffer, bytes); | 
|  | } | 
|  |  | 
|  | usleep((bytes * 1000000) / frameSize() / sampleRate()); | 
|  |  | 
|  | if(!mInFile) { | 
|  | char name[255]; | 
|  | strcpy(name, "/sdcard/music/sine440"); | 
|  | if (channels() == AudioSystem::CHANNEL_IN_MONO) { | 
|  | strcat(name, "_mo"); | 
|  | } else { | 
|  | strcat(name, "_st"); | 
|  | } | 
|  | if (format() == AudioSystem::PCM_16_BIT) { | 
|  | strcat(name, "_16b"); | 
|  | } else { | 
|  | strcat(name, "_8b"); | 
|  | } | 
|  | if (sampleRate() < 16000) { | 
|  | strcat(name, "_8k"); | 
|  | } else if (sampleRate() < 32000) { | 
|  | strcat(name, "_22k"); | 
|  | } else if (sampleRate() < 48000) { | 
|  | strcat(name, "_44k"); | 
|  | } else { | 
|  | strcat(name, "_48k"); | 
|  | } | 
|  | strcat(name, ".wav"); | 
|  | mInFile = fopen(name, "rb"); | 
|  | LOGV("Opening dump file %s, fh %p", name, mInFile); | 
|  | if (mInFile) { | 
|  | fseek(mInFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET); | 
|  | } | 
|  |  | 
|  | } | 
|  | if (mInFile) { | 
|  | ssize_t bytesRead = fread(buffer, bytes, 1, mInFile); | 
|  | if (bytesRead != bytes) { | 
|  | fseek(mInFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET); | 
|  | fread((uint8_t *)buffer+bytesRead, bytes-bytesRead, 1, mInFile); | 
|  | } | 
|  | } | 
|  | return bytes; | 
|  | } | 
|  |  | 
|  | status_t AudioStreamInDump::standby() | 
|  | { | 
|  | LOGV("AudioStreamInDump standby(), mInFile %p, mFinalStream %p", mInFile, mFinalStream); | 
|  |  | 
|  | Close(); | 
|  | if (mFinalStream != 0 ) return mFinalStream->standby(); | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | status_t AudioStreamInDump::setGain(float gain) | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->setGain(gain); | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | uint32_t AudioStreamInDump::sampleRate() const | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->sampleRate(); | 
|  | return mSampleRate; | 
|  | } | 
|  |  | 
|  | size_t AudioStreamInDump::bufferSize() const | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->bufferSize(); | 
|  | return mBufferSize; | 
|  | } | 
|  |  | 
|  | uint32_t AudioStreamInDump::channels() const | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->channels(); | 
|  | return mChannels; | 
|  | } | 
|  |  | 
|  | int AudioStreamInDump::format() const | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->format(); | 
|  | return mFormat; | 
|  | } | 
|  |  | 
|  | status_t AudioStreamInDump::setParameters(const String8& keyValuePairs) | 
|  | { | 
|  | LOGV("AudioStreamInDump::setParameters()"); | 
|  | if (mFinalStream != 0 ) return mFinalStream->setParameters(keyValuePairs); | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | String8 AudioStreamInDump::getParameters(const String8& keys) | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->getParameters(keys); | 
|  |  | 
|  | AudioParameter param = AudioParameter(keys); | 
|  | return param.toString(); | 
|  | } | 
|  |  | 
|  | unsigned int AudioStreamInDump::getInputFramesLost() const | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->getInputFramesLost(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | status_t AudioStreamInDump::dump(int fd, const Vector<String16>& args) | 
|  | { | 
|  | if (mFinalStream != 0 ) return mFinalStream->dump(fd, args); | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | void AudioStreamInDump::Close() | 
|  | { | 
|  | if(mInFile) { | 
|  | fclose(mInFile); | 
|  | mInFile = 0; | 
|  | } | 
|  | } | 
|  | }; // namespace android |