| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2007 The Android Open Source Project | 
 | 3 |  * | 
 | 4 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 |  * you may not use this file except in compliance with the License. | 
 | 6 |  * You may obtain a copy of the License at | 
 | 7 |  * | 
 | 8 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 |  * | 
 | 10 |  * Unless required by applicable law or agreed to in writing, software | 
 | 11 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 |  * See the License for the specific language governing permissions and | 
 | 14 |  * limitations under the License. | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | //#define LOG_NDEBUG 0 | 
 | 18 | #define LOG_TAG "SoundPool" | 
 | 19 | #include <utils/Log.h> | 
 | 20 |  | 
| Glenn Kasten | 8973c04 | 2013-09-11 14:35:16 -0700 | [diff] [blame] | 21 | #define USE_SHARED_MEM_BUFFER | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 22 |  | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 23 | #include <media/AudioTrack.h> | 
 | 24 | #include <media/mediaplayer.h> | 
| James Dong | 559bf28 | 2012-03-28 10:29:14 -0700 | [diff] [blame] | 25 | #include <media/SoundPool.h> | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 26 | #include "SoundPoolThread.h" | 
 | 27 |  | 
 | 28 | namespace android | 
 | 29 | { | 
 | 30 |  | 
 | 31 | int kDefaultBufferCount = 4; | 
 | 32 | uint32_t kMaxSampleRate = 48000; | 
 | 33 | uint32_t kDefaultSampleRate = 44100; | 
 | 34 | uint32_t kDefaultFrameCount = 1200; | 
| Eric Laurent | 3d00aa6 | 2013-09-24 09:53:27 -0700 | [diff] [blame] | 35 | size_t kDefaultHeapSize = 1024 * 1024; // 1MB | 
 | 36 |  | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 37 |  | 
 | 38 | SoundPool::SoundPool(int maxChannels, audio_stream_type_t streamType, int srcQuality) | 
 | 39 | { | 
 | 40 |     ALOGV("SoundPool constructor: maxChannels=%d, streamType=%d, srcQuality=%d", | 
 | 41 |             maxChannels, streamType, srcQuality); | 
 | 42 |  | 
 | 43 |     // check limits | 
 | 44 |     mMaxChannels = maxChannels; | 
 | 45 |     if (mMaxChannels < 1) { | 
 | 46 |         mMaxChannels = 1; | 
 | 47 |     } | 
 | 48 |     else if (mMaxChannels > 32) { | 
 | 49 |         mMaxChannels = 32; | 
 | 50 |     } | 
 | 51 |     ALOGW_IF(maxChannels != mMaxChannels, "App requested %d channels", maxChannels); | 
 | 52 |  | 
 | 53 |     mQuit = false; | 
 | 54 |     mDecodeThread = 0; | 
 | 55 |     mStreamType = streamType; | 
 | 56 |     mSrcQuality = srcQuality; | 
 | 57 |     mAllocated = 0; | 
 | 58 |     mNextSampleID = 0; | 
 | 59 |     mNextChannelID = 0; | 
 | 60 |  | 
 | 61 |     mCallback = 0; | 
 | 62 |     mUserData = 0; | 
 | 63 |  | 
 | 64 |     mChannelPool = new SoundChannel[mMaxChannels]; | 
 | 65 |     for (int i = 0; i < mMaxChannels; ++i) { | 
 | 66 |         mChannelPool[i].init(this); | 
 | 67 |         mChannels.push_back(&mChannelPool[i]); | 
 | 68 |     } | 
 | 69 |  | 
 | 70 |     // start decode thread | 
 | 71 |     startThreads(); | 
 | 72 | } | 
 | 73 |  | 
 | 74 | SoundPool::~SoundPool() | 
 | 75 | { | 
 | 76 |     ALOGV("SoundPool destructor"); | 
 | 77 |     mDecodeThread->quit(); | 
 | 78 |     quit(); | 
 | 79 |  | 
 | 80 |     Mutex::Autolock lock(&mLock); | 
 | 81 |  | 
 | 82 |     mChannels.clear(); | 
 | 83 |     if (mChannelPool) | 
 | 84 |         delete [] mChannelPool; | 
 | 85 |     // clean up samples | 
 | 86 |     ALOGV("clear samples"); | 
 | 87 |     mSamples.clear(); | 
 | 88 |  | 
 | 89 |     if (mDecodeThread) | 
 | 90 |         delete mDecodeThread; | 
 | 91 | } | 
 | 92 |  | 
 | 93 | void SoundPool::addToRestartList(SoundChannel* channel) | 
 | 94 | { | 
 | 95 |     Mutex::Autolock lock(&mRestartLock); | 
 | 96 |     if (!mQuit) { | 
 | 97 |         mRestart.push_back(channel); | 
 | 98 |         mCondition.signal(); | 
 | 99 |     } | 
 | 100 | } | 
 | 101 |  | 
 | 102 | void SoundPool::addToStopList(SoundChannel* channel) | 
 | 103 | { | 
 | 104 |     Mutex::Autolock lock(&mRestartLock); | 
 | 105 |     if (!mQuit) { | 
 | 106 |         mStop.push_back(channel); | 
 | 107 |         mCondition.signal(); | 
 | 108 |     } | 
 | 109 | } | 
 | 110 |  | 
 | 111 | int SoundPool::beginThread(void* arg) | 
 | 112 | { | 
 | 113 |     SoundPool* p = (SoundPool*)arg; | 
 | 114 |     return p->run(); | 
 | 115 | } | 
 | 116 |  | 
 | 117 | int SoundPool::run() | 
 | 118 | { | 
 | 119 |     mRestartLock.lock(); | 
 | 120 |     while (!mQuit) { | 
 | 121 |         mCondition.wait(mRestartLock); | 
 | 122 |         ALOGV("awake"); | 
 | 123 |         if (mQuit) break; | 
 | 124 |  | 
 | 125 |         while (!mStop.empty()) { | 
 | 126 |             SoundChannel* channel; | 
 | 127 |             ALOGV("Getting channel from stop list"); | 
 | 128 |             List<SoundChannel* >::iterator iter = mStop.begin(); | 
 | 129 |             channel = *iter; | 
 | 130 |             mStop.erase(iter); | 
 | 131 |             mRestartLock.unlock(); | 
 | 132 |             if (channel != 0) { | 
 | 133 |                 Mutex::Autolock lock(&mLock); | 
 | 134 |                 channel->stop(); | 
 | 135 |             } | 
 | 136 |             mRestartLock.lock(); | 
 | 137 |             if (mQuit) break; | 
 | 138 |         } | 
 | 139 |  | 
 | 140 |         while (!mRestart.empty()) { | 
 | 141 |             SoundChannel* channel; | 
 | 142 |             ALOGV("Getting channel from list"); | 
 | 143 |             List<SoundChannel*>::iterator iter = mRestart.begin(); | 
 | 144 |             channel = *iter; | 
 | 145 |             mRestart.erase(iter); | 
 | 146 |             mRestartLock.unlock(); | 
 | 147 |             if (channel != 0) { | 
 | 148 |                 Mutex::Autolock lock(&mLock); | 
 | 149 |                 channel->nextEvent(); | 
 | 150 |             } | 
 | 151 |             mRestartLock.lock(); | 
 | 152 |             if (mQuit) break; | 
 | 153 |         } | 
 | 154 |     } | 
 | 155 |  | 
 | 156 |     mStop.clear(); | 
 | 157 |     mRestart.clear(); | 
 | 158 |     mCondition.signal(); | 
 | 159 |     mRestartLock.unlock(); | 
 | 160 |     ALOGV("goodbye"); | 
 | 161 |     return 0; | 
 | 162 | } | 
 | 163 |  | 
 | 164 | void SoundPool::quit() | 
 | 165 | { | 
 | 166 |     mRestartLock.lock(); | 
 | 167 |     mQuit = true; | 
 | 168 |     mCondition.signal(); | 
 | 169 |     mCondition.wait(mRestartLock); | 
 | 170 |     ALOGV("return from quit"); | 
 | 171 |     mRestartLock.unlock(); | 
 | 172 | } | 
 | 173 |  | 
 | 174 | bool SoundPool::startThreads() | 
 | 175 | { | 
 | 176 |     createThreadEtc(beginThread, this, "SoundPool"); | 
 | 177 |     if (mDecodeThread == NULL) | 
 | 178 |         mDecodeThread = new SoundPoolThread(this); | 
 | 179 |     return mDecodeThread != NULL; | 
 | 180 | } | 
 | 181 |  | 
 | 182 | SoundChannel* SoundPool::findChannel(int channelID) | 
 | 183 | { | 
 | 184 |     for (int i = 0; i < mMaxChannels; ++i) { | 
 | 185 |         if (mChannelPool[i].channelID() == channelID) { | 
 | 186 |             return &mChannelPool[i]; | 
 | 187 |         } | 
 | 188 |     } | 
 | 189 |     return NULL; | 
 | 190 | } | 
 | 191 |  | 
 | 192 | SoundChannel* SoundPool::findNextChannel(int channelID) | 
 | 193 | { | 
 | 194 |     for (int i = 0; i < mMaxChannels; ++i) { | 
 | 195 |         if (mChannelPool[i].nextChannelID() == channelID) { | 
 | 196 |             return &mChannelPool[i]; | 
 | 197 |         } | 
 | 198 |     } | 
 | 199 |     return NULL; | 
 | 200 | } | 
 | 201 |  | 
| Glenn Kasten | 7c7be1e | 2013-12-19 16:34:04 -0800 | [diff] [blame^] | 202 | int SoundPool::load(const char* path, int priority __unused) | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 203 | { | 
 | 204 |     ALOGV("load: path=%s, priority=%d", path, priority); | 
 | 205 |     Mutex::Autolock lock(&mLock); | 
 | 206 |     sp<Sample> sample = new Sample(++mNextSampleID, path); | 
 | 207 |     mSamples.add(sample->sampleID(), sample); | 
 | 208 |     doLoad(sample); | 
 | 209 |     return sample->sampleID(); | 
 | 210 | } | 
 | 211 |  | 
| Glenn Kasten | 7c7be1e | 2013-12-19 16:34:04 -0800 | [diff] [blame^] | 212 | int SoundPool::load(int fd, int64_t offset, int64_t length, int priority __unused) | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 213 | { | 
 | 214 |     ALOGV("load: fd=%d, offset=%lld, length=%lld, priority=%d", | 
 | 215 |             fd, offset, length, priority); | 
 | 216 |     Mutex::Autolock lock(&mLock); | 
 | 217 |     sp<Sample> sample = new Sample(++mNextSampleID, fd, offset, length); | 
 | 218 |     mSamples.add(sample->sampleID(), sample); | 
 | 219 |     doLoad(sample); | 
 | 220 |     return sample->sampleID(); | 
 | 221 | } | 
 | 222 |  | 
 | 223 | void SoundPool::doLoad(sp<Sample>& sample) | 
 | 224 | { | 
 | 225 |     ALOGV("doLoad: loading sample sampleID=%d", sample->sampleID()); | 
 | 226 |     sample->startLoad(); | 
 | 227 |     mDecodeThread->loadSample(sample->sampleID()); | 
 | 228 | } | 
 | 229 |  | 
 | 230 | bool SoundPool::unload(int sampleID) | 
 | 231 | { | 
 | 232 |     ALOGV("unload: sampleID=%d", sampleID); | 
 | 233 |     Mutex::Autolock lock(&mLock); | 
 | 234 |     return mSamples.removeItem(sampleID); | 
 | 235 | } | 
 | 236 |  | 
 | 237 | int SoundPool::play(int sampleID, float leftVolume, float rightVolume, | 
 | 238 |         int priority, int loop, float rate) | 
 | 239 | { | 
 | 240 |     ALOGV("play sampleID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f", | 
 | 241 |             sampleID, leftVolume, rightVolume, priority, loop, rate); | 
 | 242 |     sp<Sample> sample; | 
 | 243 |     SoundChannel* channel; | 
 | 244 |     int channelID; | 
 | 245 |  | 
 | 246 |     Mutex::Autolock lock(&mLock); | 
 | 247 |  | 
 | 248 |     if (mQuit) { | 
 | 249 |         return 0; | 
 | 250 |     } | 
 | 251 |     // is sample ready? | 
 | 252 |     sample = findSample(sampleID); | 
 | 253 |     if ((sample == 0) || (sample->state() != Sample::READY)) { | 
 | 254 |         ALOGW("  sample %d not READY", sampleID); | 
 | 255 |         return 0; | 
 | 256 |     } | 
 | 257 |  | 
 | 258 |     dump(); | 
 | 259 |  | 
 | 260 |     // allocate a channel | 
 | 261 |     channel = allocateChannel_l(priority); | 
 | 262 |  | 
 | 263 |     // no channel allocated - return 0 | 
 | 264 |     if (!channel) { | 
 | 265 |         ALOGV("No channel allocated"); | 
 | 266 |         return 0; | 
 | 267 |     } | 
 | 268 |  | 
 | 269 |     channelID = ++mNextChannelID; | 
 | 270 |  | 
 | 271 |     ALOGV("play channel %p state = %d", channel, channel->state()); | 
 | 272 |     channel->play(sample, channelID, leftVolume, rightVolume, priority, loop, rate); | 
 | 273 |     return channelID; | 
 | 274 | } | 
 | 275 |  | 
 | 276 | SoundChannel* SoundPool::allocateChannel_l(int priority) | 
 | 277 | { | 
 | 278 |     List<SoundChannel*>::iterator iter; | 
 | 279 |     SoundChannel* channel = NULL; | 
 | 280 |  | 
 | 281 |     // allocate a channel | 
 | 282 |     if (!mChannels.empty()) { | 
 | 283 |         iter = mChannels.begin(); | 
 | 284 |         if (priority >= (*iter)->priority()) { | 
 | 285 |             channel = *iter; | 
 | 286 |             mChannels.erase(iter); | 
 | 287 |             ALOGV("Allocated active channel"); | 
 | 288 |         } | 
 | 289 |     } | 
 | 290 |  | 
 | 291 |     // update priority and put it back in the list | 
 | 292 |     if (channel) { | 
 | 293 |         channel->setPriority(priority); | 
 | 294 |         for (iter = mChannels.begin(); iter != mChannels.end(); ++iter) { | 
 | 295 |             if (priority < (*iter)->priority()) { | 
 | 296 |                 break; | 
 | 297 |             } | 
 | 298 |         } | 
 | 299 |         mChannels.insert(iter, channel); | 
 | 300 |     } | 
 | 301 |     return channel; | 
 | 302 | } | 
 | 303 |  | 
 | 304 | // move a channel from its current position to the front of the list | 
 | 305 | void SoundPool::moveToFront_l(SoundChannel* channel) | 
 | 306 | { | 
 | 307 |     for (List<SoundChannel*>::iterator iter = mChannels.begin(); iter != mChannels.end(); ++iter) { | 
 | 308 |         if (*iter == channel) { | 
 | 309 |             mChannels.erase(iter); | 
 | 310 |             mChannels.push_front(channel); | 
 | 311 |             break; | 
 | 312 |         } | 
 | 313 |     } | 
 | 314 | } | 
 | 315 |  | 
 | 316 | void SoundPool::pause(int channelID) | 
 | 317 | { | 
 | 318 |     ALOGV("pause(%d)", channelID); | 
 | 319 |     Mutex::Autolock lock(&mLock); | 
 | 320 |     SoundChannel* channel = findChannel(channelID); | 
 | 321 |     if (channel) { | 
 | 322 |         channel->pause(); | 
 | 323 |     } | 
 | 324 | } | 
 | 325 |  | 
 | 326 | void SoundPool::autoPause() | 
 | 327 | { | 
 | 328 |     ALOGV("autoPause()"); | 
 | 329 |     Mutex::Autolock lock(&mLock); | 
 | 330 |     for (int i = 0; i < mMaxChannels; ++i) { | 
 | 331 |         SoundChannel* channel = &mChannelPool[i]; | 
 | 332 |         channel->autoPause(); | 
 | 333 |     } | 
 | 334 | } | 
 | 335 |  | 
 | 336 | void SoundPool::resume(int channelID) | 
 | 337 | { | 
 | 338 |     ALOGV("resume(%d)", channelID); | 
 | 339 |     Mutex::Autolock lock(&mLock); | 
 | 340 |     SoundChannel* channel = findChannel(channelID); | 
 | 341 |     if (channel) { | 
 | 342 |         channel->resume(); | 
 | 343 |     } | 
 | 344 | } | 
 | 345 |  | 
 | 346 | void SoundPool::autoResume() | 
 | 347 | { | 
 | 348 |     ALOGV("autoResume()"); | 
 | 349 |     Mutex::Autolock lock(&mLock); | 
 | 350 |     for (int i = 0; i < mMaxChannels; ++i) { | 
 | 351 |         SoundChannel* channel = &mChannelPool[i]; | 
 | 352 |         channel->autoResume(); | 
 | 353 |     } | 
 | 354 | } | 
 | 355 |  | 
 | 356 | void SoundPool::stop(int channelID) | 
 | 357 | { | 
 | 358 |     ALOGV("stop(%d)", channelID); | 
 | 359 |     Mutex::Autolock lock(&mLock); | 
 | 360 |     SoundChannel* channel = findChannel(channelID); | 
 | 361 |     if (channel) { | 
 | 362 |         channel->stop(); | 
 | 363 |     } else { | 
 | 364 |         channel = findNextChannel(channelID); | 
 | 365 |         if (channel) | 
 | 366 |             channel->clearNextEvent(); | 
 | 367 |     } | 
 | 368 | } | 
 | 369 |  | 
 | 370 | void SoundPool::setVolume(int channelID, float leftVolume, float rightVolume) | 
 | 371 | { | 
 | 372 |     Mutex::Autolock lock(&mLock); | 
 | 373 |     SoundChannel* channel = findChannel(channelID); | 
 | 374 |     if (channel) { | 
 | 375 |         channel->setVolume(leftVolume, rightVolume); | 
 | 376 |     } | 
 | 377 | } | 
 | 378 |  | 
 | 379 | void SoundPool::setPriority(int channelID, int priority) | 
 | 380 | { | 
 | 381 |     ALOGV("setPriority(%d, %d)", channelID, priority); | 
 | 382 |     Mutex::Autolock lock(&mLock); | 
 | 383 |     SoundChannel* channel = findChannel(channelID); | 
 | 384 |     if (channel) { | 
 | 385 |         channel->setPriority(priority); | 
 | 386 |     } | 
 | 387 | } | 
 | 388 |  | 
 | 389 | void SoundPool::setLoop(int channelID, int loop) | 
 | 390 | { | 
 | 391 |     ALOGV("setLoop(%d, %d)", channelID, loop); | 
 | 392 |     Mutex::Autolock lock(&mLock); | 
 | 393 |     SoundChannel* channel = findChannel(channelID); | 
 | 394 |     if (channel) { | 
 | 395 |         channel->setLoop(loop); | 
 | 396 |     } | 
 | 397 | } | 
 | 398 |  | 
 | 399 | void SoundPool::setRate(int channelID, float rate) | 
 | 400 | { | 
 | 401 |     ALOGV("setRate(%d, %f)", channelID, rate); | 
 | 402 |     Mutex::Autolock lock(&mLock); | 
 | 403 |     SoundChannel* channel = findChannel(channelID); | 
 | 404 |     if (channel) { | 
 | 405 |         channel->setRate(rate); | 
 | 406 |     } | 
 | 407 | } | 
 | 408 |  | 
 | 409 | // call with lock held | 
 | 410 | void SoundPool::done_l(SoundChannel* channel) | 
 | 411 | { | 
 | 412 |     ALOGV("done_l(%d)", channel->channelID()); | 
 | 413 |     // if "stolen", play next event | 
 | 414 |     if (channel->nextChannelID() != 0) { | 
 | 415 |         ALOGV("add to restart list"); | 
 | 416 |         addToRestartList(channel); | 
 | 417 |     } | 
 | 418 |  | 
 | 419 |     // return to idle state | 
 | 420 |     else { | 
 | 421 |         ALOGV("move to front"); | 
 | 422 |         moveToFront_l(channel); | 
 | 423 |     } | 
 | 424 | } | 
 | 425 |  | 
 | 426 | void SoundPool::setCallback(SoundPoolCallback* callback, void* user) | 
 | 427 | { | 
 | 428 |     Mutex::Autolock lock(&mCallbackLock); | 
 | 429 |     mCallback = callback; | 
 | 430 |     mUserData = user; | 
 | 431 | } | 
 | 432 |  | 
 | 433 | void SoundPool::notify(SoundPoolEvent event) | 
 | 434 | { | 
 | 435 |     Mutex::Autolock lock(&mCallbackLock); | 
 | 436 |     if (mCallback != NULL) { | 
 | 437 |         mCallback(event, this, mUserData); | 
 | 438 |     } | 
 | 439 | } | 
 | 440 |  | 
 | 441 | void SoundPool::dump() | 
 | 442 | { | 
 | 443 |     for (int i = 0; i < mMaxChannels; ++i) { | 
 | 444 |         mChannelPool[i].dump(); | 
 | 445 |     } | 
 | 446 | } | 
 | 447 |  | 
 | 448 |  | 
 | 449 | Sample::Sample(int sampleID, const char* url) | 
 | 450 | { | 
 | 451 |     init(); | 
 | 452 |     mSampleID = sampleID; | 
 | 453 |     mUrl = strdup(url); | 
 | 454 |     ALOGV("create sampleID=%d, url=%s", mSampleID, mUrl); | 
 | 455 | } | 
 | 456 |  | 
 | 457 | Sample::Sample(int sampleID, int fd, int64_t offset, int64_t length) | 
 | 458 | { | 
 | 459 |     init(); | 
 | 460 |     mSampleID = sampleID; | 
 | 461 |     mFd = dup(fd); | 
 | 462 |     mOffset = offset; | 
 | 463 |     mLength = length; | 
 | 464 |     ALOGV("create sampleID=%d, fd=%d, offset=%lld, length=%lld", mSampleID, mFd, mLength, mOffset); | 
 | 465 | } | 
 | 466 |  | 
 | 467 | void Sample::init() | 
 | 468 | { | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 469 |     mSize = 0; | 
 | 470 |     mRefCount = 0; | 
 | 471 |     mSampleID = 0; | 
 | 472 |     mState = UNLOADED; | 
 | 473 |     mFd = -1; | 
 | 474 |     mOffset = 0; | 
 | 475 |     mLength = 0; | 
 | 476 |     mUrl = 0; | 
 | 477 | } | 
 | 478 |  | 
 | 479 | Sample::~Sample() | 
 | 480 | { | 
 | 481 |     ALOGV("Sample::destructor sampleID=%d, fd=%d", mSampleID, mFd); | 
 | 482 |     if (mFd > 0) { | 
 | 483 |         ALOGV("close(%d)", mFd); | 
 | 484 |         ::close(mFd); | 
 | 485 |     } | 
| Marco Nelissen | a6b47a1 | 2012-11-19 09:49:18 -0800 | [diff] [blame] | 486 |     free(mUrl); | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 487 | } | 
 | 488 |  | 
 | 489 | status_t Sample::doLoad() | 
 | 490 | { | 
 | 491 |     uint32_t sampleRate; | 
 | 492 |     int numChannels; | 
 | 493 |     audio_format_t format; | 
| Eric Laurent | 3d00aa6 | 2013-09-24 09:53:27 -0700 | [diff] [blame] | 494 |     status_t status; | 
 | 495 |     mHeap = new MemoryHeapBase(kDefaultHeapSize); | 
 | 496 |  | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 497 |     ALOGV("Start decode"); | 
 | 498 |     if (mUrl) { | 
| Eric Laurent | 3d00aa6 | 2013-09-24 09:53:27 -0700 | [diff] [blame] | 499 |         status = MediaPlayer::decode(mUrl, &sampleRate, &numChannels, &format, mHeap, &mSize); | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 500 |     } else { | 
| Eric Laurent | 3d00aa6 | 2013-09-24 09:53:27 -0700 | [diff] [blame] | 501 |         status = MediaPlayer::decode(mFd, mOffset, mLength, &sampleRate, &numChannels, &format, | 
 | 502 |                                      mHeap, &mSize); | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 503 |         ALOGV("close(%d)", mFd); | 
 | 504 |         ::close(mFd); | 
 | 505 |         mFd = -1; | 
 | 506 |     } | 
| Eric Laurent | 3d00aa6 | 2013-09-24 09:53:27 -0700 | [diff] [blame] | 507 |     if (status != NO_ERROR) { | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 508 |         ALOGE("Unable to load sample: %s", mUrl); | 
| Eric Laurent | 3d00aa6 | 2013-09-24 09:53:27 -0700 | [diff] [blame] | 509 |         goto error; | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 510 |     } | 
 | 511 |     ALOGV("pointer = %p, size = %u, sampleRate = %u, numChannels = %d", | 
| Eric Laurent | 3d00aa6 | 2013-09-24 09:53:27 -0700 | [diff] [blame] | 512 |           mHeap->getBase(), mSize, sampleRate, numChannels); | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 513 |  | 
 | 514 |     if (sampleRate > kMaxSampleRate) { | 
 | 515 |        ALOGE("Sample rate (%u) out of range", sampleRate); | 
| Eric Laurent | 3d00aa6 | 2013-09-24 09:53:27 -0700 | [diff] [blame] | 516 |        status = BAD_VALUE; | 
 | 517 |        goto error; | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 518 |     } | 
 | 519 |  | 
 | 520 |     if ((numChannels < 1) || (numChannels > 2)) { | 
 | 521 |         ALOGE("Sample channel count (%d) out of range", numChannels); | 
| Eric Laurent | 3d00aa6 | 2013-09-24 09:53:27 -0700 | [diff] [blame] | 522 |         status = BAD_VALUE; | 
 | 523 |         goto error; | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 524 |     } | 
 | 525 |  | 
| Eric Laurent | 3d00aa6 | 2013-09-24 09:53:27 -0700 | [diff] [blame] | 526 |     mData = new MemoryBase(mHeap, 0, mSize); | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 527 |     mSampleRate = sampleRate; | 
 | 528 |     mNumChannels = numChannels; | 
 | 529 |     mFormat = format; | 
 | 530 |     mState = READY; | 
| Eric Laurent | 3d00aa6 | 2013-09-24 09:53:27 -0700 | [diff] [blame] | 531 |     return NO_ERROR; | 
 | 532 |  | 
 | 533 | error: | 
 | 534 |     mHeap.clear(); | 
 | 535 |     return status; | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 536 | } | 
 | 537 |  | 
 | 538 |  | 
 | 539 | void SoundChannel::init(SoundPool* soundPool) | 
 | 540 | { | 
 | 541 |     mSoundPool = soundPool; | 
 | 542 | } | 
 | 543 |  | 
 | 544 | // call with sound pool lock held | 
 | 545 | void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftVolume, | 
 | 546 |         float rightVolume, int priority, int loop, float rate) | 
 | 547 | { | 
| Glenn Kasten | 2799d74 | 2013-05-30 14:33:29 -0700 | [diff] [blame] | 548 |     sp<AudioTrack> oldTrack; | 
 | 549 |     sp<AudioTrack> newTrack; | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 550 |     status_t status; | 
 | 551 |  | 
 | 552 |     { // scope for the lock | 
 | 553 |         Mutex::Autolock lock(&mLock); | 
 | 554 |  | 
 | 555 |         ALOGV("SoundChannel::play %p: sampleID=%d, channelID=%d, leftVolume=%f, rightVolume=%f," | 
 | 556 |                 " priority=%d, loop=%d, rate=%f", | 
 | 557 |                 this, sample->sampleID(), nextChannelID, leftVolume, rightVolume, | 
 | 558 |                 priority, loop, rate); | 
 | 559 |  | 
 | 560 |         // if not idle, this voice is being stolen | 
 | 561 |         if (mState != IDLE) { | 
 | 562 |             ALOGV("channel %d stolen - event queued for channel %d", channelID(), nextChannelID); | 
 | 563 |             mNextEvent.set(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate); | 
 | 564 |             stop_l(); | 
 | 565 |             return; | 
 | 566 |         } | 
 | 567 |  | 
 | 568 |         // initialize track | 
| Glenn Kasten | e33054e | 2012-11-14 12:54:39 -0800 | [diff] [blame] | 569 |         size_t afFrameCount; | 
| Glenn Kasten | 3b16c76 | 2012-11-14 08:44:39 -0800 | [diff] [blame] | 570 |         uint32_t afSampleRate; | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 571 |         audio_stream_type_t streamType = mSoundPool->streamType(); | 
 | 572 |         if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { | 
 | 573 |             afFrameCount = kDefaultFrameCount; | 
 | 574 |         } | 
 | 575 |         if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { | 
 | 576 |             afSampleRate = kDefaultSampleRate; | 
 | 577 |         } | 
 | 578 |         int numChannels = sample->numChannels(); | 
 | 579 |         uint32_t sampleRate = uint32_t(float(sample->sampleRate()) * rate + 0.5); | 
 | 580 |         uint32_t totalFrames = (kDefaultBufferCount * afFrameCount * sampleRate) / afSampleRate; | 
 | 581 |         uint32_t bufferFrames = (totalFrames + (kDefaultBufferCount - 1)) / kDefaultBufferCount; | 
 | 582 |         uint32_t frameCount = 0; | 
 | 583 |  | 
 | 584 |         if (loop) { | 
 | 585 |             frameCount = sample->size()/numChannels/ | 
 | 586 |                 ((sample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t)); | 
 | 587 |         } | 
 | 588 |  | 
 | 589 | #ifndef USE_SHARED_MEM_BUFFER | 
 | 590 |         // Ensure minimum audio buffer size in case of short looped sample | 
 | 591 |         if(frameCount < totalFrames) { | 
 | 592 |             frameCount = totalFrames; | 
 | 593 |         } | 
 | 594 | #endif | 
 | 595 |  | 
 | 596 |         // mToggle toggles each time a track is started on a given channel. | 
 | 597 |         // The toggle is concatenated with the SoundChannel address and passed to AudioTrack | 
 | 598 |         // as callback user data. This enables the detection of callbacks received from the old | 
 | 599 |         // audio track while the new one is being started and avoids processing them with | 
 | 600 |         // wrong audio audio buffer size  (mAudioBufferSize) | 
 | 601 |         unsigned long toggle = mToggle ^ 1; | 
 | 602 |         void *userData = (void *)((unsigned long)this | toggle); | 
| Glenn Kasten | 79c5786 | 2013-10-30 09:47:17 -0700 | [diff] [blame] | 603 |         audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(numChannels); | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 604 |  | 
 | 605 |         // do not create a new audio track if current track is compatible with sample parameters | 
 | 606 | #ifdef USE_SHARED_MEM_BUFFER | 
 | 607 |         newTrack = new AudioTrack(streamType, sampleRate, sample->format(), | 
| Glenn Kasten | 79c5786 | 2013-10-30 09:47:17 -0700 | [diff] [blame] | 608 |                 channelMask, sample->getIMemory(), AUDIO_OUTPUT_FLAG_FAST, callback, userData); | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 609 | #else | 
 | 610 |         newTrack = new AudioTrack(streamType, sampleRate, sample->format(), | 
| Glenn Kasten | 79c5786 | 2013-10-30 09:47:17 -0700 | [diff] [blame] | 611 |                 channelMask, frameCount, AUDIO_OUTPUT_FLAG_FAST, callback, userData, | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 612 |                 bufferFrames); | 
 | 613 | #endif | 
 | 614 |         oldTrack = mAudioTrack; | 
 | 615 |         status = newTrack->initCheck(); | 
 | 616 |         if (status != NO_ERROR) { | 
 | 617 |             ALOGE("Error creating AudioTrack"); | 
 | 618 |             goto exit; | 
 | 619 |         } | 
| Glenn Kasten | 2799d74 | 2013-05-30 14:33:29 -0700 | [diff] [blame] | 620 |         ALOGV("setVolume %p", newTrack.get()); | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 621 |         newTrack->setVolume(leftVolume, rightVolume); | 
 | 622 |         newTrack->setLoop(0, frameCount, loop); | 
 | 623 |  | 
 | 624 |         // From now on, AudioTrack callbacks received with previous toggle value will be ignored. | 
 | 625 |         mToggle = toggle; | 
 | 626 |         mAudioTrack = newTrack; | 
 | 627 |         mPos = 0; | 
 | 628 |         mSample = sample; | 
 | 629 |         mChannelID = nextChannelID; | 
 | 630 |         mPriority = priority; | 
 | 631 |         mLoop = loop; | 
 | 632 |         mLeftVolume = leftVolume; | 
 | 633 |         mRightVolume = rightVolume; | 
 | 634 |         mNumChannels = numChannels; | 
 | 635 |         mRate = rate; | 
 | 636 |         clearNextEvent(); | 
 | 637 |         mState = PLAYING; | 
 | 638 |         mAudioTrack->start(); | 
 | 639 |         mAudioBufferSize = newTrack->frameCount()*newTrack->frameSize(); | 
 | 640 |     } | 
 | 641 |  | 
 | 642 | exit: | 
| Glenn Kasten | 2799d74 | 2013-05-30 14:33:29 -0700 | [diff] [blame] | 643 |     ALOGV("delete oldTrack %p", oldTrack.get()); | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 644 |     if (status != NO_ERROR) { | 
| Glenn Kasten | 2799d74 | 2013-05-30 14:33:29 -0700 | [diff] [blame] | 645 |         mAudioTrack.clear(); | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 646 |     } | 
 | 647 | } | 
 | 648 |  | 
 | 649 | void SoundChannel::nextEvent() | 
 | 650 | { | 
 | 651 |     sp<Sample> sample; | 
 | 652 |     int nextChannelID; | 
 | 653 |     float leftVolume; | 
 | 654 |     float rightVolume; | 
 | 655 |     int priority; | 
 | 656 |     int loop; | 
 | 657 |     float rate; | 
 | 658 |  | 
 | 659 |     // check for valid event | 
 | 660 |     { | 
 | 661 |         Mutex::Autolock lock(&mLock); | 
 | 662 |         nextChannelID = mNextEvent.channelID(); | 
 | 663 |         if (nextChannelID  == 0) { | 
 | 664 |             ALOGV("stolen channel has no event"); | 
 | 665 |             return; | 
 | 666 |         } | 
 | 667 |  | 
 | 668 |         sample = mNextEvent.sample(); | 
 | 669 |         leftVolume = mNextEvent.leftVolume(); | 
 | 670 |         rightVolume = mNextEvent.rightVolume(); | 
 | 671 |         priority = mNextEvent.priority(); | 
 | 672 |         loop = mNextEvent.loop(); | 
 | 673 |         rate = mNextEvent.rate(); | 
 | 674 |     } | 
 | 675 |  | 
 | 676 |     ALOGV("Starting stolen channel %d -> %d", channelID(), nextChannelID); | 
 | 677 |     play(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate); | 
 | 678 | } | 
 | 679 |  | 
 | 680 | void SoundChannel::callback(int event, void* user, void *info) | 
 | 681 | { | 
 | 682 |     SoundChannel* channel = static_cast<SoundChannel*>((void *)((unsigned long)user & ~1)); | 
 | 683 |  | 
 | 684 |     channel->process(event, info, (unsigned long)user & 1); | 
 | 685 | } | 
 | 686 |  | 
 | 687 | void SoundChannel::process(int event, void *info, unsigned long toggle) | 
 | 688 | { | 
 | 689 |     //ALOGV("process(%d)", mChannelID); | 
 | 690 |  | 
 | 691 |     Mutex::Autolock lock(&mLock); | 
 | 692 |  | 
 | 693 |     AudioTrack::Buffer* b = NULL; | 
 | 694 |     if (event == AudioTrack::EVENT_MORE_DATA) { | 
 | 695 |        b = static_cast<AudioTrack::Buffer *>(info); | 
 | 696 |     } | 
 | 697 |  | 
 | 698 |     if (mToggle != toggle) { | 
 | 699 |         ALOGV("process wrong toggle %p channel %d", this, mChannelID); | 
 | 700 |         if (b != NULL) { | 
 | 701 |             b->size = 0; | 
 | 702 |         } | 
 | 703 |         return; | 
 | 704 |     } | 
 | 705 |  | 
 | 706 |     sp<Sample> sample = mSample; | 
 | 707 |  | 
 | 708 | //    ALOGV("SoundChannel::process event %d", event); | 
 | 709 |  | 
 | 710 |     if (event == AudioTrack::EVENT_MORE_DATA) { | 
 | 711 |  | 
 | 712 |         // check for stop state | 
 | 713 |         if (b->size == 0) return; | 
 | 714 |  | 
 | 715 |         if (mState == IDLE) { | 
 | 716 |             b->size = 0; | 
 | 717 |             return; | 
 | 718 |         } | 
 | 719 |  | 
 | 720 |         if (sample != 0) { | 
 | 721 |             // fill buffer | 
 | 722 |             uint8_t* q = (uint8_t*) b->i8; | 
 | 723 |             size_t count = 0; | 
 | 724 |  | 
 | 725 |             if (mPos < (int)sample->size()) { | 
 | 726 |                 uint8_t* p = sample->data() + mPos; | 
 | 727 |                 count = sample->size() - mPos; | 
 | 728 |                 if (count > b->size) { | 
 | 729 |                     count = b->size; | 
 | 730 |                 } | 
 | 731 |                 memcpy(q, p, count); | 
 | 732 | //              ALOGV("fill: q=%p, p=%p, mPos=%u, b->size=%u, count=%d", q, p, mPos, b->size, count); | 
 | 733 |             } else if (mPos < mAudioBufferSize) { | 
 | 734 |                 count = mAudioBufferSize - mPos; | 
 | 735 |                 if (count > b->size) { | 
 | 736 |                     count = b->size; | 
 | 737 |                 } | 
 | 738 |                 memset(q, 0, count); | 
 | 739 | //              ALOGV("fill extra: q=%p, mPos=%u, b->size=%u, count=%d", q, mPos, b->size, count); | 
 | 740 |             } | 
 | 741 |  | 
 | 742 |             mPos += count; | 
 | 743 |             b->size = count; | 
 | 744 |             //ALOGV("buffer=%p, [0]=%d", b->i16, b->i16[0]); | 
 | 745 |         } | 
| Eric Laurent | b3cb72a | 2013-10-12 17:05:19 -0700 | [diff] [blame] | 746 |     } else if (event == AudioTrack::EVENT_UNDERRUN || event == AudioTrack::EVENT_BUFFER_END || | 
 | 747 |             event == AudioTrack::EVENT_NEW_IAUDIOTRACK) { | 
 | 748 |         ALOGV("process %p channel %d event %s", | 
 | 749 |               this, mChannelID, (event == AudioTrack::EVENT_UNDERRUN) ? "UNDERRUN" : | 
 | 750 |                       (event == AudioTrack::EVENT_BUFFER_END) ? "BUFFER_END" : "NEW_IAUDIOTRACK"); | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 751 |         mSoundPool->addToStopList(this); | 
 | 752 |     } else if (event == AudioTrack::EVENT_LOOP_END) { | 
| Eric Laurent | 3d00aa6 | 2013-09-24 09:53:27 -0700 | [diff] [blame] | 753 |         ALOGV("End loop %p channel %d", this, mChannelID); | 
| Eric Laurent | b3cb72a | 2013-10-12 17:05:19 -0700 | [diff] [blame] | 754 |     } else { | 
 | 755 |         ALOGW("SoundChannel::process unexpected event %d", event); | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 756 |     } | 
 | 757 | } | 
 | 758 |  | 
 | 759 |  | 
 | 760 | // call with lock held | 
 | 761 | bool SoundChannel::doStop_l() | 
 | 762 | { | 
 | 763 |     if (mState != IDLE) { | 
 | 764 |         setVolume_l(0, 0); | 
 | 765 |         ALOGV("stop"); | 
 | 766 |         mAudioTrack->stop(); | 
 | 767 |         mSample.clear(); | 
 | 768 |         mState = IDLE; | 
 | 769 |         mPriority = IDLE_PRIORITY; | 
 | 770 |         return true; | 
 | 771 |     } | 
 | 772 |     return false; | 
 | 773 | } | 
 | 774 |  | 
 | 775 | // call with lock held and sound pool lock held | 
 | 776 | void SoundChannel::stop_l() | 
 | 777 | { | 
 | 778 |     if (doStop_l()) { | 
 | 779 |         mSoundPool->done_l(this); | 
 | 780 |     } | 
 | 781 | } | 
 | 782 |  | 
 | 783 | // call with sound pool lock held | 
 | 784 | void SoundChannel::stop() | 
 | 785 | { | 
 | 786 |     bool stopped; | 
 | 787 |     { | 
 | 788 |         Mutex::Autolock lock(&mLock); | 
 | 789 |         stopped = doStop_l(); | 
 | 790 |     } | 
 | 791 |  | 
 | 792 |     if (stopped) { | 
 | 793 |         mSoundPool->done_l(this); | 
 | 794 |     } | 
 | 795 | } | 
 | 796 |  | 
 | 797 | //FIXME: Pause is a little broken right now | 
 | 798 | void SoundChannel::pause() | 
 | 799 | { | 
 | 800 |     Mutex::Autolock lock(&mLock); | 
 | 801 |     if (mState == PLAYING) { | 
 | 802 |         ALOGV("pause track"); | 
 | 803 |         mState = PAUSED; | 
 | 804 |         mAudioTrack->pause(); | 
 | 805 |     } | 
 | 806 | } | 
 | 807 |  | 
 | 808 | void SoundChannel::autoPause() | 
 | 809 | { | 
 | 810 |     Mutex::Autolock lock(&mLock); | 
 | 811 |     if (mState == PLAYING) { | 
 | 812 |         ALOGV("pause track"); | 
 | 813 |         mState = PAUSED; | 
 | 814 |         mAutoPaused = true; | 
 | 815 |         mAudioTrack->pause(); | 
 | 816 |     } | 
 | 817 | } | 
 | 818 |  | 
 | 819 | void SoundChannel::resume() | 
 | 820 | { | 
 | 821 |     Mutex::Autolock lock(&mLock); | 
 | 822 |     if (mState == PAUSED) { | 
 | 823 |         ALOGV("resume track"); | 
 | 824 |         mState = PLAYING; | 
 | 825 |         mAutoPaused = false; | 
 | 826 |         mAudioTrack->start(); | 
 | 827 |     } | 
 | 828 | } | 
 | 829 |  | 
 | 830 | void SoundChannel::autoResume() | 
 | 831 | { | 
 | 832 |     Mutex::Autolock lock(&mLock); | 
 | 833 |     if (mAutoPaused && (mState == PAUSED)) { | 
 | 834 |         ALOGV("resume track"); | 
 | 835 |         mState = PLAYING; | 
 | 836 |         mAutoPaused = false; | 
 | 837 |         mAudioTrack->start(); | 
 | 838 |     } | 
 | 839 | } | 
 | 840 |  | 
 | 841 | void SoundChannel::setRate(float rate) | 
 | 842 | { | 
 | 843 |     Mutex::Autolock lock(&mLock); | 
 | 844 |     if (mAudioTrack != NULL && mSample != 0) { | 
 | 845 |         uint32_t sampleRate = uint32_t(float(mSample->sampleRate()) * rate + 0.5); | 
 | 846 |         mAudioTrack->setSampleRate(sampleRate); | 
 | 847 |         mRate = rate; | 
 | 848 |     } | 
 | 849 | } | 
 | 850 |  | 
 | 851 | // call with lock held | 
 | 852 | void SoundChannel::setVolume_l(float leftVolume, float rightVolume) | 
 | 853 | { | 
 | 854 |     mLeftVolume = leftVolume; | 
 | 855 |     mRightVolume = rightVolume; | 
 | 856 |     if (mAudioTrack != NULL) | 
 | 857 |         mAudioTrack->setVolume(leftVolume, rightVolume); | 
 | 858 | } | 
 | 859 |  | 
 | 860 | void SoundChannel::setVolume(float leftVolume, float rightVolume) | 
 | 861 | { | 
 | 862 |     Mutex::Autolock lock(&mLock); | 
 | 863 |     setVolume_l(leftVolume, rightVolume); | 
 | 864 | } | 
 | 865 |  | 
 | 866 | void SoundChannel::setLoop(int loop) | 
 | 867 | { | 
 | 868 |     Mutex::Autolock lock(&mLock); | 
 | 869 |     if (mAudioTrack != NULL && mSample != 0) { | 
 | 870 |         uint32_t loopEnd = mSample->size()/mNumChannels/ | 
 | 871 |             ((mSample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t)); | 
 | 872 |         mAudioTrack->setLoop(0, loopEnd, loop); | 
 | 873 |         mLoop = loop; | 
 | 874 |     } | 
 | 875 | } | 
 | 876 |  | 
 | 877 | SoundChannel::~SoundChannel() | 
 | 878 | { | 
 | 879 |     ALOGV("SoundChannel destructor %p", this); | 
 | 880 |     { | 
 | 881 |         Mutex::Autolock lock(&mLock); | 
 | 882 |         clearNextEvent(); | 
 | 883 |         doStop_l(); | 
 | 884 |     } | 
 | 885 |     // do not call AudioTrack destructor with mLock held as it will wait for the AudioTrack | 
 | 886 |     // callback thread to exit which may need to execute process() and acquire the mLock. | 
| Glenn Kasten | 2799d74 | 2013-05-30 14:33:29 -0700 | [diff] [blame] | 887 |     mAudioTrack.clear(); | 
| Eric Laurent | 2e66a78 | 2012-03-26 10:47:22 -0700 | [diff] [blame] | 888 | } | 
 | 889 |  | 
 | 890 | void SoundChannel::dump() | 
 | 891 | { | 
 | 892 |     ALOGV("mState = %d mChannelID=%d, mNumChannels=%d, mPos = %d, mPriority=%d, mLoop=%d", | 
 | 893 |             mState, mChannelID, mNumChannels, mPos, mPriority, mLoop); | 
 | 894 | } | 
 | 895 |  | 
 | 896 | void SoundEvent::set(const sp<Sample>& sample, int channelID, float leftVolume, | 
 | 897 |             float rightVolume, int priority, int loop, float rate) | 
 | 898 | { | 
 | 899 |     mSample = sample; | 
 | 900 |     mChannelID = channelID; | 
 | 901 |     mLeftVolume = leftVolume; | 
 | 902 |     mRightVolume = rightVolume; | 
 | 903 |     mPriority = priority; | 
 | 904 |     mLoop = loop; | 
 | 905 |     mRate =rate; | 
 | 906 | } | 
 | 907 |  | 
 | 908 | } // end namespace android |