Andy Hung e7937b9882 SoundPool: Refactor class
Make class names fit the Java API and documentation.

Rename Channel -> Stream.
Rename Sample -> Sound.
Rename SoundPoolThread -> SoundDecoder.

Move track start and stop to worker thread.

Fix up types to ensure future compatibility with Java ints
which are 32 bits always. Upgrade 16 bit types to
32 bits (e.g. sample id, sample rate, etc.)

Move sound related code into the SoundManager class.
Move stream related code into the StreamManager class.

Clean up locking, split SoundPool lock into the following locks
1) mApiLock
2) mStreamManagerLock
3) mSoundManagerLock
4) per Stream mLock.
5) mCallbackLock

Group locked data and make private in associated classes to ensure
restricted access, yet maximum concurrency.

Fix race conditions waiting for next event to play in stream.

Ensure track commands are handled consistently when stream is stolen.

Test: SoundPoolOggTest
Bug: 140807581
Change-Id: I8fcb374ee6329eb5474b973624584ca5080da862
2019-10-22 16:26:58 -07:00

94 lines
3.7 KiB
C++

/*
* Copyright (C) 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.
*/
#pragma once
#include <android-base/unique_fd.h>
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
#include <system/audio.h>
namespace android::soundpool {
class SoundDecoder;
/**
* Sound is a resource used by SoundPool, referenced by soundID.
*
* After loading, it is effectively const so no locking required.
* However, in order to guarantee that all the values have been
* written properly and read properly, we use the mState as an atomic synchronization
* point. So if getState() shows READY, then all the other getters may
* be safely read.
*
* Technical details:
* We access the mState atomic value through memory_order_seq_cst
*
* https://en.cppreference.com/w/cpp/atomic/memory_order
*
* which provides memory barriers. So if the last value written by the SoundDecoder
* is mState, then the compiler ensures no other prior writes by SoundDecoder will be
* reordered afterwards, and memory barrier is placed (as necessary) to ensure the
* cache is visible to other processors.
*
* Likewise, if the first value read by SoundPool is mState,
* the compiler ensures no reads for that thread will be reordered before mState is read,
* and a memory barrier is placed (as necessary) to ensure that the cache is properly
* updated with other processor's writes before reading.
*
* See https://developer.android.com/training/articles/smp for discussions about
* the variant load-acquire, store-release semantics.
*/
class Sound {
friend SoundDecoder; // calls doLoad().
public:
enum sound_state : int32_t { LOADING, READY, DECODE_ERROR };
// A sound starts in the LOADING state and transitions only once
// to either READY or DECODE_ERROR when doLoad() is called.
Sound(int soundID, int fd, int64_t offset, int64_t length);
~Sound();
int32_t getSoundID() const { return mSoundID; }
int32_t getChannelCount() const { return mChannelCount; }
uint32_t getSampleRate() const { return mSampleRate; }
audio_format_t getFormat() const { return mFormat; }
audio_channel_mask_t getChannelMask() const { return mChannelMask; }
size_t getSizeInBytes() const { return mSizeInBytes; }
sound_state getState() const { return mState; }
uint8_t* getData() const { return static_cast<uint8_t*>(mData->unsecurePointer()); }
sp<IMemory> getIMemory() const { return mData; }
private:
status_t doLoad(); // only SoundDecoder accesses this.
size_t mSizeInBytes = 0;
const int32_t mSoundID;
uint32_t mSampleRate = 0;
std::atomic<sound_state> mState = LOADING; // used as synchronization point
int32_t mChannelCount = 0;
audio_format_t mFormat = AUDIO_FORMAT_INVALID;
audio_channel_mask_t mChannelMask = AUDIO_CHANNEL_NONE;
base::unique_fd mFd; // initialized in constructor, reset to -1 after loading
const int64_t mOffset; // int64_t to match java long, see off64_t
const int64_t mLength; // int64_t to match java long, see off64_t
sp<IMemory> mData;
sp<MemoryHeapBase> mHeap;
};
} // namespace android::soundpool