Add Acoustic Echo Cancellation services
[platform/core/multimedia/libmm-sound.git] / aec / algo_speex.c
1 /*
2 * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
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 #include <dlog.h>
18 #include <speex/speex.h>
19 #include <speex/speex_preprocess.h>
20 #include <speex/speex_echo.h>
21 #include "algo_speex.h"
22
23 //#define __DEBUG__
24
25 #ifdef __DEBUG__
26 #include <stdio.h>
27 #include <sys/time.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 static int fdrec, fdref, fdout;
32 #endif
33
34 static int speex_init(int nframes, int rate, int channels);
35 static int speex_process(unsigned char *rec, unsigned char *ref, unsigned char *out);
36 static int speex_deinit();
37
38 static struct ec_speex {
39         SpeexEchoState *echo_state;
40         SpeexPreprocessState *preprocess;
41         int nframes;
42         int filter_frames;
43         int channels;
44         int rate;
45 } sp;
46
47 static ec_operation_t op = {
48         .init = speex_init,
49         .process = speex_process,
50         .deinit = speex_deinit,
51 };
52
53 ec_operation_t *get_speex_instance()
54 {
55         return &op;
56 }
57
58 static int speex_init(int nframes, int rate, int channels)
59 {
60         sp.nframes = nframes;
61         sp.filter_frames = nframes; /* TODO:It takes much time when 10 times of nframe */
62         sp.channels = channels;
63         sp.rate = rate;
64
65         LOGI("nframes(%d), filter_frames(%d) rate(%d), channels(%d)",
66                         sp.nframes, sp.filter_frames, sp.rate, sp.channels);
67
68         sp.echo_state = speex_echo_state_init_mc(sp.nframes, sp.filter_frames,
69                                                         sp.channels, sp.channels);
70         if (!sp.echo_state) {
71                 LOGE("speex_echo_state_init_mc failed\n");
72                 return -1;
73         }
74
75         sp.preprocess = speex_preprocess_state_init(sp.nframes, sp.rate);
76         if (!sp.preprocess) {
77                 LOGE("speex_echo_state_init_mc failed\n");
78                 return -1;
79         }
80
81         if (speex_echo_ctl(sp.echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, &sp.rate)) {
82                 LOGE("speex_echo_ctl SET_SAMPLING_RATE failed\n");
83                 return -1;
84         }
85
86         if (speex_preprocess_ctl(sp.preprocess, SPEEX_PREPROCESS_SET_ECHO_STATE, sp.echo_state)) {
87                 LOGE("speex_echo_ctl SET_ECHO_STATE failed\n");
88                 return -1;
89         }
90
91 #ifdef __DEBUG__
92         unlink("/tmp/rec.raw");
93         unlink("/tmp/ref.raw");
94         unlink("/tmp/out.raw");
95
96         fdrec = open("/tmp/rec.raw", O_RDWR | O_CREAT | O_TRUNC, 777);
97         fdref = open("/tmp/ref.raw", O_RDWR | O_CREAT | O_TRUNC, 777);
98         fdout = open("/tmp/out.raw", O_RDWR | O_CREAT | O_TRUNC, 777);
99 #endif
100
101         return 0;
102 }
103
104 static int speex_process(unsigned char *rec, unsigned char *ref, unsigned char *out)
105 {
106 #ifdef __DEBUG__
107         struct timeval before,after;
108         write(fdrec, rec, sp.nframes * 4);
109         write(fdref, ref, sp.nframes * 4);
110         gettimeofday(&before,NULL);
111 #endif
112
113         speex_echo_cancellation(sp.echo_state,
114                                 (const spx_int16_t *)rec,
115                                 (const spx_int16_t *)ref,
116                                 (spx_int16_t *)out);
117         speex_preprocess_run(sp.preprocess, (spx_int16_t *)out);
118
119         rec += sp.nframes * sp.channels;
120         ref += sp.nframes * sp.channels;
121         out += sp.nframes * sp.channels;
122
123         speex_echo_cancellation(sp.echo_state,
124                                 (const spx_int16_t *)rec,
125                                 (const spx_int16_t *)ref,
126                                 (spx_int16_t *)out);
127         speex_preprocess_run(sp.preprocess, (spx_int16_t *)out);
128
129 #ifdef __DEBUG__
130         gettimeofday(&after,NULL);
131         LOGE("It takes time(%ld)",
132                         1000*(after.tv_sec-before.tv_sec) + (after.tv_usec-before.tv_usec)/1000);
133         write(fdout, out, sp.nframes * 4);
134 #endif
135
136         return 0;
137 }
138
139 static int speex_deinit()
140 {
141         if (sp.echo_state)
142                 speex_echo_state_destroy(sp.echo_state);
143         if (sp.preprocess)
144                 speex_preprocess_state_destroy(sp.preprocess);
145
146 #ifdef __DEBUG__
147         if (fdrec)
148                 close(fdrec);
149         if (fdref)
150                 close(fdref);
151         if (fdout)
152                 close(fdout);
153 #endif
154
155         return 0;
156 }
157