1 /* 2 * Copyright (C) 2017 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 #ifndef AAUDIO_EXAMPLE_UTILS_H 18 #define AAUDIO_EXAMPLE_UTILS_H 19 20 #include <atomic> 21 #include <errno.h> 22 #include <linux/futex.h> 23 #include <sched.h> 24 #include <string.h> 25 #include <sys/syscall.h> 26 #include <unistd.h> 27 28 #include <aaudio/AAudio.h> 29 #include <utils/Errors.h> 30 31 #define NANOS_PER_MICROSECOND ((int64_t)1000) 32 #define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000) 33 #define NANOS_PER_SECOND (NANOS_PER_MILLISECOND * 1000) 34 35 template <class T = aaudio_sharing_mode_t> 36 const char *getSharingModeText(aaudio_sharing_mode_t mode) { 37 const char *text = "unknown"; 38 switch (mode) { 39 case AAUDIO_SHARING_MODE_EXCLUSIVE: 40 text = "EXCLUSIVE"; 41 break; 42 case AAUDIO_SHARING_MODE_SHARED: 43 text = "SHARED"; 44 break; 45 default: 46 break; 47 } 48 return text; 49 } 50 51 const char *getPerformanceModeText(aaudio_performance_mode_t mode) { 52 const char *text = "unknown"; 53 switch (mode) { 54 case AAUDIO_PERFORMANCE_MODE_NONE: 55 text = "NONE"; 56 break; 57 case AAUDIO_PERFORMANCE_MODE_LOW_LATENCY: 58 text = "LOW_LATENCY"; 59 break; 60 case AAUDIO_PERFORMANCE_MODE_POWER_SAVING: 61 text = "POWER_SAVING"; 62 break; 63 default: 64 break; 65 } 66 return text; 67 } 68 69 const char *getDirectionText(aaudio_direction_t direction) { 70 const char *text = "unknown"; 71 switch (direction) { 72 case AAUDIO_DIRECTION_INPUT: 73 text = "INPUT"; 74 break; 75 case AAUDIO_DIRECTION_OUTPUT: 76 text = "OUTPUT"; 77 break; 78 default: 79 break; 80 } 81 return text; 82 } 83 84 template <class T = int64_t> 85 void convertNanosecondsToTimespec(int64_t nanoseconds, struct timespec *time) { 86 time->tv_sec = nanoseconds / NANOS_PER_SECOND; 87 // Calculate the fractional nanoseconds. Avoids expensive % operation. 88 time->tv_nsec = nanoseconds - (time->tv_sec * NANOS_PER_SECOND); 89 } 90 91 template <class T = clockid_t> 92 int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) { 93 struct timespec time; 94 int result = clock_gettime(clockId, &time); 95 if (result < 0) { 96 return -errno; 97 } 98 return (time.tv_sec * NANOS_PER_SECOND) + time.tv_nsec; 99 } 100 101 template <class T = float> 102 void displayPeakLevel(float peakLevel) { 103 printf("%5.3f ", peakLevel); 104 const int maxStars = 50; // arbitrary, fits on one line 105 int numStars = (int) (peakLevel * maxStars); 106 for (int i = 0; i < numStars; i++) { 107 printf("*"); 108 } 109 printf("\n"); 110 } 111 112 /** 113 * @param position1 position of hardware frame 114 * @param nanoseconds1 115 * @param position2 position of client read/write 116 * @param nanoseconds2 117 * @param sampleRate 118 * @return latency in milliseconds 119 */ 120 template <class T = int64_t> 121 double calculateLatencyMillis(int64_t position1, int64_t nanoseconds1, 122 int64_t position2, int64_t nanoseconds2, 123 int64_t sampleRate) { 124 int64_t deltaFrames = position2 - position1; 125 int64_t deltaTime = 126 (NANOS_PER_SECOND * deltaFrames / sampleRate); 127 int64_t timeCurrentFramePlayed = nanoseconds1 + deltaTime; 128 int64_t latencyNanos = timeCurrentFramePlayed - nanoseconds2; 129 double latencyMillis = latencyNanos / 1000000.0; 130 return latencyMillis; 131 } 132 133 // ================================================================================ 134 // These Futex calls are common online examples. 135 template <class T = int> 136 android::status_t sys_futex(void *addr1, int op, int val1, 137 struct timespec *timeout, void *addr2, int val3) { 138 android::status_t result = (android::status_t) syscall(SYS_futex, addr1, 139 op, val1, timeout, 140 addr2, val3); 141 return (result == 0) ? 0 : -errno; 142 } 143 144 template <class T = int> 145 android::status_t futex_wake(void *addr, int numWake) { 146 // Use _PRIVATE because we are just using the futex in one process. 147 return sys_futex(addr, FUTEX_WAKE_PRIVATE, numWake, NULL, NULL, 0); 148 } 149 150 template <class T = int> 151 android::status_t futex_wait(void *addr, int current, struct timespec *time) { 152 // Use _PRIVATE because we are just using the futex in one process. 153 return sys_futex(addr, FUTEX_WAIT_PRIVATE, current, time, NULL, 0); 154 } 155 156 // TODO better name? 157 /** 158 * The WakeUp class is used to send a wakeup signal to one or more sleeping threads. 159 */ 160 class WakeUp { 161 public: 162 WakeUp() : mValue(0) {} 163 explicit WakeUp(int32_t value) : mValue(value) {} 164 165 /** 166 * Wait until the internal value no longer matches the given value. 167 * Note that this code uses a futex, which is subject to spurious wake-ups. 168 * So check to make sure that the desired condition has been met. 169 * 170 * @return zero if the value changes or various negative errors including 171 * -ETIMEDOUT if a timeout occurs, 172 * or -EINTR if interrupted by a signal, 173 * or -EAGAIN or -EWOULDBLOCK if the internal value does not match the specified value 174 */ 175 android::status_t wait(int32_t value, int64_t timeoutNanoseconds) { 176 struct timespec time; 177 convertNanosecondsToTimespec(timeoutNanoseconds, &time); 178 return futex_wait(&mValue, value, &time); 179 } 180 181 /** 182 * Increment value and wake up any threads that need to be woken. 183 * 184 * @return number of waiters woken up 185 */ 186 android::status_t wake() { 187 ++mValue; 188 return futex_wake(&mValue, INT_MAX); 189 } 190 191 /** 192 * Set value and wake up any threads that need to be woken. 193 * 194 * @return number of waiters woken up 195 */ 196 android::status_t wake(int32_t value) { 197 mValue.store(value); 198 return futex_wake(&mValue, INT_MAX); 199 } 200 201 int32_t get() { 202 return mValue.load(); 203 } 204 205 private: 206 std::atomic<int32_t> mValue; 207 }; 208 209 #endif // AAUDIO_EXAMPLE_UTILS_H 210