1 /* Copyright 2018 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6 #include <syslog.h>
7
8 #include "buffer_share.h"
9 #include "cras_audio_area.h"
10 #include "cras_dsp_pipeline.h"
11 #include "cras_mix.h"
12 #include "cras_rstream.h"
13 #include "cras_system_state.h"
14 #include "dsp_util.h"
15 #include "input_data.h"
16 #include "utlist.h"
17
input_data_run(struct ext_dsp_module * ext,unsigned int nframes)18 void input_data_run(struct ext_dsp_module *ext, unsigned int nframes)
19 {
20 struct input_data *data = (struct input_data *)ext;
21 float *const *wp;
22 int i;
23 unsigned int writable;
24 unsigned int offset = 0;
25
26 while (nframes) {
27 writable = float_buffer_writable(data->fbuffer);
28 writable = MIN(nframes, writable);
29 if (!writable) {
30 syslog(LOG_ERR,
31 "Not enough space to process input data");
32 break;
33 }
34 wp = float_buffer_write_pointer(data->fbuffer);
35 for (i = 0; i < data->fbuffer->num_channels; i++)
36 memcpy(wp[i], ext->ports[i] + offset,
37 writable * sizeof(float));
38
39 float_buffer_written(data->fbuffer, writable);
40 nframes -= writable;
41 offset += writable;
42 }
43 }
44
input_data_configure(struct ext_dsp_module * ext,unsigned int buffer_size,unsigned int num_channels,unsigned int rate)45 void input_data_configure(struct ext_dsp_module *ext, unsigned int buffer_size,
46 unsigned int num_channels, unsigned int rate)
47 {
48 struct input_data *data = (struct input_data *)ext;
49
50 if (data->fbuffer)
51 float_buffer_destroy(&data->fbuffer);
52 data->fbuffer = float_buffer_create(buffer_size, num_channels);
53 }
54
input_data_create(void * dev_ptr)55 struct input_data *input_data_create(void *dev_ptr)
56 {
57 struct input_data *data = (struct input_data *)calloc(1, sizeof(*data));
58
59 data->dev_ptr = dev_ptr;
60
61 data->ext.run = input_data_run;
62 data->ext.configure = input_data_configure;
63
64 return data;
65 }
66
input_data_destroy(struct input_data ** data)67 void input_data_destroy(struct input_data **data)
68 {
69 if ((*data)->fbuffer)
70 float_buffer_destroy(&(*data)->fbuffer);
71 free(*data);
72 *data = NULL;
73 }
74
input_data_set_all_streams_read(struct input_data * data,unsigned int nframes)75 void input_data_set_all_streams_read(struct input_data *data,
76 unsigned int nframes)
77 {
78 if (!data->fbuffer)
79 return;
80
81 if (float_buffer_level(data->fbuffer) < nframes) {
82 syslog(LOG_ERR,
83 "All streams read %u frames exceeds %u"
84 " in input_data's buffer",
85 nframes, float_buffer_level(data->fbuffer));
86 float_buffer_reset(data->fbuffer);
87 return;
88 }
89 float_buffer_read(data->fbuffer, nframes);
90 }
91
92 /*
93 * The logic is not trivial to return the cras_audio_area and offset for
94 * a input stream to read. The buffer position and length of a bunch of
95 * input member variables are described below.
96 *
97 * hw_ptr appl_ptr
98 * a. buffer of input device: |------------------------|
99 * b. fbuffer of input data: |<--------------->|
100 * c. stream offset of input data: |<--------->|
101 * stream offset of input data: |<-->|
102 * stream offset of input data: |<------------->|
103 * d. audio area of input data: |<----------->|
104 *
105 * One thing to keep in mind is, the offset could exceed the size of
106 * buffer to read. It's not intuitive though why the stream offset would
107 * exceed buffer size. Check this example:
108 *
109 * Idev gets input buffer 500 frames. One stream read 400, while the other
110 * stream read 100. We track stream offset [0, 300] after both stream
111 * consumes 100 frames. In the next wake up, audio thread asks idev to
112 * get 250 frames. Now the input data holds audio area containing 250 frames
113 * of audio as queried, while its float buffer contains 400 frames of audio
114 * deinterleaved from last wake up.
115 *
116 * Wake up at T0:
117 * hw_ptr appl_ptr
118 * Input audio area |-------------------------------|
119 * deinterleave float |-------------------------------|
120 * Stream 1 read |------|
121 * Stream 2 read |-----------------------|
122 *
123 * Wake up at T1:
124 hw_ptr appl_ptr
125 * Input audio area |------------|
126 * deinterleave float |------------------------|
127 * Stream 1 offset |
128 * Stream 2 offset |----------------|
129 *
130 * Case 1:
131 * A normal input stream, of read offset 0, about to read from device.
132 * We shall return the exact audio area from idev, and set read offset to 0.
133 *
134 * Case 2:
135 * A normal input stream, of read offset 300, about to read from device.
136 * We shall return the exact audio area from idev but clip read offset to 250.
137 *
138 * Case 3:
139 * An APM Stream of read offset 300, would like to read the deinterleaved
140 * float buffer. We shall let APM process the float buffer from offset 300.
141 * Don't bother clip read offset in this case, because fbuffer contains
142 * the deepest deinterleaved audio data ever read from idev.
143 */
input_data_get_for_stream(struct input_data * data,struct cras_rstream * stream,struct buffer_share * offsets,struct cras_audio_area ** area,unsigned int * offset)144 int input_data_get_for_stream(struct input_data *data,
145 struct cras_rstream *stream,
146 struct buffer_share *offsets,
147 struct cras_audio_area **area,
148 unsigned int *offset)
149 {
150 int apm_processed;
151 struct cras_apm *apm;
152 int stream_offset = buffer_share_id_offset(offsets, stream->stream_id);
153
154 apm = cras_apm_list_get_active_apm(stream, data->dev_ptr);
155 if (apm == NULL) {
156 /*
157 * Case 1 and 2 from above example.
158 */
159 *area = data->area;
160 *offset = MIN(stream_offset, data->area->frames);
161 } else {
162 /*
163 * Case 3 from above example.
164 */
165 apm_processed = cras_apm_list_process(apm, data->fbuffer,
166 stream_offset);
167 if (apm_processed < 0) {
168 cras_apm_list_remove_apm(stream->apm_list, apm);
169 return 0;
170 }
171 buffer_share_offset_update(offsets, stream->stream_id,
172 apm_processed);
173 *area = cras_apm_list_get_processed(apm);
174 *offset = 0;
175 }
176
177 return 0;
178 }
179
input_data_put_for_stream(struct input_data * data,struct cras_rstream * stream,struct buffer_share * offsets,unsigned int frames)180 int input_data_put_for_stream(struct input_data *data,
181 struct cras_rstream *stream,
182 struct buffer_share *offsets, unsigned int frames)
183 {
184 struct cras_apm *apm =
185 cras_apm_list_get_active_apm(stream, data->dev_ptr);
186
187 if (apm)
188 cras_apm_list_put_processed(apm, frames);
189 else
190 buffer_share_offset_update(offsets, stream->stream_id, frames);
191
192 return 0;
193 }
194
input_data_get_software_gain_scaler(struct input_data * data,float idev_sw_gain_scaler,struct cras_rstream * stream)195 float input_data_get_software_gain_scaler(struct input_data *data,
196 float idev_sw_gain_scaler,
197 struct cras_rstream *stream)
198 {
199 struct cras_apm *apm;
200 /*
201 * APM has more advanced gain control mechanism. If it is using tuned
202 * settings, give APM total control of the captured samples without
203 * additional gain scaler at all.
204 */
205 apm = cras_apm_list_get_active_apm(stream, data->dev_ptr);
206 if (apm && cras_apm_list_get_use_tuned_settings(apm))
207 return 1.0f;
208
209 return idev_sw_gain_scaler * cras_rstream_get_volume_scaler(stream);
210 }
211