summaryrefslogtreecommitdiff
path: root/cras/src/server/input_data.c
blob: cc0f10ba867e25ba6d7d6d302a873f58b2374f18 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/* Copyright 2018 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <syslog.h>

#include "buffer_share.h"
#include "cras_audio_area.h"
#include "cras_dsp_pipeline.h"
#include "cras_mix.h"
#include "cras_rstream.h"
#include "cras_system_state.h"
#include "dsp_util.h"
#include "input_data.h"
#include "utlist.h"

void input_data_run(struct ext_dsp_module *ext, unsigned int nframes)
{
	struct input_data *data = (struct input_data *)ext;
	float *const *wp;
	int i;
	unsigned int writable;
	unsigned int offset = 0;

	while (nframes) {
		writable = float_buffer_writable(data->fbuffer);
		writable = MIN(nframes, writable);
		if (!writable) {
			syslog(LOG_ERR,
			       "Not enough space to process input data");
			break;
		}
		wp = float_buffer_write_pointer(data->fbuffer);
		for (i = 0; i < data->fbuffer->num_channels; i++)
			memcpy(wp[i], ext->ports[i] + offset,
			       writable * sizeof(float));

		float_buffer_written(data->fbuffer, writable);
		nframes -= writable;
		offset += writable;
	}
}

void input_data_configure(struct ext_dsp_module *ext, unsigned int buffer_size,
			  unsigned int num_channels, unsigned int rate)
{
	struct input_data *data = (struct input_data *)ext;

	if (data->fbuffer)
		float_buffer_destroy(&data->fbuffer);
	data->fbuffer = float_buffer_create(buffer_size, num_channels);
}

struct input_data *input_data_create(void *dev_ptr)
{
	struct input_data *data = (struct input_data *)calloc(1, sizeof(*data));

	data->dev_ptr = dev_ptr;

	data->ext.run = input_data_run;
	data->ext.configure = input_data_configure;

	return data;
}

void input_data_destroy(struct input_data **data)
{
	if ((*data)->fbuffer)
		float_buffer_destroy(&(*data)->fbuffer);
	free(*data);
	*data = NULL;
}

void input_data_set_all_streams_read(struct input_data *data,
				     unsigned int nframes)
{
	if (!data->fbuffer)
		return;

	if (float_buffer_level(data->fbuffer) < nframes) {
		syslog(LOG_ERR,
		       "All streams read %u frames exceeds %u"
		       " in input_data's buffer",
		       nframes, float_buffer_level(data->fbuffer));
		float_buffer_reset(data->fbuffer);
		return;
	}
	float_buffer_read(data->fbuffer, nframes);
}

/*
 * The logic is not trivial to return the cras_audio_area and offset for
 * a input stream to read. The buffer position and length of a bunch of
 * input member variables are described below.
 *
 *                          hw_ptr                 appl_ptr
 * a. buffer of input device: |------------------------|
 * b. fbuffer of input data:         |<--------------->|
 * c. stream offset of input data:         |<--------->|
 *    stream offset of input data:                |<-->|
 *    stream offset of input data:     |<------------->|
 * d. audio area of input data:          |<----------->|
 *
 * One thing to keep in mind is, the offset could exceed the size of
 * buffer to read. It's not intuitive though why the stream offset would
 * exceed buffer size. Check this example:
 *
 * Idev gets input buffer 500 frames. One stream read 400, while the other
 * stream read 100. We track stream offset [0, 300] after both stream
 * consumes 100 frames. In the next wake up, audio thread asks idev to
 * get 250 frames. Now the input data holds audio area containing 250 frames
 * of audio as queried, while its float buffer contains 400 frames of audio
 * deinterleaved from last wake up.
 *
 * Wake up at T0:
 *                        hw_ptr                        appl_ptr
 * Input audio area         |-------------------------------|
 * deinterleave float       |-------------------------------|
 * Stream 1 read                                     |------|
 * Stream 2 read                    |-----------------------|
 *
 * Wake up at T1:
                          hw_ptr                 appl_ptr
 * Input audio area                     |------------|
 * deinterleave float       |------------------------|
 * Stream 1 offset                                   |
 * Stream 2 offset                  |----------------|
 *
 * Case 1:
 * A normal input stream, of read offset 0, about to read from device.
 * We shall return the exact audio area from idev, and set read offset to 0.
 *
 * Case 2:
 * A normal input stream, of read offset 300, about to read from device.
 * We shall return the exact audio area from idev but clip read offset to 250.
 *
 * Case 3:
 * An APM Stream of read offset 300, would like to read the deinterleaved
 * float buffer. We shall let APM process the float buffer from offset 300.
 * Don't bother clip read offset in this case, because fbuffer contains
 * the deepest deinterleaved audio data ever read from idev.
 */
int 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)
{
	int apm_processed;
	struct cras_apm *apm;
	int stream_offset = buffer_share_id_offset(offsets, stream->stream_id);

	apm = cras_apm_list_get_active_apm(stream, data->dev_ptr);
	if (apm == NULL) {
		/*
		 * Case 1 and 2 from above example.
		 */
		*area = data->area;
		*offset = MIN(stream_offset, data->area->frames);
	} else {
		/*
		 * Case 3 from above example.
		 */
		apm_processed = cras_apm_list_process(apm, data->fbuffer,
						      stream_offset);
		if (apm_processed < 0) {
			cras_apm_list_remove_apm(stream->apm_list, apm);
			return 0;
		}
		buffer_share_offset_update(offsets, stream->stream_id,
					   apm_processed);
		*area = cras_apm_list_get_processed(apm);
		*offset = 0;
	}

	return 0;
}

int input_data_put_for_stream(struct input_data *data,
			      struct cras_rstream *stream,
			      struct buffer_share *offsets, unsigned int frames)
{
	struct cras_apm *apm =
		cras_apm_list_get_active_apm(stream, data->dev_ptr);

	if (apm)
		cras_apm_list_put_processed(apm, frames);
	else
		buffer_share_offset_update(offsets, stream->stream_id, frames);

	return 0;
}

float input_data_get_software_gain_scaler(struct input_data *data,
					  float idev_sw_gain_scaler,
					  struct cras_rstream *stream)
{
	struct cras_apm *apm;
	/*
	 * APM has more advanced gain control mechanism. If it is using tuned
	 * settings, give APM total control of the captured samples without
	 * additional gain scaler at all.
	 */
	apm = cras_apm_list_get_active_apm(stream, data->dev_ptr);
	if (apm && cras_apm_list_get_use_tuned_settings(apm))
		return 1.0f;

	return idev_sw_gain_scaler * cras_rstream_get_volume_scaler(stream);
}