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