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