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