1 /*
2  * Copyright (C) 2011 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 #include <inttypes.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <vector>
20 
21 #include <audio_effects/effect_downmix.h>
22 #include <audio_utils/channels.h>
23 #include <audio_utils/primitives.h>
24 #include <log/log.h>
25 #include <system/audio.h>
26 
27 #include "EffectDownmix.h"
28 #define FRAME_LENGTH 256
29 #define MAX_NUM_CHANNELS 8
30 
31 struct downmix_cntxt_s {
32   effect_descriptor_t desc;
33   effect_handle_t handle;
34   effect_config_t config;
35 
36   int numFileChannels;
37   int numProcessChannels;
38 };
39 
40 extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
41 
printUsage()42 void printUsage() {
43   printf("\nUsage:");
44   printf("\n     downmixtest <input_file> <out_file> [options]\n");
45   printf("\nwhere,");
46   printf("\n     <input_file>  is the input file name");
47   printf("\n                  on which LVM effects are applied");
48   printf("\n     <output_file> processed output file");
49   printf("\n     and options are mentioned below");
50   printf("\n");
51   printf("\n      -h");
52   printf("\n           Prints this usage information");
53   printf("\n");
54   printf("\n     -ch_fmt:<format_of_input_audio>");
55   printf("\n         0:AUDIO_CHANNEL_OUT_7POINT1(default)");
56   printf("\n         1:AUDIO_CHANNEL_OUT_5POINT1_SIDE");
57   printf("\n         2:AUDIO_CHANNEL_OUT_5POINT1_BACK");
58   printf("\n         3:AUDIO_CHANNEL_OUT_QUAD_SIDE");
59   printf("\n         4:AUDIO_CHANNEL_OUT_QUAD_BACK");
60   printf("\n");
61   printf("\n     -fch:<file_channels> (1 through 8)");
62   printf("\n");
63 }
64 
DownmixDefaultConfig(effect_config_t * pConfig)65 int32_t DownmixDefaultConfig(effect_config_t *pConfig) {
66   pConfig->inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
67   pConfig->inputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
68   pConfig->inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
69   pConfig->inputCfg.bufferProvider.getBuffer = nullptr;
70   pConfig->inputCfg.bufferProvider.releaseBuffer = nullptr;
71   pConfig->inputCfg.bufferProvider.cookie = nullptr;
72   pConfig->inputCfg.mask = EFFECT_CONFIG_ALL;
73 
74   pConfig->inputCfg.samplingRate = 44100;
75   pConfig->outputCfg.samplingRate = pConfig->inputCfg.samplingRate;
76 
77   // set a default value for the access mode, but should be overwritten by caller
78   pConfig->outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
79   pConfig->outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
80   pConfig->outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
81   pConfig->outputCfg.bufferProvider.getBuffer = nullptr;
82   pConfig->outputCfg.bufferProvider.releaseBuffer = nullptr;
83   pConfig->outputCfg.bufferProvider.cookie = nullptr;
84   pConfig->outputCfg.mask = EFFECT_CONFIG_ALL;
85 
86   return 0;
87 }
88 
DownmixConfiureAndEnable(downmix_cntxt_s * pDescriptor)89 int32_t DownmixConfiureAndEnable(downmix_cntxt_s *pDescriptor) {
90   effect_handle_t *effectHandle = &pDescriptor->handle;
91   downmix_module_t *downmixEffectHandle = (downmix_module_t *)*effectHandle;
92   const struct effect_interface_s *Downmix_api = downmixEffectHandle->itfe;
93   int32_t err = 0;
94   uint32_t replySize = (uint32_t)sizeof(err);
95 
96   err = (Downmix_api->command)(*effectHandle, EFFECT_CMD_SET_CONFIG,
97                                sizeof(effect_config_t), &(pDescriptor->config),
98                                &replySize, &err);
99   if (err != 0) {
100     ALOGE("Downmix command to configure returned an error %d", err);
101     return err;
102   }
103 
104   err = ((Downmix_api->command))(*effectHandle, EFFECT_CMD_ENABLE, 0, nullptr,
105                                  &replySize, &err);
106   if (err != 0) {
107     ALOGE("Downmix command to enable effect returned an error %d", err);
108     return err;
109   }
110   return 0;
111 }
112 
DownmixExecute(downmix_cntxt_s * pDescriptor,FILE * finp,FILE * fout)113 int32_t DownmixExecute(downmix_cntxt_s *pDescriptor, FILE *finp,
114                        FILE *fout) {
115   effect_handle_t *effectHandle = &pDescriptor->handle;
116   downmix_module_t *downmixEffectHandle = (downmix_module_t *)*effectHandle;
117   const struct effect_interface_s *Downmix_api = downmixEffectHandle->itfe;
118 
119   const int numFileChannels = pDescriptor->numFileChannels;
120   const int numProcessChannels = pDescriptor->numProcessChannels;
121   const int fileFrameSize = numFileChannels * sizeof(short);
122   const unsigned int outputChannels =
123       audio_channel_count_from_out_mask(AUDIO_CHANNEL_OUT_STEREO);
124 
125   std::vector<float> outFloat(FRAME_LENGTH * MAX_NUM_CHANNELS);
126   std::vector<float> inFloat(FRAME_LENGTH * MAX_NUM_CHANNELS);
127 
128   audio_buffer_t inbuffer, outbuffer;
129   inbuffer.f32 = inFloat.data();
130   outbuffer.f32 = outFloat.data();
131   inbuffer.frameCount = FRAME_LENGTH;
132   outbuffer.frameCount = FRAME_LENGTH;
133 
134   audio_buffer_t *pinbuf, *poutbuf;
135   pinbuf = &inbuffer;
136   poutbuf = &outbuffer;
137 
138   int frameCounter = 0;
139   std::vector<short> inS16(FRAME_LENGTH * MAX_NUM_CHANNELS);
140   std::vector<short> outS16(FRAME_LENGTH * MAX_NUM_CHANNELS);
141 
142   while (fread(inS16.data(), fileFrameSize, FRAME_LENGTH, finp) ==
143          FRAME_LENGTH) {
144     if (numFileChannels != numProcessChannels) {
145       adjust_channels(inS16.data(), numFileChannels, inS16.data(),
146                       numProcessChannels, sizeof(short),
147                       FRAME_LENGTH * fileFrameSize);
148     }
149 
150     memcpy_to_float_from_i16(inFloat.data(), inS16.data(),
151                              FRAME_LENGTH * numProcessChannels);
152 
153     const int32_t err = (Downmix_api->process)(*effectHandle, pinbuf, poutbuf);
154     if (err != 0) {
155       ALOGE("DownmixProcess returned an error %d", err);
156       return -1;
157     }
158 
159     memcpy_to_i16_from_float(outS16.data(), outFloat.data(),
160                              FRAME_LENGTH * outputChannels);
161     fwrite(outS16.data(), sizeof(short), (FRAME_LENGTH * outputChannels),
162            fout);
163     frameCounter++;
164   }
165   printf("frameCounter: [%d]\n", frameCounter);
166   return 0;
167 }
168 
DowmixMainProcess(downmix_cntxt_s * pDescriptor,FILE * finp,FILE * fout)169 int32_t DowmixMainProcess(downmix_cntxt_s *pDescriptor, FILE *finp,
170                           FILE *fout) {
171   effect_handle_t *effectHandle = &pDescriptor->handle;
172   int32_t sessionId = 0, ioId = 0;
173   const effect_uuid_t downmix_uuid = {
174       0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}};
175 
176   int32_t err = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(
177       &downmix_uuid, sessionId, ioId,
178       effectHandle);
179   if (err != 0) {
180     ALOGE("DownmixLib_Create returned an error %d", err);
181     return -1;
182   }
183 
184   // Passing the init config for time being.
185   err = DownmixConfiureAndEnable(pDescriptor);
186   if (err != 0) {
187     ALOGE("DownmixConfigureAndEnable returned an error %d", err);
188     return -1;
189   }
190   // execute call for downmix.
191   err = DownmixExecute(pDescriptor, finp, fout);
192   if (err != 0) {
193     ALOGE("DownmixExecute returned an error %d", err);
194     return -1;
195   }
196   // Release the library function.
197   err = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(*effectHandle);
198   if (err != 0) {
199     ALOGE("DownmixRelease returned an error %d", err);
200     return -1;
201   }
202   return 0;
203 }
204 
main(int argc,const char * argv[])205 int main(int argc, const char *argv[]) {
206   int numFileChannels = 1, numProcessChannels = 8;
207   downmix_cntxt_s descriptor = {};
208   DownmixDefaultConfig(&(descriptor.config));
209 
210   const char *infile = nullptr;
211   const char *outfile = nullptr;
212   for (int i = 1; i < argc; i++) {
213     printf("%s ", argv[i]);
214     if (argv[i][0] != '-') {
215       if (infile == nullptr) {
216         infile = argv[i];
217       } else if (outfile == nullptr) {
218         outfile = argv[i];
219       } else {
220         printUsage();
221         return -1;
222       }
223     } else if (!strncmp(argv[i], "-fs:", 4)) {
224       // Add a check for all the supported streams.
225       const int samplingFreq = atoi(argv[i] + 4);
226       if (samplingFreq != 8000 && samplingFreq != 11025 &&
227           samplingFreq != 12000 && samplingFreq != 16000 &&
228           samplingFreq != 22050 && samplingFreq != 24000 &&
229           samplingFreq != 32000 && samplingFreq != 44100 &&
230           samplingFreq != 48000 && samplingFreq != 88200 &&
231           samplingFreq != 96000 && samplingFreq != 176400 &&
232           samplingFreq != 192000) {
233         printf("Unsupported Sampling Frequency : %d", samplingFreq);
234         printUsage();
235         return -1;
236       }
237 
238       descriptor.config.inputCfg.samplingRate = samplingFreq;
239       descriptor.config.outputCfg.samplingRate = samplingFreq;
240     } else if (!strncmp(argv[i], "-ch_fmt:", 8)) {
241       const int format = atoi(argv[i] + 8);
242       uint32_t *audioType = &descriptor.config.inputCfg.channels;
243       switch (format) {
244         case 0:
245           *audioType = AUDIO_CHANNEL_OUT_7POINT1;
246           break;
247         case 1:
248           *audioType = AUDIO_CHANNEL_OUT_5POINT1_SIDE;
249           break;
250         case 2:
251           *audioType = AUDIO_CHANNEL_OUT_5POINT1_BACK;
252           break;
253         case 3:
254           *audioType = AUDIO_CHANNEL_OUT_QUAD_SIDE;
255           break;
256         case 4:
257           *audioType = AUDIO_CHANNEL_OUT_QUAD_BACK;
258           break;
259         default:
260           *audioType = AUDIO_CHANNEL_OUT_7POINT1;
261           break;
262       }
263       descriptor.numProcessChannels =
264           audio_channel_count_from_out_mask(*audioType);
265     } else if (!strncmp(argv[i], "-fch:", 5)) {
266       const int fChannels = atoi(argv[i] + 5);
267       if (fChannels > 8 || fChannels < 1) {
268         printf("Unsupported number of file channels : %d", fChannels);
269         printUsage();
270         return -1;
271       }
272       descriptor.numFileChannels = fChannels;
273 
274     } else if (!strncmp(argv[i], "-h", 2)) {
275       printUsage();
276       return 0;
277     }
278   }
279 
280   if (/*infile == nullptr || */ outfile == nullptr) {
281     printUsage();
282     return -1;
283   }
284 
285   FILE *finp = fopen(infile, "rb");
286   if (finp == nullptr) {
287     printf("Cannot open input file %s", infile);
288     return -1;
289   }
290   FILE *fout = fopen(outfile, "wb");
291   if (fout == nullptr) {
292     printf("Cannot open output file %s", outfile);
293     fclose(finp);
294     return -1;
295   }
296 
297   const int err = DowmixMainProcess(&descriptor, finp, fout);
298   // close input and output files.
299   fclose(finp);
300   fclose(fout);
301   if (err != 0) {
302     printf("Error: %d\n", err);
303   }
304   return err;
305 }
306