예제 #1
0
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;
}
예제 #2
0
파일: cras_hfp_info.c 프로젝트: dgreid/adhd
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;
}
예제 #3
0
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;
}
예제 #4
0
파일: cras_hfp_info.c 프로젝트: dgreid/adhd
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;
}
예제 #5
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;
}
예제 #6
0
파일: cras_hfp_info.c 프로젝트: dgreid/adhd
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;
}
예제 #7
0
파일: cras_hfp_info.c 프로젝트: dgreid/adhd
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);
}
예제 #8
0
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;
}
예제 #9
0
파일: cras_hfp_info.c 프로젝트: dgreid/adhd
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;
}
예제 #10
0
파일: cras_fmt_conv.c 프로젝트: dgreid/adhd
/* 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;
}
예제 #11
0
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;
}
예제 #12
0
파일: cras_fmt_conv.c 프로젝트: dgreid/adhd
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;
}
예제 #13
0
파일: cras_hfp_info.c 프로젝트: dgreid/adhd
int hfp_buf_size(struct hfp_info *info, struct cras_iodev *dev)
{
	return info->playback_buf->used_size / cras_get_format_bytes(dev->format);
}