• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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