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