static int sample_hook(const uint8_t *frames, unsigned int nframes, const struct cras_audio_format *fmt, void *cb_data) { struct loopback_iodev *loopdev = (struct loopback_iodev *)cb_data; struct byte_buffer *sbuf = loopdev->sample_buffer; unsigned int frame_bytes = cras_get_format_bytes(fmt); unsigned int frames_to_copy, bytes_to_copy; struct cras_iodev *edev = cras_iodev_list_get_first_enabled_iodev( CRAS_STREAM_OUTPUT); /* If there's no active streams, the logic in frames_queued will fill * zeros for loopback capture, do not accept zeros for draining device. */ if (!edev || !edev->streams) return 0; frames_to_copy = MIN(buf_writable(sbuf) / frame_bytes, nframes); if (!frames_to_copy) return 0; bytes_to_copy = frames_to_copy * frame_bytes; memcpy(buf_write_pointer(sbuf), frames, bytes_to_copy); buf_increment_write(sbuf, bytes_to_copy); return frames_to_copy; }
int hfp_fill_output_with_zeros(struct hfp_info *info, struct cras_iodev *dev, unsigned int nframes) { unsigned int buf_avail; unsigned int format_bytes; unsigned int nbytes; uint8_t *buf; int i; int ret = 0; format_bytes = cras_get_format_bytes(dev->format); nbytes = nframes * format_bytes; /* Loop twice to make sure ring buffer is filled. */ for (i = 0; i < 2; i++) { buf = buf_write_pointer_size(info->playback_buf, &buf_avail); if (buf_avail == 0) break; buf_avail = MIN(nbytes, buf_avail); memset(buf, 0, buf_avail); buf_increment_write(info->playback_buf, buf_avail); nbytes -= buf_avail; ret += buf_avail / format_bytes; } return ret; }
static int frames_queued(const struct cras_iodev *iodev, struct timespec *hw_tstamp) { struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev; struct byte_buffer *sbuf = loopdev->sample_buffer; unsigned int frame_bytes = cras_get_format_bytes(iodev->format); struct cras_iodev *edev = cras_iodev_list_get_first_enabled_iodev( CRAS_STREAM_OUTPUT); if (!edev || !edev->streams) { unsigned int frames_since_start, frames_to_fill, bytes_to_fill; frames_since_start = cras_frames_since_time( &loopdev->dev_start_time, iodev->format->frame_rate); frames_to_fill = frames_since_start > loopdev->read_frames ? frames_since_start - loopdev->read_frames : 0; frames_to_fill = MIN(buf_writable(sbuf) / frame_bytes, frames_to_fill); if (frames_to_fill > 0) { bytes_to_fill = frames_to_fill * frame_bytes; memset(buf_write_pointer(sbuf), 0, bytes_to_fill); buf_increment_write(sbuf, bytes_to_fill); } } clock_gettime(CLOCK_MONOTONIC_RAW, hw_tstamp); return buf_queued(sbuf) / frame_bytes; }
int hfp_force_output_level(struct hfp_info *info, struct cras_iodev *dev, unsigned int level) { level *= cras_get_format_bytes(dev->format); level = MIN(level, MAX_HFP_BUF_SIZE_BYTES); buf_adjust_readable(info->playback_buf, level); return 0; }
static int put_record_buffer(struct cras_iodev *iodev, unsigned nframes) { struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev; struct byte_buffer *sbuf = loopdev->sample_buffer; unsigned int frame_bytes = cras_get_format_bytes(iodev->format); buf_increment_read(sbuf, nframes * frame_bytes); loopdev->read_frames += nframes; return 0; }
int hfp_buf_queued(struct hfp_info *info, const struct cras_iodev *dev) { size_t format_bytes; format_bytes = cras_get_format_bytes(dev->format); if (dev->direction == CRAS_STREAM_OUTPUT) return buf_queued(info->playback_buf) / format_bytes; else return buf_queued(info->capture_buf) / format_bytes; }
void hfp_buf_release(struct hfp_info *info, struct cras_iodev *dev, unsigned written_frames) { size_t format_bytes; format_bytes = cras_get_format_bytes(dev->format); written_frames *= format_bytes; if (dev->direction == CRAS_STREAM_OUTPUT) buf_increment_write(info->playback_buf, written_frames); else buf_increment_read(info->capture_buf, written_frames); }
static int get_record_buffer(struct cras_iodev *iodev, struct cras_audio_area **area, unsigned *frames) { struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev; struct byte_buffer *sbuf = loopdev->sample_buffer; unsigned int frame_bytes = cras_get_format_bytes(iodev->format); unsigned int avail_frames = buf_readable(sbuf) / frame_bytes; *frames = MIN(avail_frames, *frames); iodev->area->frames = *frames; cras_audio_area_config_buf_pointers(iodev->area, iodev->format, buf_read_pointer(sbuf)); *area = iodev->area; return 0; }
void hfp_buf_acquire(struct hfp_info *info, struct cras_iodev *dev, uint8_t **buf, unsigned *count) { size_t format_bytes; unsigned int buf_avail; format_bytes = cras_get_format_bytes(dev->format); *count *= format_bytes; if (dev->direction == CRAS_STREAM_OUTPUT) *buf = buf_write_pointer_size(info->playback_buf, &buf_avail); else *buf = buf_read_pointer_size(info->capture_buf, &buf_avail); if (*count > buf_avail) *count = buf_avail; *count /= format_bytes; }
/* Converts S16 N channels to S16 M channels. The out buffer must have room for * M channel. This convert function is used as the default behavior when channel * layout is not set from the client side. */ static size_t s16_default_all_to_all(struct cras_fmt_conv *conv, const int16_t *in, size_t in_frames, int16_t *out) { unsigned int num_in_ch = conv->in_fmt.num_channels; unsigned int num_out_ch = conv->out_fmt.num_channels; unsigned int in_ch, out_ch, i; memset(out, 0, num_out_ch * in_frames * cras_get_format_bytes(&conv->out_fmt)); for (out_ch = 0; out_ch < num_out_ch; out_ch++) { for (in_ch = 0; in_ch < num_in_ch; in_ch++) { for (i = 0; i < in_frames; i++) { out[out_ch + i * num_out_ch] += in[in_ch + i * num_in_ch] / num_in_ch; } } } return in_frames; }
static int get_buffer(struct cras_iodev *iodev, struct cras_audio_area **area, unsigned *frames) { struct hfp_io *hfpio = (struct hfp_io *)iodev; uint8_t *dst = NULL; if (!hfp_info_running(hfpio->info)) return -1; hfp_buf_acquire(hfpio->info, iodev, &dst, frames); iodev->area->frames = *frames; /* HFP is mono only. */ iodev->area->channels[0].step_bytes = cras_get_format_bytes(iodev->format); iodev->area->channels[0].buf = dst; *area = iodev->area; return 0; }
struct cras_fmt_conv *cras_fmt_conv_create(const struct cras_audio_format *in, const struct cras_audio_format *out, size_t max_frames, size_t pre_linear_resample) { struct cras_fmt_conv *conv; int rc; unsigned i; conv = calloc(1, sizeof(*conv)); if (conv == NULL) return NULL; conv->in_fmt = *in; conv->out_fmt = *out; conv->tmp_buf_frames = max_frames; conv->pre_linear_resample = pre_linear_resample; /* Set up sample format conversion. */ /* TODO(dgreid) - modify channel and sample rate conversion so * converting to s16 isnt necessary. */ if (in->format != SND_PCM_FORMAT_S16_LE) { conv->num_converters++; syslog(LOG_DEBUG, "Convert from format %d to %d.", in->format, out->format); switch(in->format) { case SND_PCM_FORMAT_U8: conv->in_format_converter = convert_u8_to_s16le; break; case SND_PCM_FORMAT_S24_LE: conv->in_format_converter = convert_s24le_to_s16le; break; case SND_PCM_FORMAT_S32_LE: conv->in_format_converter = convert_s32le_to_s16le; break; case SND_PCM_FORMAT_S24_3LE: conv->in_format_converter = convert_s243le_to_s16le; break; default: syslog(LOG_WARNING, "Invalid format %d", in->format); cras_fmt_conv_destroy(conv); return NULL; } } if (out->format != SND_PCM_FORMAT_S16_LE) { conv->num_converters++; syslog(LOG_DEBUG, "Convert from format %d to %d.", in->format, out->format); switch (out->format) { case SND_PCM_FORMAT_U8: conv->out_format_converter = convert_s16le_to_u8; break; case SND_PCM_FORMAT_S24_LE: conv->out_format_converter = convert_s16le_to_s24le; break; case SND_PCM_FORMAT_S32_LE: conv->out_format_converter = convert_s16le_to_s32le; break; case SND_PCM_FORMAT_S24_3LE: conv->out_format_converter = convert_s16le_to_s243le; break; default: syslog(LOG_WARNING, "Invalid format %d", out->format); cras_fmt_conv_destroy(conv); return NULL; } } /* Set up channel number conversion. */ if (in->num_channels != out->num_channels) { conv->num_converters++; syslog(LOG_DEBUG, "Convert from %zu to %zu channels.", in->num_channels, out->num_channels); /* Populate the conversion matrix base on in/out channel count * and layout. */ if (in->num_channels == 1 && out->num_channels == 2) { conv->channel_converter = s16_mono_to_stereo; } else if (in->num_channels == 1 && out->num_channels == 6) { conv->channel_converter = s16_mono_to_51; } else if (in->num_channels == 2 && out->num_channels == 1) { conv->channel_converter = s16_stereo_to_mono; } else if (in->num_channels == 2 && out->num_channels == 6) { conv->channel_converter = s16_stereo_to_51; } else if (in->num_channels == 6 && out->num_channels == 2) { int in_channel_layout_set = 0; /* Checks if channel_layout is set in the incoming format */ for (i = 0; i < CRAS_CH_MAX; i++) if (in->channel_layout[i] != -1) in_channel_layout_set = 1; /* Use the conversion matrix based converter when a * channel layout is set, or default to use existing * converter to downmix to stereo */ if (in_channel_layout_set) { conv->ch_conv_mtx = cras_channel_conv_matrix_alloc( in->num_channels, out->num_channels); if (conv->ch_conv_mtx == NULL) { cras_fmt_conv_destroy(conv); return NULL; } conv->channel_converter = convert_channels; surround51_to_stereo_downmix_mtx( conv->ch_conv_mtx, conv->in_fmt.channel_layout); } else { conv->channel_converter = s16_51_to_stereo; } } else { syslog(LOG_WARNING, "Using default channel map for %zu to %zu", in->num_channels, out->num_channels); conv->channel_converter = s16_default_all_to_all; } } else if (in->num_channels > 2 && !is_channel_layout_equal(in, out)) { conv->num_converters++; conv->ch_conv_mtx = cras_channel_conv_matrix_create(in, out); if (conv->ch_conv_mtx == NULL) { syslog(LOG_ERR, "Failed to create channel conversion matrix"); cras_fmt_conv_destroy(conv); return NULL; } conv->channel_converter = convert_channels; } /* Set up sample rate conversion. */ if (in->frame_rate != out->frame_rate) { conv->num_converters++; syslog(LOG_DEBUG, "Convert from %zu to %zu Hz.", in->frame_rate, out->frame_rate); conv->speex_state = speex_resampler_init(out->num_channels, in->frame_rate, out->frame_rate, SPEEX_QUALITY_LEVEL, &rc); if (conv->speex_state == NULL) { syslog(LOG_ERR, "Fail to create speex:%zu %zu %zu %d", out->num_channels, in->frame_rate, out->frame_rate, rc); cras_fmt_conv_destroy(conv); return NULL; } } /* Set up linear resampler. */ conv->num_converters++; conv->resampler = linear_resampler_create( out->num_channels, cras_get_format_bytes(out), out->frame_rate, out->frame_rate); if (conv->resampler == NULL) { syslog(LOG_ERR, "Fail to create linear resampler"); cras_fmt_conv_destroy(conv); return NULL; } /* Need num_converters-1 temp buffers, the final converter renders * directly into the output. */ for (i = 0; i < conv->num_converters - 1; i++) { conv->tmp_bufs[i] = malloc( max_frames * 4 * /* width in bytes largest format. */ MAX(in->num_channels, out->num_channels)); if (conv->tmp_bufs[i] == NULL) { cras_fmt_conv_destroy(conv); return NULL; } } assert(conv->num_converters <= MAX_NUM_CONVERTERS); return conv; }
int hfp_buf_size(struct hfp_info *info, struct cras_iodev *dev) { return info->playback_buf->used_size / cras_get_format_bytes(dev->format); }