1 /* 2 * Copyright (C) 2019 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 #pragma once 18 19 #include <android-base/unique_fd.h> 20 #include <binder/MemoryBase.h> 21 #include <binder/MemoryHeapBase.h> 22 #include <system/audio.h> 23 24 namespace android::soundpool { 25 26 class SoundDecoder; 27 28 /** 29 * Sound is a resource used by SoundPool, referenced by soundID. 30 * 31 * After loading, it is effectively const so no locking required. 32 * However, in order to guarantee that all the values have been 33 * written properly and read properly, we use the mState as an atomic synchronization 34 * point. So if getState() shows READY, then all the other getters may 35 * be safely read. 36 * 37 * Technical details: 38 * We access the mState atomic value through memory_order_seq_cst 39 * 40 * https://en.cppreference.com/w/cpp/atomic/memory_order 41 * 42 * which provides memory barriers. So if the last value written by the SoundDecoder 43 * is mState, then the compiler ensures no other prior writes by SoundDecoder will be 44 * reordered afterwards, and memory barrier is placed (as necessary) to ensure the 45 * cache is visible to other processors. 46 * 47 * Likewise, if the first value read by SoundPool is mState, 48 * the compiler ensures no reads for that thread will be reordered before mState is read, 49 * and a memory barrier is placed (as necessary) to ensure that the cache is properly 50 * updated with other processor's writes before reading. 51 * 52 * See https://developer.android.com/training/articles/smp for discussions about 53 * the variant load-acquire, store-release semantics. 54 */ 55 class Sound { 56 friend SoundDecoder; // calls doLoad(). 57 58 public: 59 enum sound_state : int32_t { LOADING, READY, DECODE_ERROR }; 60 // A sound starts in the LOADING state and transitions only once 61 // to either READY or DECODE_ERROR when doLoad() is called. 62 63 Sound(int soundID, int fd, int64_t offset, int64_t length); 64 ~Sound(); 65 getSoundID()66 int32_t getSoundID() const { return mSoundID; } getChannelCount()67 int32_t getChannelCount() const { return mChannelCount; } getSampleRate()68 uint32_t getSampleRate() const { return mSampleRate; } getFormat()69 audio_format_t getFormat() const { return mFormat; } getChannelMask()70 audio_channel_mask_t getChannelMask() const { return mChannelMask; } getSizeInBytes()71 size_t getSizeInBytes() const { return mSizeInBytes; } getState()72 sound_state getState() const { return mState; } getData()73 uint8_t* getData() const { return static_cast<uint8_t*>(mData->unsecurePointer()); } getIMemory()74 sp<IMemory> getIMemory() const { return mData; } 75 76 private: 77 status_t doLoad(); // only SoundDecoder accesses this. 78 79 size_t mSizeInBytes = 0; 80 const int32_t mSoundID; 81 uint32_t mSampleRate = 0; 82 std::atomic<sound_state> mState = LOADING; // used as synchronization point 83 int32_t mChannelCount = 0; 84 audio_format_t mFormat = AUDIO_FORMAT_INVALID; 85 audio_channel_mask_t mChannelMask = AUDIO_CHANNEL_NONE; 86 base::unique_fd mFd; // initialized in constructor, reset to -1 after loading 87 const int64_t mOffset; // int64_t to match java long, see off64_t 88 const int64_t mLength; // int64_t to match java long, see off64_t 89 sp<IMemory> mData; 90 sp<MemoryHeapBase> mHeap; 91 }; 92 93 } // namespace android::soundpool 94