1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include <cstdio>
16 #include <iostream>
17
18 #include "audio_service_client.h"
19 #include "audio_log.h"
20 #include "audio_system_manager.h"
21 #include "pcm2wav.h"
22
23
24 using namespace OHOS::AudioStandard;
25 using namespace std;
26
27 namespace {
28 constexpr uint8_t DEFAULT_FORMAT = SAMPLE_S16LE;
29 constexpr uint8_t CHANNEL_INDEX = 2;
30 constexpr uint8_t SLEEP_TIME = 2;
31 constexpr uint8_t MAX_FRAME_COUNT = 10;
32
33 constexpr int32_t SAMPLE_FORMAT_U8 = 8;
34 constexpr int32_t SAMPLE_FORMAT_S16LE = 16;
35 constexpr int32_t SAMPLE_FORMAT_S24LE = 24;
36 constexpr int32_t SAMPLE_FORMAT_S32LE = 32;
37 } // namespace
38
InitClient(std::unique_ptr<AudioServiceClient> & client,uint32_t samplingRate,uint32_t channelCount)39 static bool InitClient(std::unique_ptr<AudioServiceClient> &client, uint32_t samplingRate, uint32_t channelCount)
40 {
41 AudioStreamParams audioParams;
42 audioParams.format = DEFAULT_FORMAT;
43 audioParams.samplingRate = samplingRate;
44 audioParams.channels = channelCount;
45
46 if (client->Initialize(AUDIO_SERVICE_CLIENT_RECORD) < 0)
47 return false;
48
49 if (client->CreateStream(audioParams, STREAM_MUSIC) < 0) {
50 client->ReleaseStream();
51 return false;
52 }
53
54 if (client->StartStream() < 0)
55 return false;
56
57 return true;
58 }
59
StartRecording(char * recPath,int samplingRate,size_t frames,uint32_t channelCount)60 int32_t StartRecording(char *recPath, int samplingRate, size_t frames, uint32_t channelCount)
61 {
62 AUDIO_INFO_LOG("Recording::Path [%{public}s], rate [%{public}d], frames [%{public}zu], channel [%{public}d]",
63 recPath, samplingRate, frames, channelCount);
64
65 if (frames > MAX_FRAME_COUNT || frames <= 0) {
66 std::cout << "ERROR!!<buffer count> supported value is from 1-10" << std::endl;
67 return AUDIO_ERR;
68 }
69
70 std::unique_ptr client = std::make_unique<AudioServiceClient>();
71 if (!InitClient(client, samplingRate, channelCount)) {
72 AUDIO_ERR_LOG("Initialize client failed");
73 return AUDIO_ERR;
74 }
75
76 size_t bufferLen;
77 if (client->GetMinimumBufferSize(bufferLen) < 0) {
78 return AUDIO_ERR;
79 }
80
81 uint8_t* buffer = nullptr;
82 buffer = (uint8_t *) malloc(bufferLen);
83 if (buffer == nullptr) {
84 AUDIO_ERR_LOG("Failed to allocate buffer");
85 return AUDIO_ERR;
86 }
87
88 char realPath[PATH_MAX + 1] = {0x00};
89 std::string sourceFilePath(recPath);
90 std::string rootPath;
91 std::string fileName;
92
93 auto pos = sourceFilePath.rfind("/");
94 if (pos!= std::string::npos) {
95 rootPath = sourceFilePath.substr(0, pos);
96 fileName = sourceFilePath.substr(pos);
97 }
98
99 if ((strlen(sourceFilePath.c_str()) >= PATH_MAX) || (realpath(rootPath.c_str(), realPath) == nullptr)) {
100 free(buffer);
101 AUDIO_ERR_LOG("StartRecording:: Invalid path errno = %{public}d", errno);
102 return AUDIO_ERR;
103 }
104
105 std::string verifiedPath(realPath);
106 FILE *pFile = fopen(verifiedPath.append(fileName).c_str(), "wb");
107 if (pFile == nullptr) {
108 free(buffer);
109 AUDIO_ERR_LOG("StartRecording:: Failed to open file errno = %{public}d", errno);
110 return AUDIO_ERR;
111 }
112
113 size_t size = 1;
114 size_t numBuffersToCapture = frames * 1024;
115 StreamBuffer stream;
116 stream.buffer = buffer;
117 stream.bufferLen = bufferLen;
118 int32_t bytesRead = 0;
119
120 while (numBuffersToCapture) {
121 bytesRead = client->ReadStream(stream, false);
122 if (bytesRead < 0) {
123 break;
124 }
125
126 if (bytesRead > 0) {
127 fwrite(stream.buffer, size, bytesRead, pFile);
128 numBuffersToCapture--;
129 }
130 }
131
132 (void)fflush(pFile);
133 client->FlushStream();
134 client->StopStream();
135 client->ReleaseStream();
136 free(buffer);
137 (void)fclose(pFile);
138 sleep(SLEEP_TIME);
139 AUDIO_INFO_LOG("================RECORDING OVER==================");
140 return 0;
141 }
142
InitPlayback(std::unique_ptr<AudioServiceClient> & client,AudioStreamParams & audioParams)143 static int32_t InitPlayback(std::unique_ptr<AudioServiceClient> &client, AudioStreamParams &audioParams)
144 {
145 if (client == nullptr) {
146 AUDIO_ERR_LOG("Create AudioServiceClient instance failed");
147 return AUDIO_ERR;
148 }
149
150 AUDIO_INFO_LOG("Initializing of AudioServiceClient");
151 if (client->Initialize(AUDIO_SERVICE_CLIENT_PLAYBACK) < 0) {
152 AUDIO_ERR_LOG("Initializing of AudioServiceClien failed");
153 return AUDIO_ERR;
154 }
155
156 AUDIO_INFO_LOG("Creating Stream");
157 if (client->CreateStream(audioParams, STREAM_MUSIC) < 0) {
158 AUDIO_ERR_LOG("Creating Stream failed");
159 return AUDIO_ERR;
160 }
161
162 AUDIO_INFO_LOG("Starting Stream");
163 if (client->StartStream() < 0) {
164 AUDIO_ERR_LOG("Starting Stream failed");
165 return AUDIO_ERR;
166 }
167
168 return 0;
169 }
170
StartPlayback(std::unique_ptr<AudioServiceClient> & client,FILE * wavFile)171 int32_t StartPlayback(std::unique_ptr<AudioServiceClient> &client, FILE *wavFile)
172 {
173 uint8_t* buffer = nullptr;
174 size_t n = 2;
175 size_t bytesToWrite = 0;
176 size_t bytesWritten = 0;
177 size_t minBytes = 4;
178 int32_t writeError;
179 uint64_t timeStamp = 0;
180 size_t bufferLen = 0;
181 StreamBuffer stream;
182
183 if (client->GetMinimumBufferSize(bufferLen) < 0) {
184 AUDIO_ERR_LOG(" GetMinimumBufferSize failed");
185 return AUDIO_ERR;
186 }
187
188 buffer = (uint8_t *) malloc(n * bufferLen);
189 if (buffer == nullptr) {
190 AUDIO_ERR_LOG("Failed to allocate buffer");
191 return AUDIO_ERR;
192 }
193
194 while (!feof(wavFile)) {
195 bytesToWrite = fread(buffer, 1, bufferLen, wavFile);
196 bytesWritten = 0;
197
198 while ((bytesWritten < bytesToWrite) && ((bytesToWrite - bytesWritten) > minBytes)) {
199 stream.buffer = buffer + bytesWritten;
200 stream.bufferLen = bytesToWrite - bytesWritten;
201 bytesWritten += client->WriteStream(stream, writeError);
202 if (client->GetCurrentTimeStamp(timeStamp) >= 0) {
203 AUDIO_DEBUG_LOG("current timestamp: %{public}" PRIu64, timeStamp);
204 }
205 }
206 }
207
208 AUDIO_DEBUG_LOG("File writing over");
209
210 free(buffer);
211
212 return 0;
213 }
214
GetSampleFormat(int32_t wavSampleFormat)215 AudioSampleFormat GetSampleFormat(int32_t wavSampleFormat)
216 {
217 switch (wavSampleFormat) {
218 case SAMPLE_FORMAT_U8:
219 return AudioSampleFormat::SAMPLE_U8;
220 case SAMPLE_FORMAT_S16LE:
221 return AudioSampleFormat::SAMPLE_S16LE;
222 case SAMPLE_FORMAT_S24LE:
223 return AudioSampleFormat::SAMPLE_S24LE;
224 case SAMPLE_FORMAT_S32LE:
225 return AudioSampleFormat::SAMPLE_S32LE;
226 default:
227 return AudioSampleFormat::INVALID_WIDTH;
228 }
229 }
230
StartRendererPlayback(char * inputPath)231 int32_t StartRendererPlayback(char *inputPath)
232 {
233 AUDIO_INFO_LOG("================PLAYBACK STARTED==================");
234 wav_hdr wavHeader;
235 size_t headerSize = sizeof(wav_hdr);
236 char path[PATH_MAX + 1] = { 0x00 };
237 if ((strlen(inputPath) > PATH_MAX) || (realpath(inputPath, path) == nullptr)) {
238 AUDIO_ERR_LOG("Invalid path");
239 return AUDIO_ERR;
240 }
241 AUDIO_INFO_LOG("path = %{public}s", path);
242 FILE* wavFile = fopen(path, "rb");
243 if (wavFile == nullptr) {
244 AUDIO_ERR_LOG("Unable to open wave file");
245 return AUDIO_ERR;
246 }
247
248 float volume = 1.0f;
249 (void)fread(&wavHeader, 1, headerSize, wavFile);
250 AudioStreamParams audioParams;
251 audioParams.format = GetSampleFormat(wavHeader.bitsPerSample);
252 audioParams.samplingRate = wavHeader.SamplesPerSec;
253 audioParams.channels = wavHeader.NumOfChan;
254
255 std::unique_ptr client = std::make_unique<AudioServiceClient>();
256 if (InitPlayback(client, audioParams) < 0) {
257 AUDIO_INFO_LOG("Initialize playback failed");
258 (void)fclose(wavFile);
259 return AUDIO_ERR;
260 }
261
262 AudioSystemManager *audioSystemMgr = AudioSystemManager::GetInstance();
263 audioSystemMgr->SetVolume(AudioVolumeType::STREAM_MUSIC, volume);
264
265 if (StartPlayback(client, wavFile) < 0) {
266 AUDIO_INFO_LOG("Start playback failed");
267 (void)fclose(wavFile);
268 return AUDIO_ERR;
269 }
270
271 client->FlushStream();
272 client->StopStream();
273 client->ReleaseStream();
274 (void)fclose(wavFile);
275 sleep(SLEEP_TIME);
276 AUDIO_INFO_LOG("================PLAYBACK OVER==================");
277 return 0;
278 }
279
ActivateFileIoDevice(bool isActive)280 int32_t ActivateFileIoDevice(bool isActive)
281 {
282 AUDIO_INFO_LOG("==ActivateFileIoDevice== flag[%{public}d]", isActive);
283 AudioSystemManager::GetInstance()->SetDeviceActive(ActiveDeviceType::FILE_SINK_DEVICE, isActive);
284 return 0;
285 }
286
ReconfigureChannel(uint32_t count,DeviceType type)287 int32_t ReconfigureChannel(uint32_t count, DeviceType type)
288 {
289 AUDIO_INFO_LOG("==ReconfigureChannel== count [%{public}d] type [%{public}d]", count, type);
290 AudioSystemManager::GetInstance()->ReconfigureAudioChannel(count, type);
291 return 0;
292 }
293
PrintUsage()294 void PrintUsage()
295 {
296 cout << "[Audio Multichannel Test App]" << endl << endl;
297 cout << "Supported Functionalities:" << endl;
298 cout << " a) Activates/Deactivates file sink and source module" << endl;
299 cout << " b) Plays an input audio file"<< endl;
300 cout << " c) Records audio stream and writes to a file"<< endl;
301 cout << " d) Dynamically modify the sink and source module channel configuration"<< endl << endl;
302 cout << "================================Usage=======================================" << endl << endl;
303 cout << "-a\n\tActivates/Deactivates file sink and source module" << endl;
304 cout << "\tUsage : ./audio_multichannel_test -a <boolean>" << endl;
305 cout << "\tExample 1 : ./audio_multichannel_test -a true" << endl;
306 cout << "\tExample 2 : ./audio_multichannel_test -a false" << endl << endl;
307 cout << "-p\n\tPlays an input audio file" << endl;
308 cout << "\tUsage : ./audio_multichannel_test -p <audio file path>" << endl;
309 cout << "\tExample : ./audio_multichannel_test -p /data/test.wav" << endl << endl;
310 cout << "-r\n\tRecords audio stream and writes to a file" << endl;
311 cout << "\tUsage : ./audio_multichannel_test -r <file path> <sampling rate> <buffer count> <channel>" << endl;
312 cout << "\tNOTE :Total num of buffer to record = <buffer count> * 1024. <buffer count> Max value is 10" << endl;
313 cout << "\tExample 1 : ./audio_multichannel_test -r /data/record.pcm 48000 1 6" << endl;
314 cout << "\tExample 2 : ./audio_multichannel_test -r /data/record.pcm 44100 2 4" << endl << endl;
315 cout << "-c\n\tDynamically modify the sink and source module channel configuration" << endl;
316 cout << "\tUsage : ./audio_multichannel_test -c <channel count> <DeviceType>" << endl;
317 cout << "\tExample 1 : ./audio_multichannel_test -c 4 50" << endl;
318 cout << "\tExample 2: ./audio_multichannel_test -c 2 51" << endl;
319 cout << "\tNOTE :DeviceType 50=file_sink | 51=file_source" << endl;
320 }
321
main(int argc,char * argv[])322 int main(int argc, char* argv[])
323 {
324 int c;
325 bool flag;
326 opterr = 0;
327 while ((c = getopt(argc, argv, "a:c:p:r:h")) != AUDIO_INVALID_PARAM) {
328 switch (c) {
329 case 'a':
330 flag = (strcmp(optarg, "true") == 0 ? true : false);
331 ActivateFileIoDevice(flag);
332 break;
333 case 'p':
334 StartRendererPlayback(optarg);
335 break;
336 case 'r':
337 StartRecording(optarg, atoi(argv[optind]), atoi(argv[optind+1]), atoi(argv[optind+CHANNEL_INDEX]));
338 break;
339 case 'c':
340 ReconfigureChannel(atoi(optarg), static_cast<DeviceType>(atoi(argv[optind])));
341 break;
342 case 'h':
343 PrintUsage();
344 break;
345 default:
346 cout << "Unsupported option. Exiting!!!" << endl;
347 exit(0);
348 break;
349 }
350 }
351
352 return 0;
353 }
354