/* Copyright (c) 2014 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.
 *
 * The dev_stream structure is used for mapping streams to a device.  In
 * addition to the rstream, other mixing information is stored here.
 */

#ifndef DEV_STREAM_H_
#define DEV_STREAM_H_

#include <stdint.h>
#include <sys/time.h>

#include "cras_types.h"
#include "cras_rstream.h"

struct cras_audio_area;
struct cras_fmt_conv;
struct cras_iodev;

/*
 * Linked list of streams of audio from/to a client.
 * Args:
 *    dev_id - Index of the hw device.
 *    stream - The rstream attached to a device.
 *    conv - Sample rate or format converter.
 *    conv_buffer - The buffer for converter if needed.
 *    conv_buffer_size_frames - Size of conv_buffer in frames.
 *    dev_rate - Sampling rate of device. This is set when dev_stream is
 *               created.
 */
struct dev_stream {
	unsigned int dev_id;
	struct cras_rstream *stream;
	struct cras_fmt_conv *conv;
	struct byte_buffer *conv_buffer;
	struct cras_audio_area *conv_area;
	unsigned int conv_buffer_size_frames;
	size_t dev_rate;
	struct dev_stream *prev, *next;
};

struct dev_stream *dev_stream_create(struct cras_rstream *stream,
				     unsigned int dev_id,
				     const struct cras_audio_format *dev_fmt,
				     void *dev_ptr, struct timespec *cb_ts);
void dev_stream_destroy(struct dev_stream *dev_stream);

/*
 * Update the estimated sample rate of the device. For multiple active
 * devices case, the linear resampler will be configured by the estimated
 * rate ration of the master device and the current active device the
 * rstream attaches to.
 *
 * Args:
 *    dev_stream - The structure holding the stream.
 *    dev_rate - The sample rate device is using.
 *    dev_rate_ratio - The ratio of estimated rate and used rate.
 *    master_rate_ratio - The ratio of estimated rate and used rate of
 *        master device.
 *    coarse_rate_adjust - The flag to indicate the direction device
 *        sample rate should adjust to.
 */
void dev_stream_set_dev_rate(struct dev_stream *dev_stream,
			     unsigned int dev_rate,
			     double dev_rate_ratio,
			     double master_rate_ratio,
			     int coarse_rate_adjust);

/*
 * Renders count frames from shm into dst.  Updates count if anything is
 * written. If it's muted and the only stream zero memory.
 * Args:
 *    dev_stream - The struct holding the stream to mix.
 *    format - The format of the audio device.
 *    dst - The destination buffer for mixing.
 *    num_to_write - The number of frames written.
 */
int dev_stream_mix(struct dev_stream *dev_stream,
		   const struct cras_audio_format *fmt,
		   uint8_t *dst,
		   unsigned int num_to_write);

/*
 * Reads froms from the source into the dev_stream.
 * Args:
 *    dev_stream - The struct holding the stream to mix to.
 *    area - The area to copy audio from.
 *    area_offset - The offset at which to start reading from area.
 *    software_gain_scaler - The software gain scaler.
 */
unsigned int dev_stream_capture(struct dev_stream *dev_stream,
			const struct cras_audio_area *area,
			unsigned int area_offset,
			float software_gain_scaler);

/* Returns the number of iodevs this stream has attached to. */
int dev_stream_attached_devs(const struct dev_stream *dev_stream);

/* Updates the number of queued frames in dev_stream. */
void dev_stream_update_frames(const struct dev_stream *dev_stream);

/*
 * Returns the number of playback frames queued in shared memory.  This is a
 * post-format-conversion number.  If the stream is 24k with 10 frames queued
 * and the device is playing at 48k, 20 will be returned.
 */
int dev_stream_playback_frames(const struct dev_stream *dev_stream);

/*
 * Returns the number of frames free to be written to in a capture stream.  This
 * number is also post format conversion, similar to playback_frames above.
 */
unsigned int dev_stream_capture_avail(const struct dev_stream *dev_stream);

/*
 * Returns the callback threshold, if necesary converted from a stream frame
 * count to a device frame count.
 */
unsigned int dev_stream_cb_threshold(const struct dev_stream *dev_stream);

/*
 * If enough samples have been captured, post them to the client.
 * TODO(dgreid) - see if this function can be eliminated.
 */
int dev_stream_capture_update_rstream(struct dev_stream *dev_stream);

/* Updates the read buffer pointers for the stream. */
int dev_stream_playback_update_rstream(struct dev_stream *dev_stream);

/* Fill ts with the time the playback sample will be played. */
void cras_set_playback_timestamp(size_t frame_rate,
				 size_t frames,
				 struct cras_timespec *ts);

/* Fill ts with the time the capture sample was recorded. */
void cras_set_capture_timestamp(size_t frame_rate,
				size_t frames,
				struct cras_timespec *ts);

/* Fill shm ts with the time the playback sample will be played or the capture
 * sample was captured depending on the direction of the stream.
 * Args:
 *    delay_frames - The delay reproted by the device, in frames at the device's
 *      sample rate.
 */
void dev_stream_set_delay(const struct dev_stream *dev_stream,
			  unsigned int delay_frames);

/* Returns if it's okay to request playback samples for this stream. */
int dev_stream_can_fetch(struct dev_stream *dev_stream);

/* Ask the client for cb_threshold samples of audio to play. */
int dev_stream_request_playback_samples(struct dev_stream *dev_stream,
					const struct timespec *now);

/*
 * Gets the wake up time for a dev_stream.
 * For an input stream, it considers both needed samples and proper time
 * interval between each callbacks.
 * Args:
 *   dev_stream[in]: The dev_stream to check wake up time.
 *   curr_level[in]: The current level of device.
 *   wake_time_out[out]: A timespec for wake up time.
 * Returns:
 *   0 on success; negative error code on failure.
 */
int dev_stream_wake_time(struct dev_stream *dev_stream,
			 unsigned int curr_level,
			 struct timespec *level_tstamp,
			 struct timespec *wake_time_out);

/*
 * Returns a non-negative fd if the fd is expecting a message and should be
 * added to the list of descriptors to poll.
 */
int dev_stream_poll_stream_fd(const struct dev_stream *dev_stream);

static inline const struct timespec *
dev_stream_next_cb_ts(struct dev_stream *dev_stream)
{
	if (dev_stream->stream->flags & USE_DEV_TIMING)
		return NULL;

	return &dev_stream->stream->next_cb_ts;
}

static inline const struct timespec *
dev_stream_sleep_interval_ts(struct dev_stream *dev_stream)
{
	return &dev_stream->stream->sleep_interval_ts;
}

#endif /* DEV_STREAM_H_ */