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