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_stream.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<AudioServiceClient> client = std::make_unique<AudioStream>(STREAM_MUSIC, AUDIO_MODE_RECORD,
71 getuid());
72 if (!InitClient(client, samplingRate, channelCount)) {
73 AUDIO_ERR_LOG("Initialize client failed");
74 return AUDIO_ERR;
75 }
76
77 size_t bufferLen;
78 if (client->GetMinimumBufferSize(bufferLen) < 0) {
79 return AUDIO_ERR;
80 }
81
82 uint8_t* buffer = nullptr;
83 buffer = (uint8_t *) malloc(bufferLen);
84 if (buffer == nullptr) {
85 AUDIO_ERR_LOG("Failed to allocate buffer");
86 return AUDIO_ERR;
87 }
88
89 char realPath[PATH_MAX + 1] = {0x00};
90 std::string sourceFilePath(recPath);
91 std::string rootPath;
92 std::string fileName;
93
94 auto pos = sourceFilePath.rfind("/");
95 if (pos!= std::string::npos) {
96 rootPath = sourceFilePath.substr(0, pos);
97 fileName = sourceFilePath.substr(pos);
98 }
99
100 if ((strlen(sourceFilePath.c_str()) >= PATH_MAX) || (realpath(rootPath.c_str(), realPath) == nullptr)) {
101 free(buffer);
102 AUDIO_ERR_LOG("StartRecording:: Invalid path errno = %{public}d", errno);
103 return AUDIO_ERR;
104 }
105
106 std::string verifiedPath(realPath);
107 FILE *pFile = fopen(verifiedPath.append(fileName).c_str(), "wb");
108 if (pFile == nullptr) {
109 free(buffer);
110 AUDIO_ERR_LOG("StartRecording:: Failed to open file errno = %{public}d", errno);
111 return AUDIO_ERR;
112 }
113
114 size_t size = 1;
115 size_t numBuffersToCapture = frames * 1024;
116 StreamBuffer stream;
117 stream.buffer = buffer;
118 stream.bufferLen = bufferLen;
119 int32_t bytesRead = 0;
120
121 while (numBuffersToCapture) {
122 bytesRead = client->ReadStream(stream, false);
123 if (bytesRead < 0) {
124 break;
125 }
126
127 if (bytesRead > 0) {
128 fwrite(stream.buffer, size, bytesRead, pFile);
129 numBuffersToCapture--;
130 }
131 }
132
133 (void)fflush(pFile);
134 client->FlushStream();
135 client->StopStream();
136 client->ReleaseStream();
137 free(buffer);
138 (void)fclose(pFile);
139 sleep(SLEEP_TIME);
140 AUDIO_INFO_LOG("================RECORDING OVER==================");
141 return 0;
142 }
143
InitPlayback(std::unique_ptr<AudioServiceClient> & client,AudioStreamParams & audioParams)144 static int32_t InitPlayback(std::unique_ptr<AudioServiceClient> &client, AudioStreamParams &audioParams)
145 {
146 if (client == nullptr) {
147 AUDIO_ERR_LOG("Create AudioServiceClient instance failed");
148 return AUDIO_ERR;
149 }
150
151 AUDIO_INFO_LOG("Initializing of AudioServiceClient");
152 if (client->Initialize(AUDIO_SERVICE_CLIENT_PLAYBACK) < 0) {
153 AUDIO_ERR_LOG("Initializing of AudioServiceClien failed");
154 return AUDIO_ERR;
155 }
156
157 AUDIO_INFO_LOG("Creating Stream");
158 if (client->CreateStream(audioParams, STREAM_MUSIC) < 0) {
159 AUDIO_ERR_LOG("Creating Stream failed");
160 return AUDIO_ERR;
161 }
162
163 AUDIO_INFO_LOG("Starting Stream");
164 if (client->StartStream() < 0) {
165 AUDIO_ERR_LOG("Starting Stream failed");
166 return AUDIO_ERR;
167 }
168
169 return 0;
170 }
171
StartPlayback(std::unique_ptr<AudioServiceClient> & client,FILE * wavFile)172 int32_t StartPlayback(std::unique_ptr<AudioServiceClient> &client, FILE *wavFile)
173 {
174 uint8_t* buffer = nullptr;
175 size_t n = 2;
176 size_t bytesToWrite = 0;
177 size_t bytesWritten = 0;
178 size_t minBytes = 4;
179 int32_t writeError;
180 uint64_t timeStamp = 0;
181 size_t bufferLen = 0;
182 StreamBuffer stream;
183
184 if (client->GetMinimumBufferSize(bufferLen) < 0) {
185 AUDIO_ERR_LOG(" GetMinimumBufferSize failed");
186 return AUDIO_ERR;
187 }
188
189 buffer = (uint8_t *) malloc(n * bufferLen);
190 if (buffer == nullptr) {
191 AUDIO_ERR_LOG("Failed to allocate buffer");
192 return AUDIO_ERR;
193 }
194
195 while (!feof(wavFile)) {
196 bytesToWrite = fread(buffer, 1, bufferLen, wavFile);
197 bytesWritten = 0;
198
199 while ((bytesWritten < bytesToWrite) && ((bytesToWrite - bytesWritten) > minBytes)) {
200 stream.buffer = buffer + bytesWritten;
201 stream.bufferLen = bytesToWrite - bytesWritten;
202 bytesWritten += client->WriteStream(stream, writeError);
203 if (client->GetCurrentTimeStamp(timeStamp) >= 0) {
204 AUDIO_DEBUG_LOG("current timestamp: %{public}" PRIu64, timeStamp);
205 }
206 }
207 }
208
209 AUDIO_DEBUG_LOG("File writing over");
210
211 free(buffer);
212
213 return 0;
214 }
215
GetSampleFormat(int32_t wavSampleFormat)216 AudioSampleFormat GetSampleFormat(int32_t wavSampleFormat)
217 {
218 switch (wavSampleFormat) {
219 case SAMPLE_FORMAT_U8:
220 return AudioSampleFormat::SAMPLE_U8;
221 case SAMPLE_FORMAT_S16LE:
222 return AudioSampleFormat::SAMPLE_S16LE;
223 case SAMPLE_FORMAT_S24LE:
224 return AudioSampleFormat::SAMPLE_S24LE;
225 case SAMPLE_FORMAT_S32LE:
226 return AudioSampleFormat::SAMPLE_S32LE;
227 default:
228 return AudioSampleFormat::INVALID_WIDTH;
229 }
230 }
231
StartRendererPlayback(char * inputPath)232 int32_t StartRendererPlayback(char *inputPath)
233 {
234 AUDIO_INFO_LOG("================PLAYBACK STARTED==================");
235 wav_hdr wavHeader;
236 size_t headerSize = sizeof(wav_hdr);
237 char path[PATH_MAX + 1] = { 0x00 };
238 if ((strlen(inputPath) > PATH_MAX) || (realpath(inputPath, path) == nullptr)) {
239 AUDIO_ERR_LOG("Invalid path");
240 return AUDIO_ERR;
241 }
242 AUDIO_INFO_LOG("path = %{public}s", path);
243 FILE* wavFile = fopen(path, "rb");
244 if (wavFile == nullptr) {
245 AUDIO_ERR_LOG("Unable to open wave file");
246 return AUDIO_ERR;
247 }
248
249 float volume = 1.0f;
250 (void)fread(&wavHeader, 1, headerSize, wavFile);
251 AudioStreamParams audioParams;
252 audioParams.format = GetSampleFormat(wavHeader.bitsPerSample);
253 audioParams.samplingRate = wavHeader.SamplesPerSec;
254 audioParams.channels = wavHeader.NumOfChan;
255
256 std::unique_ptr<AudioServiceClient> client = std::make_unique<AudioStream>(STREAM_MUSIC, AUDIO_MODE_PLAYBACK,
257 getuid());
258 if (InitPlayback(client, audioParams) < 0) {
259 AUDIO_INFO_LOG("Initialize playback failed");
260 (void)fclose(wavFile);
261 return AUDIO_ERR;
262 }
263
264 AudioSystemManager *audioSystemMgr = AudioSystemManager::GetInstance();
265 audioSystemMgr->SetVolume(AudioVolumeType::STREAM_MUSIC, volume);
266
267 if (StartPlayback(client, wavFile) < 0) {
268 AUDIO_INFO_LOG("Start playback failed");
269 (void)fclose(wavFile);
270 return AUDIO_ERR;
271 }
272
273 client->FlushStream();
274 client->StopStream();
275 client->ReleaseStream();
276 (void)fclose(wavFile);
277 sleep(SLEEP_TIME);
278 AUDIO_INFO_LOG("================PLAYBACK OVER==================");
279 return 0;
280 }
281
ActivateFileIoDevice(bool isActive)282 int32_t ActivateFileIoDevice(bool isActive)
283 {
284 AUDIO_INFO_LOG("==ActivateFileIoDevice== flag[%{public}d]", isActive);
285 AudioSystemManager::GetInstance()->SetDeviceActive(ActiveDeviceType::FILE_SINK_DEVICE, isActive);
286 return 0;
287 }
288
ReconfigureChannel(uint32_t count,DeviceType type)289 int32_t ReconfigureChannel(uint32_t count, DeviceType type)
290 {
291 AUDIO_INFO_LOG("==ReconfigureChannel== count [%{public}d] type [%{public}d]", count, type);
292 AudioSystemManager::GetInstance()->ReconfigureAudioChannel(count, type);
293 return 0;
294 }
295
PrintUsage()296 void PrintUsage()
297 {
298 cout << "[Audio Multichannel Test App]" << endl << endl;
299 cout << "Supported Functionalities:" << endl;
300 cout << " a) Activates/Deactivates file sink and source module" << endl;
301 cout << " b) Plays an input audio file"<< endl;
302 cout << " c) Records audio stream and writes to a file"<< endl;
303 cout << " d) Dynamically modify the sink and source module channel configuration"<< endl << endl;
304 cout << "================================Usage=======================================" << endl << endl;
305 cout << "-a\n\tActivates/Deactivates file sink and source module" << endl;
306 cout << "\tUsage : ./audio_multichannel_test -a <boolean>" << endl;
307 cout << "\tExample 1 : ./audio_multichannel_test -a true" << endl;
308 cout << "\tExample 2 : ./audio_multichannel_test -a false" << endl << endl;
309 cout << "-p\n\tPlays an input audio file" << endl;
310 cout << "\tUsage : ./audio_multichannel_test -p <audio file path>" << endl;
311 cout << "\tExample : ./audio_multichannel_test -p /data/test.wav" << endl << endl;
312 cout << "-r\n\tRecords audio stream and writes to a file" << endl;
313 cout << "\tUsage : ./audio_multichannel_test -r <file path> <sampling rate> <buffer count> <channel>" << endl;
314 cout << "\tNOTE :Total num of buffer to record = <buffer count> * 1024. <buffer count> Max value is 10" << endl;
315 cout << "\tExample 1 : ./audio_multichannel_test -r /data/record.pcm 48000 1 6" << endl;
316 cout << "\tExample 2 : ./audio_multichannel_test -r /data/record.pcm 44100 2 4" << endl << endl;
317 cout << "-c\n\tDynamically modify the sink and source module channel configuration" << endl;
318 cout << "\tUsage : ./audio_multichannel_test -c <channel count> <DeviceType>" << endl;
319 cout << "\tExample 1 : ./audio_multichannel_test -c 4 50" << endl;
320 cout << "\tExample 2: ./audio_multichannel_test -c 2 51" << endl;
321 cout << "\tNOTE :DeviceType 50=file_sink | 51=file_source" << endl;
322 }
323
main(int argc,char * argv[])324 int main(int argc, char* argv[])
325 {
326 int c;
327 bool flag;
328 opterr = 0;
329 while ((c = getopt(argc, argv, "a:c:p:r:h")) != AUDIO_INVALID_PARAM) {
330 switch (c) {
331 case 'a':
332 flag = (strcmp(optarg, "true") == 0 ? true : false);
333 ActivateFileIoDevice(flag);
334 break;
335 case 'p':
336 StartRendererPlayback(optarg);
337 break;
338 case 'r':
339 StartRecording(optarg, atoi(argv[optind]), atoi(argv[optind+1]), atoi(argv[optind+CHANNEL_INDEX]));
340 break;
341 case 'c':
342 ReconfigureChannel(atoi(optarg), static_cast<DeviceType>(atoi(argv[optind])));
343 break;
344 case 'h':
345 PrintUsage();
346 break;
347 default:
348 cout << "Unsupported option. Exiting!!!" << endl;
349 exit(0);
350 break;
351 }
352 }
353
354 return 0;
355 }
356