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