Coverage Report

Created: 2026-09-28 06:12

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