1 /*
2 * Copyright (C) 2024 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 // PCM offload
18
19 #include <memory>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <vector>
23
24 #include <aaudio/AAudio.h>
25
26 #include "AAudioArgsParser.h"
27 #include "AAudioSimplePlayer.h"
28 #include "SineGenerator.h"
29
30 const static int DEFAULT_TIME_TO_RUN_IN_SECOND = 5;
31
32 aaudio_data_callback_result_t MyDatacallback(AAudioStream* stream,
33 void* userData,
34 void* audioData,
35 int32_t numFrames);
36
37 void MyErrorCallback(AAudioStream* /*stream*/, void* /*userData*/, aaudio_result_t error);
38
39 void MyPresentationEndCallback(AAudioStream* /*stream*/, void* userData);
40
41 class OffloadPlayer : public AAudioSimplePlayer {
42 public:
OffloadPlayer(AAudioArgsParser & argParser,int delay,int padding,int streamFrames,bool useDataCallback)43 OffloadPlayer(AAudioArgsParser& argParser, int delay, int padding, int streamFrames,
44 bool useDataCallback)
45 : mArgParser(argParser), mDelay(delay), mPadding(padding), mStreamFrames(streamFrames),
46 mUseDataCallback(useDataCallback) {
47 }
48
open()49 aaudio_result_t open() {
50 aaudio_result_t result = AAudioSimplePlayer::open(
51 mArgParser,
52 mUseDataCallback ? &MyDatacallback : nullptr,
53 &MyErrorCallback,
54 this,
55 &MyPresentationEndCallback);
56 if (result != AAUDIO_OK) {
57 return result;
58 }
59 mChannelCount = getChannelCount();
60 for (int i = 0; i < mChannelCount; ++i) {
61 SineGenerator sine;
62 sine.setup(440.0, 48000.0);
63 mSines.push_back(sine);
64 }
65 return result;
66 }
67
renderAudio(AAudioStream * stream,void * audioData,int32_t numFrames)68 aaudio_data_callback_result_t renderAudio(AAudioStream* stream,
69 void* audioData,
70 int32_t numFrames) {
71 // Just handle PCM_16 and PCM_FLOAT for testing
72 if (!fillData(stream, audioData, numFrames)) {
73 return AAUDIO_CALLBACK_RESULT_STOP;
74 }
75 mFramesWritten += numFrames;
76 if (mStreamFrames > 0 && mFramesWritten >= mStreamFrames) {
77 if (auto result = setOffloadEndOfStream(); result != AAUDIO_OK) {
78 printf("Failed to set offload end of stream, stopping the stream now");
79 return AAUDIO_CALLBACK_RESULT_STOP;
80 }
81 (void) setOffloadDelayPadding(mDelay, mPadding);
82 mFramesWritten = 0;
83 }
84 return AAUDIO_CALLBACK_RESULT_CONTINUE;
85 }
86
presentationEnd(AAudioStream * stream)87 void presentationEnd(AAudioStream* stream) {
88 printf("Presentation end\n");
89 if (!mUseDataCallback) {
90 writeAllStreamData(stream);
91 }
92 }
93
writeData()94 void writeData() {
95 writeAllStreamData(getStream());
96 }
97
98 private:
writeAllStreamData(AAudioStream * stream)99 void writeAllStreamData(AAudioStream* stream) {
100 int bytesPerFrame = mChannelCount;
101 std::shared_ptr<uint8_t[]> data;
102 switch (AAudioStream_getFormat(stream)) {
103 case AAUDIO_FORMAT_PCM_I16: {
104 bytesPerFrame *= 2;
105 } break;
106 case AAUDIO_FORMAT_PCM_FLOAT: {
107 bytesPerFrame *= 4;
108 } break;
109 default:
110 printf("Unsupported format %d\n", AAudioStream_getFormat(stream));
111 return;
112 }
113 data = std::make_shared<uint8_t[]>(bytesPerFrame * mStreamFrames);
114 fillData(stream, static_cast<void*>(data.get()), mStreamFrames);
115 int bytesWritten = 0;
116 int framesLeft = mStreamFrames;
117 while (framesLeft > 0) {
118 auto framesWritten = AAudioStream_write(
119 stream, static_cast<void *>(&data[bytesWritten]),
120 framesLeft, NANOS_PER_SECOND);
121 if (framesWritten < 0) {
122 printf("Failed to write data %d\n", framesWritten);
123 return;
124 }
125 printf("Write data succeed, frames=%d\n", framesWritten);
126 framesLeft -= framesWritten;
127 bytesWritten += framesWritten * bytesPerFrame;
128 }
129 if (auto result = setOffloadEndOfStream(); result != AAUDIO_OK) {
130 printf("Failed to set offload end of stream, result=%d\n", result);
131 }
132 }
133
fillData(AAudioStream * stream,void * data,int numFrames)134 bool fillData(AAudioStream* stream, void* data, int numFrames) {
135 switch (AAudioStream_getFormat(stream)) {
136 case AAUDIO_FORMAT_PCM_I16: {
137 int16_t *audioBuffer = static_cast<int16_t *>(data);
138 for (int i = 0; i < mChannelCount; ++i) {
139 mSines[i].render(&audioBuffer[i], mChannelCount, numFrames);
140 }
141 } break;
142 case AAUDIO_FORMAT_PCM_FLOAT: {
143 float *audioBuffer = static_cast<float *>(data);
144 for (int i = 0; i < mChannelCount; ++i) {
145 mSines[i].render(&audioBuffer[i], mChannelCount, numFrames);
146 }
147 } break;
148 default:
149 return false;
150 }
151 return true;
152 }
153
154 const AAudioArgsParser mArgParser;
155 const int mDelay;
156 const int mPadding;
157 const int mStreamFrames;
158 const bool mUseDataCallback;
159
160 int mChannelCount;
161 std::vector<SineGenerator> mSines;
162 int mFramesWritten = 0;
163 };
164
MyDatacallback(AAudioStream * stream,void * userData,void * audioData,int32_t numFrames)165 aaudio_data_callback_result_t MyDatacallback(AAudioStream* stream,
166 void* userData,
167 void* audioData,
168 int32_t numFrames) {
169 OffloadPlayer* player = static_cast<OffloadPlayer*>(userData);
170 return player->renderAudio(stream, audioData, numFrames);
171 }
172
MyErrorCallback(AAudioStream *,void *,aaudio_result_t error)173 void MyErrorCallback(AAudioStream* /*stream*/, void* /*userData*/, aaudio_result_t error) {
174 printf("Error callback, error=%d\n", error);
175 }
176
MyPresentationEndCallback(AAudioStream * stream,void * userData)177 void MyPresentationEndCallback(AAudioStream* stream, void* userData) {
178 OffloadPlayer* player = static_cast<OffloadPlayer*>(userData);
179 return player->presentationEnd(stream);
180 }
181
usage()182 static void usage() {
183 AAudioArgsParser::usage();
184 printf(" -D{delay} offload delay in frames\n");
185 printf(" -P{padding} offload padding in frames\n");
186 printf(" -E{frames} frames to notify end of stream\n");
187 printf(" -T{seconds} time to run the test\n");
188 printf(" -B use blocking write instead of data callback\n");
189 }
190
main(int argc,char ** argv)191 int main(int argc, char **argv) {
192 AAudioArgsParser argParser;
193 int delay = 0;
194 int padding = 0;
195 int streamFrames = 0;
196 int timeToRun = DEFAULT_TIME_TO_RUN_IN_SECOND;
197 bool useDataCallback = true;
198 for (int i = 1; i < argc; ++i) {
199 const char *arg = argv[i];
200 if (argParser.parseArg(arg)) {
201 if (arg[0] == '-') {
202 char option = arg[1];
203 switch (option) {
204 case 'D':
205 delay = atoi(&arg[2]);
206 break;
207 case 'P':
208 padding = atoi(&arg[2]);
209 break;
210 case 'E':
211 streamFrames = atoi(&arg[2]);
212 break;
213 case 'T':
214 timeToRun = atoi(&arg[2]);
215 break;
216 case 'B':
217 useDataCallback = false;
218 break;
219 default:
220 usage();
221 exit(EXIT_FAILURE);
222 }
223 } else {
224 usage();
225 exit(EXIT_FAILURE);
226 }
227 }
228 }
229
230 // Force to use offload mode
231 argParser.setPerformanceMode(AAUDIO_PERFORMANCE_MODE_POWER_SAVING_OFFLOADED);
232
233 OffloadPlayer player(argParser, delay, padding, streamFrames, useDataCallback);
234 if (auto result = player.open(); result != AAUDIO_OK) {
235 printf("Failed to open stream, error=%d\n", result);
236 exit(EXIT_FAILURE);
237 }
238
239 // Failed to set offload delay and padding will affect the gapless transition between tracks
240 // but doesn't affect playback.
241 (void) player.setOffloadDelayPadding(delay, padding);
242
243 if (auto result = player.start(); result != AAUDIO_OK) {
244 printf("Failed to start stream, error=%d", result);
245 exit(EXIT_FAILURE);
246 } else if (!useDataCallback) {
247 player.writeData();
248 }
249
250 sleep(timeToRun);
251
252 return EXIT_SUCCESS;
253 }
254