blob: 9ef62c921f58f2dd8ca45d6fd4c261491883044a [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>
21#include <linux/futex.h>
Eric Laurent629afae2017-05-25 18:25:51 -070022#include <sched.h>
Phil Burk7a61a3a2017-07-10 11:53:09 -070023#include <sys/syscall.h>
24#include <unistd.h>
25
Eric Laurent629afae2017-05-25 18:25:51 -070026#include <aaudio/AAudio.h>
Phil Burk7a61a3a2017-07-10 11:53:09 -070027#include <utils/Errors.h>
Eric Laurent629afae2017-05-25 18:25:51 -070028
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
Phil Burk97350f92017-07-21 15:59:44 -070033const char *getSharingModeText(aaudio_sharing_mode_t mode) {
Eric Laurent629afae2017-05-25 18:25:51 -070034 const char *modeText = "unknown";
35 switch (mode) {
36 case AAUDIO_SHARING_MODE_EXCLUSIVE:
37 modeText = "EXCLUSIVE";
38 break;
39 case AAUDIO_SHARING_MODE_SHARED:
40 modeText = "SHARED";
41 break;
42 default:
43 break;
44 }
45 return modeText;
46}
47
Phil Burk7a61a3a2017-07-10 11:53:09 -070048static void convertNanosecondsToTimespec(int64_t nanoseconds, struct timespec *time) {
49 time->tv_sec = nanoseconds / NANOS_PER_SECOND;
50 // Calculate the fractional nanoseconds. Avoids expensive % operation.
51 time->tv_nsec = nanoseconds - (time->tv_sec * NANOS_PER_SECOND);
52}
53
Eric Laurent629afae2017-05-25 18:25:51 -070054static int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) {
55 struct timespec time;
56 int result = clock_gettime(clockId, &time);
57 if (result < 0) {
58 return -errno;
59 }
60 return (time.tv_sec * NANOS_PER_SECOND) + time.tv_nsec;
61}
62
Phil Burk97350f92017-07-21 15:59:44 -070063static void displayPeakLevel(float peakLevel) {
Eric Laurent629afae2017-05-25 18:25:51 -070064 printf("%5.3f ", peakLevel);
65 const int maxStars = 50; // arbitrary, fits on one line
66 int numStars = (int) (peakLevel * maxStars);
67 for (int i = 0; i < numStars; i++) {
68 printf("*");
69 }
70 printf("\n");
71}
72
Phil Burk97350f92017-07-21 15:59:44 -070073/**
74 * @param position1 position of hardware frame
75 * @param nanoseconds1
76 * @param position2 position of client read/write
77 * @param nanoseconds2
78 * @param sampleRate
79 * @return latency in milliseconds
80 */
81static double calculateLatencyMillis(int64_t position1, int64_t nanoseconds1,
82 int64_t position2, int64_t nanoseconds2,
83 int64_t sampleRate) {
84 int64_t deltaFrames = position2 - position1;
85 int64_t deltaTime =
86 (NANOS_PER_SECOND * deltaFrames / sampleRate);
87 int64_t timeCurrentFramePlayed = nanoseconds1 + deltaTime;
88 int64_t latencyNanos = timeCurrentFramePlayed - nanoseconds2;
89 double latencyMillis = latencyNanos / 1000000.0;
90 return latencyMillis;
91}
92
Phil Burk7a61a3a2017-07-10 11:53:09 -070093// ================================================================================
94// These Futex calls are common online examples.
95static android::status_t sys_futex(void *addr1, int op, int val1,
96 struct timespec *timeout, void *addr2, int val3) {
97 android::status_t result = (android::status_t) syscall(SYS_futex, addr1,
98 op, val1, timeout,
99 addr2, val3);
100 return (result == 0) ? 0 : -errno;
101}
102
103static android::status_t futex_wake(void *addr, int numWake) {
104 // Use _PRIVATE because we are just using the futex in one process.
105 return sys_futex(addr, FUTEX_WAKE_PRIVATE, numWake, NULL, NULL, 0);
106}
107
108static android::status_t futex_wait(void *addr, int current, struct timespec *time) {
109 // Use _PRIVATE because we are just using the futex in one process.
110 return sys_futex(addr, FUTEX_WAIT_PRIVATE, current, time, NULL, 0);
111}
112
113// TODO better name?
114/**
115 * The WakeUp class is used to send a wakeup signal to one or more sleeping threads.
116 */
117class WakeUp {
118public:
119 WakeUp() : mValue(0) {}
120 explicit WakeUp(int32_t value) : mValue(value) {}
121
122 /**
123 * Wait until the internal value no longer matches the given value.
124 * Note that this code uses a futex, which is subject to spurious wake-ups.
125 * So check to make sure that the desired condition has been met.
126 *
127 * @return zero if the value changes or various negative errors including
128 * -ETIMEDOUT if a timeout occurs,
129 * or -EINTR if interrupted by a signal,
130 * or -EAGAIN or -EWOULDBLOCK if the internal value does not match the specified value
131 */
132 android::status_t wait(int32_t value, int64_t timeoutNanoseconds) {
133 struct timespec time;
134 convertNanosecondsToTimespec(timeoutNanoseconds, &time);
135 return futex_wait(&mValue, value, &time);
136 }
137
138 /**
139 * Increment value and wake up any threads that need to be woken.
140 *
141 * @return number of waiters woken up
142 */
143 android::status_t wake() {
144 ++mValue;
145 return futex_wake(&mValue, INT_MAX);
146 }
147
148 /**
149 * Set value and wake up any threads that need to be woken.
150 *
151 * @return number of waiters woken up
152 */
153 android::status_t wake(int32_t value) {
154 mValue.store(value);
155 return futex_wake(&mValue, INT_MAX);
156 }
157
158 int32_t get() {
159 return mValue.load();
160 }
161
162private:
163 std::atomic<int32_t> mValue;
164};
165
Eric Laurent629afae2017-05-25 18:25:51 -0700166#endif // AAUDIO_EXAMPLE_UTILS_H