1 /******************************************************************************
2  *
3  *  Copyright 2022 Google LLC
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at:
8  *
9  *  http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  ******************************************************************************/
18 
19 #include "lc3.h"
20 #include <Python.h>
21 #include <numpy/ndarrayobject.h>
22 
23 #include <spec.c>
24 #include "ctypes.h"
25 
estimate_gain_py(PyObject * m,PyObject * args)26 static PyObject *estimate_gain_py(PyObject *m, PyObject *args)
27 {
28     PyObject *x_obj;
29     unsigned dt, sr;
30     float *x;
31     int nbits_budget;
32     float nbits_off;
33     int g_off;
34     bool reset_off;
35 
36     if (!PyArg_ParseTuple(args, "IIOifi", &dt, &sr,
37                 &x_obj, &nbits_budget, &nbits_off, &g_off))
38         return NULL;
39 
40     CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
41     CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
42 
43     int ne = LC3_NE(dt, sr);
44 
45     CTYPES_CHECK("x", x_obj = to_1d_ptr(x_obj, NPY_FLOAT, ne, &x));
46 
47     int g_int = estimate_gain(dt, sr,
48         x, nbits_budget, nbits_off, g_off, &reset_off);
49 
50     return Py_BuildValue("ii", g_int, reset_off);
51 }
52 
adjust_gain_py(PyObject * m,PyObject * args)53 static PyObject *adjust_gain_py(PyObject *m, PyObject *args)
54 {
55     unsigned sr;
56     int g_idx, nbits, nbits_budget;
57 
58     if (!PyArg_ParseTuple(args, "Iiii", &sr, &g_idx, &nbits, &nbits_budget))
59         return NULL;
60 
61     CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
62     CTYPES_CHECK("g_idx", g_idx >= 0 && g_idx <= 255);
63 
64     g_idx = adjust_gain(sr, g_idx, nbits, nbits_budget);
65 
66     return Py_BuildValue("i", g_idx);
67 }
68 
quantize_py(PyObject * m,PyObject * args)69 static PyObject *quantize_py(PyObject *m, PyObject *args)
70 {
71     PyObject *x_obj, *xq_obj;
72     unsigned dt, sr;
73     float *x;
74     int16_t *xq;
75     int g_int, nq;
76 
77     if (!PyArg_ParseTuple(args, "IIiO", &dt, &sr, &g_int, &x_obj))
78         return NULL;
79 
80     CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
81     CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
82     CTYPES_CHECK("g_int", g_int >= -255 && g_int <= 255);
83 
84     int ne = LC3_NE(dt, sr);
85 
86     CTYPES_CHECK("x", x_obj = to_1d_ptr(x_obj, NPY_FLOAT, ne, &x));
87 
88     xq_obj = new_1d_ptr(NPY_INT16, ne, &xq);
89     uint16_t __xq[ne];
90 
91     quantize(dt, sr, g_int, x, __xq, &nq);
92 
93     for (int i = 0; i < nq; i++)
94         xq[i] = __xq[i] & 1 ? -(__xq[i] >> 1) : (__xq[i] >> 1);
95 
96     return Py_BuildValue("ONi", x_obj, xq_obj, nq);
97 }
98 
compute_nbits_py(PyObject * m,PyObject * args)99 static PyObject *compute_nbits_py(PyObject *m, PyObject *args)
100 {
101     PyObject *xq_obj;
102     unsigned dt, sr, nbytes;
103     int16_t *xq;
104     int nq, nbits_budget;
105     bool lsb_mode;
106 
107     if (!PyArg_ParseTuple(args, "IIIOii", &dt, &sr,
108                 &nbytes, &xq_obj, &nq, &nbits_budget))
109         return NULL;
110 
111     CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
112     CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
113 
114     int ne = LC3_NE(dt, sr);
115 
116     CTYPES_CHECK("xq", xq_obj = to_1d_ptr(xq_obj, NPY_INT16, ne, &xq));
117 
118     uint16_t __xq[ne];
119     for (int i = 0; i < ne; i++)
120         __xq[i] = xq[i] < 0 ? (-xq[i] << 1) + 1 : (xq[i] << 1);
121 
122     int nbits = compute_nbits(
123         dt, sr, nbytes, __xq, &nq, nbits_budget, &lsb_mode);
124 
125     return Py_BuildValue("iii", nbits, nq, lsb_mode);
126 }
127 
analyze_py(PyObject * m,PyObject * args)128 static PyObject *analyze_py(PyObject *m, PyObject *args)
129 {
130     PyObject *tns_obj, *spec_obj, *x_obj, *xq_obj;
131     struct lc3_tns_data tns = { 0 };
132     struct lc3_spec_analysis spec = { 0 };
133     struct lc3_spec_side side = { 0 };
134     unsigned dt, sr, nbytes;
135     int pitch;
136     float *x;
137     int16_t *xq;
138 
139     if (!PyArg_ParseTuple(args, "IIIpOOO", &dt, &sr, &nbytes,
140                 &pitch, &tns_obj, &spec_obj, &x_obj))
141         return NULL;
142 
143     CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
144     CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
145 
146     int ne = LC3_NE(dt, sr);
147 
148     CTYPES_CHECK(NULL, tns_obj = to_tns_data(tns_obj, &tns));
149     CTYPES_CHECK(NULL, spec_obj = to_spec_analysis(spec_obj, &spec));
150     CTYPES_CHECK("x", x_obj = to_1d_ptr(x_obj, NPY_FLOAT, ne, &x));
151 
152     xq_obj = new_1d_ptr(NPY_INT16, ne, &xq);
153     uint16_t __xq[ne];
154 
155     lc3_spec_analyze(dt, sr, nbytes, pitch, &tns, &spec, x, __xq, &side);
156 
157     for (int i = 0; i < ne; i++)
158         xq[i] = __xq[i] & 1 ? -(__xq[i] >> 1) : (__xq[i] >> 1);
159 
160     from_spec_analysis(spec_obj, &spec);
161     return Py_BuildValue("ONN", x_obj, xq_obj, new_spec_side(&side));
162 }
163 
estimate_noise_py(PyObject * m,PyObject * args)164 static PyObject *estimate_noise_py(PyObject *m, PyObject *args)
165 {
166     PyObject *x_obj, *xq_obj;
167     unsigned dt, bw;
168     int16_t *xq;
169     float *x;
170     int nq;
171 
172     if (!PyArg_ParseTuple(args, "IIOIO", &dt, &bw, &xq_obj, &nq, &x_obj))
173         return NULL;
174 
175     CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
176     CTYPES_CHECK("bw", (unsigned)bw < LC3_NUM_BANDWIDTH);
177 
178     int ne = LC3_NE(dt, bw);
179 
180     CTYPES_CHECK("xq", xq_obj = to_1d_ptr(xq_obj, NPY_INT16, ne, &xq));
181     CTYPES_CHECK("x" , x_obj = to_1d_ptr(x_obj, NPY_FLOAT, ne, &x ));
182 
183     uint16_t __xq[nq];
184     for (int i = 0; i < nq; i++)
185         __xq[i] = xq[i] < 0 ? (-xq[i] << 1) + 1 : (xq[i] << 1);
186 
187     int noise_factor = estimate_noise(dt, bw, __xq, nq, x);
188 
189     return Py_BuildValue("i", noise_factor);
190 }
191 
192 static PyMethodDef methods[] = {
193     { "spec_estimate_gain" , estimate_gain_py , METH_VARARGS },
194     { "spec_adjust_gain"   , adjust_gain_py   , METH_VARARGS },
195     { "spec_quantize"      , quantize_py      , METH_VARARGS },
196     { "spec_compute_nbits" , compute_nbits_py , METH_VARARGS },
197     { "spec_analyze"       , analyze_py       , METH_VARARGS },
198     { "spec_estimate_noise", estimate_noise_py, METH_VARARGS },
199     { NULL },
200 };
201 
lc3_spec_py_init(PyObject * m)202 PyMODINIT_FUNC lc3_spec_py_init(PyObject *m)
203 {
204     import_array();
205 
206     PyModule_AddFunctions(m, methods);
207 
208     return m;
209 }
210