blob: 5819dfd2272318e246ac9d264c2742415c497c17 [file] [log] [blame]
Eric Laurent629afae2017-05-25 18:25:51 -07001/*
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
Phil Burk7a61a3a2017-07-10 11:53:09 -070020#include <atomic>
Dan Albertcb2e7fb2017-10-11 12:43:41 -070021#include <errno.h>
Phil Burk7a61a3a2017-07-10 11:53:09 -070022#include <linux/futex.h>
Eric Laurent629afae2017-05-25 18:25:51 -070023#include <sched.h>
Dan Albertcb2e7fb2017-10-11 12:43:41 -070024#include <string.h>
Phil Burk7a61a3a2017-07-10 11:53:09 -070025#include <sys/syscall.h>
26#include <unistd.h>
27
Eric Laurent629afae2017-05-25 18:25:51 -070028#include <aaudio/AAudio.h>
Phil Burk7a61a3a2017-07-10 11:53:09 -070029#include <utils/Errors.h>
Eric Laurent629afae2017-05-25 18:25:51 -070030
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
Phil Burkb6586302020-11-12 20:18:00 +000035// Use template functions to avoid warning of unused static functions.
Chih-Hung Hsiehabf85fc2017-11-09 10:18:04 -080036template <class T = aaudio_sharing_mode_t>
Phil Burk97350f92017-07-21 15:59:44 -070037const char *getSharingModeText(aaudio_sharing_mode_t mode) {
Phil Burkcda5c072017-08-31 17:23:18 -070038 const char *text = "unknown";
Eric Laurent629afae2017-05-25 18:25:51 -070039 switch (mode) {
Phil Burkcda5c072017-08-31 17:23:18 -070040 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;
Eric Laurent629afae2017-05-25 18:25:51 -070048 }
Phil Burkcda5c072017-08-31 17:23:18 -070049 return text;
50}
51
Phil Burkb6586302020-11-12 20:18:00 +000052template <class T = aaudio_performance_mode_t>
Phil Burkcda5c072017-08-31 17:23:18 -070053const 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
Phil Burkb6586302020-11-12 20:18:00 +000071template <class T = aaudio_direction_t>
Phil Burkcda5c072017-08-31 17:23:18 -070072const 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;
Eric Laurent629afae2017-05-25 18:25:51 -070085}
86
Phil Burkb6586302020-11-12 20:18:00 +000087template <class T = aaudio_direction_t>
88constexpr 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
104inline 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
Chih-Hung Hsieh9c59bc92017-11-09 13:52:18 -0800110template <class T = int64_t>
111void convertNanosecondsToTimespec(int64_t nanoseconds, struct timespec *time) {
Phil Burk7a61a3a2017-07-10 11:53:09 -0700112 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
Chih-Hung Hsiehabf85fc2017-11-09 10:18:04 -0800117template <class T = clockid_t>
118int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) {
Eric Laurent629afae2017-05-25 18:25:51 -0700119 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
Chih-Hung Hsieh9c59bc92017-11-09 13:52:18 -0800127template <class T = float>
Eric Laurent629afae2017-05-25 18:25:51 -0700128void 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
Phil Burk97350f92017-07-21 15:59:44 -0700138/**
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 */
Chih-Hung Hsieh9c59bc92017-11-09 13:52:18 -0800146template <class T = int64_t>
147double calculateLatencyMillis(int64_t position1, int64_t nanoseconds1,
Phil Burk97350f92017-07-21 15:59:44 -0700148 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
Phil Burk7a61a3a2017-07-10 11:53:09 -0700159// ================================================================================
160// These Futex calls are common online examples.
Chih-Hung Hsieh9c59bc92017-11-09 13:52:18 -0800161template <class T = int>
162android::status_t sys_futex(void *addr1, int op, int val1,
Phil Burk7a61a3a2017-07-10 11:53:09 -0700163 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
Chih-Hung Hsieh9c59bc92017-11-09 13:52:18 -0800170template <class T = int>
171android::status_t futex_wake(void *addr, int numWake) {
Phil Burk7a61a3a2017-07-10 11:53:09 -0700172 // 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
Chih-Hung Hsieh9c59bc92017-11-09 13:52:18 -0800176template <class T = int>
177android::status_t futex_wait(void *addr, int current, struct timespec *time) {
Phil Burk7a61a3a2017-07-10 11:53:09 -0700178 // 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 */
186class WakeUp {
187public:
188 WakeUp() : mValue(0) {}
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 */
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 */
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 */
222 android::status_t wake(int32_t value) {
223 mValue.store(value);
224 return futex_wake(&mValue, INT_MAX);
225 }
226
227 int32_t get() {
228 return mValue.load();
229 }
230
231private:
232 std::atomic<int32_t> mValue;
233};
234
Eric Laurent629afae2017-05-25 18:25:51 -0700235#endif // AAUDIO_EXAMPLE_UTILS_H