From b484b2aa3ae9b8512eedac1f70a0d0a20c2ce672 Mon Sep 17 00:00:00 2001
From: Paul Kocialkowski <contact@paulk.fr>
Date: Wed, 31 Oct 2012 12:57:33 +0100
Subject: Output/Input: Use resampler when required

Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
---
 audio_hw.h  |  10 +-
 audio_in.c  | 317 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 audio_out.c | 229 ++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 511 insertions(+), 45 deletions(-)

diff --git a/audio_hw.h b/audio_hw.h
index c7e6d05..a06ef9b 100644
--- a/audio_hw.h
+++ b/audio_hw.h
@@ -22,6 +22,8 @@
 #include <hardware/audio.h>
 #include <system/audio.h>
 
+#include <audio_utils/resampler.h>
+
 #ifdef YAMAHA_MC1N2_AUDIO
 #include <yamaha-mc1n2-audio.h>
 #endif
@@ -41,6 +43,8 @@ struct tinyalsa_audio_stream_out {
 
 	audio_devices_t device_current;
 
+	struct resampler_itfe *resampler;
+
 	struct pcm *pcm;
 	int standby;
 };
@@ -50,13 +54,17 @@ struct tinyalsa_audio_stream_in {
 	struct tinyalsa_audio_device *device;
 
 	struct tinyalsa_mixer_io_props *mixer_props;
-
 	int rate;
 	audio_channels_t channels;
 	audio_format_t format;
 
 	audio_devices_t device_current;
 
+	struct resampler_itfe *resampler;
+	struct resampler_buffer_provider buffer_provider;
+	void *buffer;
+	int frames_left;
+
 	struct pcm *pcm;
 	int standby;
 };
diff --git a/audio_in.c b/audio_in.c
index 560aa25..3933bbf 100644
--- a/audio_in.c
+++ b/audio_in.c
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
- * 
+ *
  * This is based on Galaxy Nexus audio.primary.tuna implementation:
  * Copyright 2011, The Android Open-Source Project
  *
@@ -31,6 +31,7 @@
 
 #define EFFECT_UUID_NULL EFFECT_UUID_NULL_IN
 #define EFFECT_UUID_NULL_STR EFFECT_UUID_NULL_STR_IN
+#include <audio_utils/resampler.h>
 #include "audio_hw.h"
 #include "mixer.h"
 
@@ -73,6 +74,9 @@ int audio_in_pcm_open(struct tinyalsa_audio_stream_in *stream_in)
 
 	stream_in->pcm = pcm;
 
+	if(stream_in->resampler != NULL)
+		stream_in->resampler->reset(stream_in->resampler);
+
 	return 0;
 }
 
@@ -108,12 +112,239 @@ int audio_in_set_route(struct tinyalsa_audio_stream_in *stream_in,
 	return 0;
 }
 
+int audio_in_resampler_open(struct tinyalsa_audio_stream_in *stream_in)
+{
+	int rc;
+
+	if(stream_in == NULL)
+		return -1;
+
+	rc = create_resampler(stream_in->mixer_props->rate,
+		stream_in->rate,
+		popcount(stream_in->mixer_props->channels),
+		RESAMPLER_QUALITY_DEFAULT,
+		&stream_in->buffer_provider,
+		&stream_in->resampler);
+	if(rc < 0 || stream_in->resampler == NULL) {
+		LOGE("Failed to create resampler");
+		return -1;
+	}
+
+	stream_in->buffer = malloc(stream_in->mixer_props->period_size *
+		popcount(stream_in->mixer_props->channels) *
+		audio_bytes_per_sample(stream_in->mixer_props->format));
+
+	return 0;
+}
+
+void audio_in_resampler_close(struct tinyalsa_audio_stream_in *stream_in)
+{
+	if(stream_in == NULL)
+		return;
+
+	if(stream_in->resampler != NULL) {
+		release_resampler(stream_in->resampler);
+		stream_in->resampler = NULL;
+	}
+
+	if(stream_in->buffer != NULL) {
+		free(stream_in->buffer);
+		stream_in->buffer = NULL;
+	}
+}
+
+int audio_in_get_next_buffer(struct resampler_buffer_provider *buffer_provider,
+	struct resampler_buffer *buffer)
+{
+	struct tinyalsa_audio_stream_in *stream_in;
+	int rc;
+
+	if(buffer_provider == NULL || buffer == NULL)
+		return -1;
+
+	stream_in = (struct tinyalsa_audio_stream_in *) ((void *) buffer_provider -
+		offsetof(struct tinyalsa_audio_stream_in, buffer_provider));
+
+	if(stream_in->frames_left == 0) {
+		if(stream_in->pcm == NULL || !pcm_is_ready(stream_in->pcm)) {
+			LOGE("pcm device is not ready");
+			goto error_pcm;
+		}
+
+		rc = pcm_read(stream_in->pcm, stream_in->buffer,
+			stream_in->mixer_props->period_size *
+			popcount(stream_in->mixer_props->channels) *
+			audio_bytes_per_sample(stream_in->mixer_props->format));
+		if(rc != 0) {
+			LOGE("pcm read failed!");
+			goto error_pcm;
+		}
+
+		stream_in->frames_left = stream_in->mixer_props->period_size;
+	}
+
+	buffer->frame_count = (buffer->frame_count > stream_in->frames_left) ?
+		stream_in->frames_left : buffer->frame_count;
+
+	buffer->raw = stream_in->buffer +
+		(stream_in->mixer_props->period_size - stream_in->frames_left) *
+		popcount(stream_in->mixer_props->channels) *
+		audio_bytes_per_sample(stream_in->mixer_props->format);
+
+	return 0;
+
+error_pcm:
+	buffer->raw = NULL;
+	buffer->frame_count = 0;
+	return -1;
+}
+
+void audio_in_release_buffer(struct resampler_buffer_provider *buffer_provider,
+	struct resampler_buffer *buffer)
+{
+	struct tinyalsa_audio_stream_in *stream_in;
+
+	if(buffer_provider == NULL || buffer == NULL)
+		return;
+
+	stream_in = (struct tinyalsa_audio_stream_in *) ((void *) buffer_provider -
+		offsetof(struct tinyalsa_audio_stream_in, buffer_provider));
+
+	stream_in->frames_left -= buffer->frame_count;
+}
+
+int audio_in_read_process(struct tinyalsa_audio_stream_in *stream_in, void *buffer, int size)
+{
+	size_t frames_out;
+
+	size_t frames_in;
+	int size_in;
+	void *buffer_in = NULL;
+
+	int frames_out_resampler;
+	int size_out_resampler;
+	void *buffer_out_resampler = NULL;
+
+	int frames_out_read;
+	int size_out_read;
+	void *buffer_out_read = NULL;
+
+	int frames_out_channels;
+	int size_out_channels;
+	void *buffer_out_channels = NULL;
+
+	int i, j;
+	int rc;
+
+	if(stream_in == NULL || buffer == NULL || size <= 0)
+		return -1;
+
+	frames_in = size / audio_stream_frame_size((struct audio_stream *) stream_in);
+
+	if(stream_in->resampler != NULL) {
+		frames_out_resampler = frames_in;
+		size_out_resampler = frames_out_resampler * popcount(stream_in->mixer_props->channels) * audio_bytes_per_sample(stream_in->mixer_props->format);
+		buffer_out_resampler = calloc(1, size_out_resampler);
+
+		frames_out = 0;
+		while(frames_out < frames_out_resampler) {
+			frames_in = frames_out_resampler - frames_out;
+			stream_in->resampler->resample_from_provider(stream_in->resampler,
+				buffer_out_resampler + (size_out_resampler / frames_out_resampler) * frames_out,
+				&frames_in);
+
+			frames_out += frames_in;
+		}
+
+		frames_in = frames_out_resampler;
+		size_in = size_out_resampler;
+		buffer_in = buffer_out_resampler;
+	} else {
+		frames_out_read = frames_in;
+		size_out_read = frames_out_read * popcount(stream_in->mixer_props->channels) * audio_bytes_per_sample(stream_in->mixer_props->format);
+		buffer_out_read = calloc(1, size_out_read);
+
+		if(stream_in->pcm == NULL || !pcm_is_ready(stream_in->pcm)) {
+			LOGE("pcm device is not ready");
+			goto error;
+		}
+
+		rc = pcm_read(stream_in->pcm, buffer_out_read, size_out_read);
+		if(rc != 0) {
+			LOGE("pcm read failed!");
+			goto error;
+		}
+
+		frames_in = frames_out_read;
+		size_in = size_out_read;
+		buffer_in = buffer_out_read;
+	}
+
+	if(buffer_in == NULL)
+		goto error;
+
+	//FIXME: This is only for PCM 16
+	if(popcount(stream_in->channels) < popcount(stream_in->mixer_props->channels)) {
+		frames_out_channels = frames_in;
+		size_out_channels = frames_out_channels * audio_stream_frame_size((struct audio_stream *) stream_in);
+		buffer_out_channels = calloc(1, size_out_channels);
+
+		int channels_count_in = popcount(stream_in->mixer_props->channels);
+		int channels_count_out = popcount(stream_in->channels);
+		int ratio = channels_count_in / channels_count_out;
+
+		int16_t *byte_in = (int16_t *) buffer_in;
+		int16_t *byte_out = (int16_t *) buffer_out_channels;
+		int16_t byte;
+
+		for(i=0 ; i < frames_out_channels * channels_count_out; i++) {
+			byte = 0;
+			for(j=0 ; j < ratio ; j++) {
+				byte += *byte_in / ratio;
+				byte_in++;
+			}
+
+			*byte_out = byte;
+			byte_out++;
+		}
+
+		frames_in = frames_out_channels;
+		size_in = size_out_channels;
+		buffer_in = buffer_out_channels;
+	} else if(popcount(stream_in->channels) != popcount(stream_in->mixer_props->channels)) {
+		LOGE("Asked for more channels than hardware can provide!");
+		goto error;
+	}
+
+	if(buffer_in != NULL)
+		memcpy(buffer, buffer_in, size);
+
+	if(buffer_out_resampler != NULL)
+		free(buffer_out_resampler);
+	if(buffer_out_read != NULL)
+		free(buffer_out_read);
+	if(buffer_out_channels != NULL)
+		free(buffer_out_channels);
+
+	return 0;
+
+error:
+	if(buffer_out_resampler != NULL)
+		free(buffer_out_resampler);
+	if(buffer_out_read != NULL)
+		free(buffer_out_read);
+	if(buffer_out_channels != NULL)
+		free(buffer_out_channels);
+
+	return -1;
+}
+
 static uint32_t audio_in_get_sample_rate(const struct audio_stream *stream)
 {
 	struct tinyalsa_audio_stream_in *stream_in;
 
 	if(stream == NULL)
-		return -1;
+		return 0;
 
 	stream_in = (struct tinyalsa_audio_stream_in *) stream;
 
@@ -131,8 +362,16 @@ static int audio_in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
 
 	stream_in = (struct tinyalsa_audio_stream_in *) stream;
 
-	// FIXME: If rate is different, change resampler
-	stream_in->rate = rate;
+	if(stream_in->rate != (int) rate) {
+		stream_in->rate = rate;
+
+		if(stream_in->rate != stream_in->mixer_props->rate) {
+			audio_in_resampler_close(stream_in);
+			audio_in_resampler_open(stream_in);
+
+			stream_in->standby = 1;
+		}
+	}
 
 	return 0;
 }
@@ -148,7 +387,7 @@ static size_t audio_in_get_buffer_size(const struct audio_stream *stream)
 	stream_in = (struct tinyalsa_audio_stream_in *) stream;
 
 	size = (stream_in->mixer_props->period_size * stream_in->rate) /
-		stream_in->rate;
+		stream_in->mixer_props->rate;
 	size = ((size + 15) / 16) * 16;
 	size = size * audio_stream_frame_size((struct audio_stream *) stream);
 
@@ -190,8 +429,12 @@ static int audio_in_set_format(struct audio_stream *stream, int format)
 
 	stream_in = (struct tinyalsa_audio_stream_in *) stream;
 
-	// FIXME: If format is different, change resampler
-	stream_in->format = format;
+	if(stream_in->format != (audio_format_t) format) {
+		stream_in->format = format;
+
+		if(stream_in->format != stream_in->mixer_props->format)
+			stream_in->standby = 1;
+	}
 
 	return 0;
 }
@@ -329,14 +572,9 @@ static ssize_t audio_in_read(struct audio_stream_in *stream,
 		stream_in->standby = 0;
 	}
 
-	if(stream_in->pcm == NULL || !pcm_is_ready(stream_in->pcm)) {
-		LOGE("pcm device is not ready");
-		return -1;
-	}
-
-	rc = pcm_read(stream_in->pcm, (void *) buffer, (int) bytes);
-	if(rc != 0) {
-		LOGE("pcm read failed!");
+	rc = audio_in_read_process(stream_in, buffer, (int) bytes);
+	if(rc < 0) {
+		LOGE("Read and process failed!");
 		return -1;
 	}
 
@@ -379,6 +617,9 @@ void audio_hw_close_input_stream(struct audio_hw_device *dev,
 
 	stream_in = (struct tinyalsa_audio_stream_in *) stream;
 
+	if(stream_in != NULL && stream_in->resampler != NULL)
+		audio_in_resampler_close(stream_in);
+
 #ifdef YAMAHA_MC1N2_AUDIO
 	if(stream_in != NULL && !stream_in->standby)
 		yamaha_mc1n2_audio_input_stop(stream_in->device->mc1n2_pdata);
@@ -409,7 +650,7 @@ int audio_hw_open_input_stream(struct audio_hw_device *dev,
 	LOGD("%s(%p, %d, %p, %p, %p, %d, %p)",
 		__func__, dev, devices, format, channels, sample_rate, acoustics, stream_in);
 
-	if(dev == NULL || stream_in == NULL)
+	if(dev == NULL || format == NULL || channels == NULL || sample_rate == NULL || stream_in == NULL)
 		return -EINVAL;
 
 	tinyalsa_audio_device = (struct tinyalsa_audio_device *) dev;
@@ -447,12 +688,42 @@ int audio_hw_open_input_stream(struct audio_hw_device *dev,
 	if(tinyalsa_audio_stream_in->mixer_props == NULL)
 		goto error_stream;
 
-	tinyalsa_audio_stream_in->rate =
-		tinyalsa_audio_stream_in->mixer_props->rate;
-	tinyalsa_audio_stream_in->channels =
-		tinyalsa_audio_stream_in->mixer_props->channels;
-	tinyalsa_audio_stream_in->format =
-		tinyalsa_audio_stream_in->mixer_props->format;
+	// Default values
+	if(tinyalsa_audio_stream_in->mixer_props->rate == 0)
+		tinyalsa_audio_stream_in->mixer_props->rate = 44100;
+	if(tinyalsa_audio_stream_in->mixer_props->channels == 0)
+		tinyalsa_audio_stream_in->mixer_props->channels = AUDIO_CHANNEL_IN_STEREO;
+	if(tinyalsa_audio_stream_in->mixer_props->format == 0)
+		tinyalsa_audio_stream_in->mixer_props->format = AUDIO_FORMAT_PCM_16_BIT;
+
+	if(*sample_rate == 0)
+		tinyalsa_audio_stream_in->rate =
+			tinyalsa_audio_stream_in->mixer_props->rate;
+	else
+		tinyalsa_audio_stream_in->rate = *sample_rate;
+	if(*channels == 0)
+		tinyalsa_audio_stream_in->channels =
+			tinyalsa_audio_stream_in->mixer_props->channels;
+	else
+		tinyalsa_audio_stream_in->channels = *channels;
+	if(*format == 0)
+		tinyalsa_audio_stream_in->format =
+			tinyalsa_audio_stream_in->mixer_props->format;
+	else
+		tinyalsa_audio_stream_in->format = *format;
+
+        tinyalsa_audio_stream_in->buffer_provider.get_next_buffer =
+		audio_in_get_next_buffer;
+        tinyalsa_audio_stream_in->buffer_provider.release_buffer =
+		audio_in_release_buffer;
+
+	if(tinyalsa_audio_stream_in->rate != tinyalsa_audio_stream_in->mixer_props->rate) {
+		rc = audio_in_resampler_open(tinyalsa_audio_stream_in);
+		if(rc < 0) {
+			LOGE("Unable to open resampler!");
+			goto error_stream;
+		}
+	}
 
 	*sample_rate = (uint32_t) tinyalsa_audio_stream_in->rate;
 	*channels = (uint32_t) tinyalsa_audio_stream_in->channels;
@@ -482,6 +753,8 @@ int audio_hw_open_input_stream(struct audio_hw_device *dev,
 
 error_stream:
 	*stream_in = NULL;
+	if(tinyalsa_audio_stream_in->resampler != NULL)
+		audio_in_resampler_close(tinyalsa_audio_stream_in);
 	free(tinyalsa_audio_stream_in);
 	tinyalsa_audio_device->stream_in = NULL;
 
diff --git a/audio_out.c b/audio_out.c
index 9b814a6..3438f53 100644
--- a/audio_out.c
+++ b/audio_out.c
@@ -35,6 +35,7 @@
 
 #define EFFECT_UUID_NULL EFFECT_UUID_NULL_OUT
 #define EFFECT_UUID_NULL_STR EFFECT_UUID_NULL_STR_OUT
+#include <audio_utils/resampler.h>
 #include "audio_hw.h"
 #include "mixer.h"
 
@@ -77,6 +78,9 @@ int audio_out_pcm_open(struct tinyalsa_audio_stream_out *stream_out)
 
 	stream_out->pcm = pcm;
 
+	if(stream_out->resampler != NULL)
+		stream_out->resampler->reset(stream_out->resampler);
+
 	return 0;
 }
 
@@ -111,12 +115,151 @@ int audio_out_set_route(struct tinyalsa_audio_stream_out *stream_out,
 	return 0;
 }
 
+int audio_out_resampler_open(struct tinyalsa_audio_stream_out *stream_out)
+{
+	int rc;
+
+	if(stream_out == NULL)
+		return -1;
+
+	rc = create_resampler(stream_out->rate,
+		stream_out->mixer_props->rate,
+		popcount(stream_out->channels),
+		RESAMPLER_QUALITY_DEFAULT,
+		NULL,
+		&stream_out->resampler);
+	if(rc < 0 || stream_out->resampler == NULL) {
+		LOGE("Failed to create resampler");
+		return -1;
+	}
+
+	return 0;
+}
+
+void audio_out_resampler_close(struct tinyalsa_audio_stream_out *stream_out)
+{
+	if(stream_out == NULL)
+		return;
+
+	if(stream_out->resampler != NULL) {
+		release_resampler(stream_out->resampler);
+		stream_out->resampler = NULL;
+	}
+}
+
+int audio_out_write_process(struct tinyalsa_audio_stream_out *stream_out, void *buffer, int size)
+{
+	size_t frames_out;
+
+	size_t frames_in;
+	int size_in;
+	void *buffer_in = NULL;
+
+	int frames_out_resampler;
+	int size_out_resampler;
+	void *buffer_out_resampler = NULL;
+
+	int frames_out_channels;
+	int size_out_channels;
+	void *buffer_out_channels = NULL;
+
+	int i, j;
+	int rc;
+
+	if(stream_out == NULL || buffer == NULL || size <= 0)
+		return -1;
+
+	frames_in = size / audio_stream_frame_size((struct audio_stream *) stream_out);
+	size_in = size;
+	buffer_in = buffer;
+
+	if(stream_out->resampler != NULL) {
+		frames_out_resampler = (frames_in * stream_out->mixer_props->rate) /
+			stream_out->rate;
+		frames_out_resampler = ((frames_out_resampler + 15) / 16) * 16;
+		size_out_resampler = frames_out_resampler * audio_stream_frame_size((struct audio_stream *) stream_out);
+		buffer_out_resampler = calloc(1, size_out_resampler);
+
+		frames_out = frames_out_resampler;
+		stream_out->resampler->resample_from_input(stream_out->resampler,
+			buffer_in, &frames_in, buffer_out_resampler, &frames_out);
+
+		frames_in = frames_out;
+		size_in = frames_out * audio_stream_frame_size((struct audio_stream *) stream_out);
+		buffer_in = buffer_out_resampler;
+	}
+
+	if(buffer_in == NULL)
+		goto error;
+
+	//FIXME: This is only for PCM 16
+	if(popcount(stream_out->mixer_props->channels) < popcount(stream_out->channels)) {
+		frames_out_channels = frames_in;
+		size_out_channels = frames_out_channels * popcount(stream_out->mixer_props->channels) * audio_bytes_per_sample(stream_out->mixer_props->format);
+		buffer_out_channels = calloc(1, size_out_channels);
+
+		int channels_count_in = popcount(stream_out->channels);
+		int channels_count_out = popcount(stream_out->mixer_props->channels);
+		int ratio = channels_count_in / channels_count_out;
+
+		int16_t *byte_in = (int16_t *) buffer_in;
+		int16_t *byte_out = (int16_t *) buffer_out_channels;
+		int16_t byte;
+
+		for(i=0 ; i < frames_out_channels * channels_count_out; i++) {
+			byte = 0;
+			for(j=0 ; j < ratio ; j++) {
+				byte += *byte_in / ratio;
+				byte_in++;
+			}
+
+			*byte_out = byte;
+			byte_out++;
+		}
+
+		frames_in = frames_out_channels;
+		size_in = size_out_channels;
+		buffer_in = buffer_out_channels;
+	} else if(popcount(stream_out->channels) != popcount(stream_out->mixer_props->channels)) {
+		LOGE("Asked for more channels than software can provide!");
+		goto error;
+	}
+
+	if(buffer_in != NULL) {
+		if(stream_out->pcm == NULL || !pcm_is_ready(stream_out->pcm)) {
+			LOGE("pcm device is not ready");
+			goto error;
+		}
+
+		rc = pcm_write(stream_out->pcm, buffer_in, size_in);
+		if(rc != 0) {
+			LOGE("pcm write failed!");
+			goto error;
+		}
+	}
+
+	if(buffer_out_resampler != NULL)
+		free(buffer_out_resampler);
+	if(buffer_out_channels != NULL)
+		free(buffer_out_channels);
+
+	return 0;
+
+error:
+	if(buffer_out_resampler != NULL)
+		free(buffer_out_resampler);
+	if(buffer_out_channels != NULL)
+		free(buffer_out_channels);
+
+	return -1;
+}
+
 static uint32_t audio_out_get_sample_rate(const struct audio_stream *stream)
 {
 	struct tinyalsa_audio_stream_out *stream_out;
 
 	if(stream == NULL)
-		return -1;
+		return 0;
 
 	stream_out = (struct tinyalsa_audio_stream_out *) stream;
 
@@ -134,8 +277,16 @@ static int audio_out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
 
 	stream_out = (struct tinyalsa_audio_stream_out *) stream;
 
-	// FIXME: If rate is different, change resampler
-	stream_out->rate = rate;
+	if(stream_out->rate != (int) rate) {
+		stream_out->rate = rate;
+
+		if(stream_out->rate != stream_out->mixer_props->rate) {
+			audio_out_resampler_close(stream_out);
+			audio_out_resampler_open(stream_out);
+
+			stream_out->standby = 1;
+		}
+	}
 
 	return 0;
 }
@@ -151,7 +302,7 @@ static size_t audio_out_get_buffer_size(const struct audio_stream *stream)
 	stream_out = (struct tinyalsa_audio_stream_out *) stream;
 
 	size = (stream_out->mixer_props->period_size * stream_out->rate) /
-		stream_out->rate;
+		stream_out->mixer_props->rate;
 	size = ((size + 15) / 16) * 16;
 	size = size * audio_stream_frame_size((struct audio_stream *) stream);
 
@@ -193,8 +344,12 @@ static int audio_out_set_format(struct audio_stream *stream, int format)
 
 	stream_out = (struct tinyalsa_audio_stream_out *) stream;
 
-	// FIXME: If format is different, change resampler
-	stream_out->format = format;
+	if(stream_out->format != (audio_format_t) format) {
+		stream_out->format = format;
+
+		if(stream_out->format != stream_out->mixer_props->format)
+			stream_out->standby = 1;
+	}
 
 	return 0;
 }
@@ -287,9 +442,21 @@ static char *audio_out_get_parameters(const struct audio_stream *stream, const c
 
 static uint32_t audio_out_get_latency(const struct audio_stream_out *stream)
 {
+	struct tinyalsa_audio_stream_out *stream_out;
+	uint32_t latency;
+
 	LOGD("%s(%p)", __func__, stream);
 
-	return 0;
+	if(stream == NULL)
+		return -1;
+
+	stream_out = (struct tinyalsa_audio_stream_out *) stream;
+
+	latency = (stream_out->mixer_props->period_size *
+		stream_out->mixer_props->period_count * 1000) /
+		stream_out->mixer_props->rate;
+
+	return latency;
 }
 
 static int audio_out_set_volume(struct audio_stream_out *stream, float left,
@@ -344,14 +511,9 @@ static ssize_t audio_out_write(struct audio_stream_out *stream,
 		stream_out->standby = 0;
 	}
 
-	if(stream_out->pcm == NULL || !pcm_is_ready(stream_out->pcm)) {
-		LOGE("pcm device is not ready");
-		return -1;
-	}
-
-	rc = pcm_write(stream_out->pcm, (void *) buffer, (int) bytes);
-	if(rc != 0) {
-		LOGE("pcm write failed!");
+	rc = audio_out_write_process(stream_out, (void *) buffer, (int) bytes);
+	if(rc < 0) {
+		LOGE("Process and write failed!");
 		return -1;
 	}
 
@@ -424,7 +586,7 @@ int audio_hw_open_output_stream(struct audio_hw_device *dev,
 	LOGD("%s(%p, %d, %p, %p, %p, %p)",
 		__func__, dev, devices, format, channels, sample_rate, stream_out);
 
-	if(dev == NULL || stream_out == NULL)
+	if(dev == NULL || format == NULL || channels == NULL || sample_rate == NULL || stream_out == NULL)
 		return -EINVAL;
 
 	tinyalsa_audio_device = (struct tinyalsa_audio_device *) dev;
@@ -463,12 +625,35 @@ int audio_hw_open_output_stream(struct audio_hw_device *dev,
 	if(tinyalsa_audio_stream_out->mixer_props == NULL)
 		goto error_stream;
 
-	tinyalsa_audio_stream_out->rate =
-		tinyalsa_audio_stream_out->mixer_props->rate;
-	tinyalsa_audio_stream_out->channels =
-		tinyalsa_audio_stream_out->mixer_props->channels;
-	tinyalsa_audio_stream_out->format =
-		tinyalsa_audio_stream_out->mixer_props->format;
+	// Default values
+	if(tinyalsa_audio_stream_out->mixer_props->rate == 0)
+		tinyalsa_audio_stream_out->mixer_props->rate = 44100;
+	if(tinyalsa_audio_stream_out->mixer_props->channels == 0)
+		tinyalsa_audio_stream_out->mixer_props->channels = AUDIO_CHANNEL_OUT_STEREO;
+	if(tinyalsa_audio_stream_out->mixer_props->format == 0)
+		tinyalsa_audio_stream_out->mixer_props->format = AUDIO_FORMAT_PCM_16_BIT;
+
+	//Default incoming data will always be 44100Hz, stereo, PCM 16
+	if(*sample_rate == 0)
+		tinyalsa_audio_stream_out->rate = 44100;
+	else
+		tinyalsa_audio_stream_out->rate = *sample_rate;
+	if(*channels == 0)
+		tinyalsa_audio_stream_out->channels = AUDIO_CHANNEL_OUT_STEREO;
+	else
+		tinyalsa_audio_stream_out->channels = *channels;
+	if(*format == 0)
+		tinyalsa_audio_stream_out->format = AUDIO_FORMAT_PCM_16_BIT;
+	else
+		tinyalsa_audio_stream_out->format = *format;
+
+	if(tinyalsa_audio_stream_out->rate != tinyalsa_audio_stream_out->mixer_props->rate) {
+		rc = audio_out_resampler_open(tinyalsa_audio_stream_out);
+		if(rc < 0) {
+			LOGE("Unable to open resampler!");
+			goto error_stream;
+		}
+	}
 
 	*sample_rate = (uint32_t) tinyalsa_audio_stream_out->rate;
 	*channels = (uint32_t) tinyalsa_audio_stream_out->channels;
-- 
cgit v1.1