#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <alsa/asoundlib.h>
#include <speex/speex.h>


static const int BUF_SIZE = 128000;

int main (int argc, char *argv[])
{
	snd_pcm_t *play_handle;
	snd_pcm_t *capture_handle;
	snd_pcm_hw_params_t *play_params;
	snd_pcm_hw_params_t *capture_params;
	
	SpeexBits speex_enc_buffer;
	SpeexBits speex_dec_buffer;
	void *speex_encoder;
	void *speex_decoder;
	int speex_enc_frmsize, speex_dec_frmsize;

	int err, i, tmp;
	unsigned int play_rate = 32000;
	unsigned int capture_rate = 32000;
	short buffer[BUF_SIZE];
	int buffer_size = 0;
	float float_buffer[BUF_SIZE];
	int float_buffer_size = 0;
	char speex_buffer[BUF_SIZE];
	int speex_buffer_size = 0;

	printf("Configuring the playback device...\n");	
	// Configure the playback interface.
	if ((err = snd_pcm_open(&play_handle, "plughw:0,0", 
		SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
		fprintf(stderr, "Unable to open audio device (%s)\n",
			snd_strerror(err));
		return err;
	}

	if ((err = snd_pcm_hw_params_malloc(&play_params)) < 0) {
		fprintf(stderr, "Unable to hardware parameters structure (%s)\n",
			snd_strerror(err));
		return err;
	}

	if ((err = snd_pcm_hw_params_any(play_handle, play_params)) < 0) {
		fprintf(stderr, "Unable to initialize hardware parameters structure (%s).\n", snd_strerror(err));
		return err;
	}

	if ((err = snd_pcm_hw_params_set_access(play_handle, play_params,
		SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
		fprintf(stderr, "Unable to set access type (%s)\n", snd_strerror(err));
		return err;
	}

	if ((err = snd_pcm_hw_params_set_format(play_handle, play_params,
		SND_PCM_FORMAT_S16_LE)) < 0) {
		fprintf(stderr, "Unable to set sample format (%s)\n",
			snd_strerror(err));
		return err;
	}

	if ((err = snd_pcm_hw_params_set_rate_near(play_handle, play_params,
		&play_rate, 0)) < 0) {
		fprintf(stderr, "Unable to set sample rate (%s)\n", snd_strerror(err));
		return err;
	}
	
	printf("Sample rate is: %u Hz\n", play_rate);
	
	if ((err = snd_pcm_hw_params_set_channels(play_handle, play_params, 1))
		< 0) {
		fprintf(stderr, "Unable to set channel count (%s)\n",
			snd_strerror(err));
			return err;
	}
	
	if ((err = snd_pcm_hw_params(play_handle, play_params)) < 0) {
		fprintf(stderr, "Unable to set hardware parameters (%s).",
			snd_strerror(err));
		return err;
	}
	
	snd_pcm_hw_params_free(play_params);

	if ((err = snd_pcm_prepare(play_handle)) < 0) {
		fprintf(stderr, "Unable to prepare audio interface for use (%s)\n",
			snd_strerror(err));
		return err;
	}
	
	printf("Configuring the capture device...\n");
	// Configure the recording interface handle.
	if ((err = snd_pcm_open(&capture_handle, "plughw:0,0", 
		SND_PCM_STREAM_CAPTURE, 0)) < 0) {
		fprintf(stderr, "Unable to open audio device (%s)\n",
			snd_strerror(err));
		return err;
	}

	if ((err = snd_pcm_hw_params_malloc(&capture_params)) < 0) {
		fprintf(stderr, "Unable to hardware parameters structure (%s)\n",
			snd_strerror(err));
		return err;
	}
	
	if ((err = snd_pcm_hw_params_any(capture_handle, capture_params)) < 0) {
		fprintf(stderr, "Unable to initialize hardware parameters structure (%s).\n", snd_strerror(err));
		return err;
	}

	if ((err = snd_pcm_hw_params_set_access(capture_handle, capture_params,
		SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
		fprintf(stderr, "Unable to set access type (%s)\n", snd_strerror(err));
		return err;
	}

	if ((err = snd_pcm_hw_params_set_format(capture_handle, capture_params,
		SND_PCM_FORMAT_S16_LE)) < 0) {
		fprintf(stderr, "Unable to set sample format (%s)\n",
			snd_strerror(err));
		return err;
	}

	if ((err = snd_pcm_hw_params_set_rate_near(capture_handle, capture_params,
		&capture_rate, 0)) < 0) {
		fprintf(stderr, "Unable to set sample rate (%s)\n", snd_strerror(err));
		return err;
	}
	
	printf("Sample rate is: %u Hz\n", capture_rate);
	
	if ((err = snd_pcm_hw_params_set_channels(capture_handle, capture_params, 1))
		< 0) {
		fprintf(stderr, "Unable to set channel count (%s)\n",
			snd_strerror(err));
			return err;
	}
	
	if ((err = snd_pcm_hw_params(capture_handle, capture_params)) < 0) {
		fprintf(stderr, "Unable to set hardware parameters (%s).",
			snd_strerror(err));
		return err;
	}
	
	snd_pcm_hw_params_free(capture_params);

	if ((err = snd_pcm_prepare(capture_handle)) < 0) {
		fprintf(stderr, "Unable to prepare audio interface for use (%s)\n",
			snd_strerror(err));
		return err;
	}
	
	printf("Capturing audio...\n");
	// Capture audio into the buffer
	if ((err = snd_pcm_readi(capture_handle, buffer, BUF_SIZE)) != BUF_SIZE) {
		if (err < 0) {
			fprintf(stderr, "Audio capture failed (%s)\n", snd_strerror(err));
			return err;
		} else {
			fprintf(stderr, "Warning: Audio capture is misbehaving!\n");
			buffer_size = err;
		}
	}
	
	printf("Buffer sample, signed 16-bit integer: %d\n", buffer[0]);

	speex_bits_init(&speex_enc_buffer);
	speex_bits_init(&speex_dec_buffer);
	speex_encoder = speex_encoder_init(&speex_uwb_mode);
	speex_decoder = speex_decoder_init(&speex_uwb_mode);
	speex_encoder_ctl(speex_encoder, SPEEX_GET_FRAME_SIZE, &speex_enc_frmsize);
	speex_decoder_ctl(speex_decoder, SPEEX_GET_FRAME_SIZE, &speex_dec_frmsize);
	
	printf("Speex encoder frame size is: %d bytes.\n", speex_enc_frmsize);
	printf("Speex decoder frame size is: %d bytes.\n", speex_dec_frmsize);
	
	printf("Converting data to floating point...\n");
	float_buffer_size = buffer_size = 128000;
	for (i = 0; i < buffer_size; ++i) {
		
		float_buffer[i] = (float) buffer[i];
	}
	
	printf("Buffer sample, float: %f\n", float_buffer[0]);
	
	printf("Encoding audio to Speex format...\n");
	
	tmp = 0;
	for (i = 0; i < 200; ++i) {
		// Reset the encoding buffer
		speex_bits_reset(&speex_enc_buffer);
		
		// Compress one frame of Speex
		speex_encode(speex_encoder, &float_buffer[i * 640],
			&speex_enc_buffer);
		
		assert(speex_buffer_size < BUF_SIZE);
		tmp = speex_bits_write(&speex_enc_buffer,
			speex_buffer + speex_buffer_size, BUF_SIZE - tmp);
		speex_buffer_size += tmp;
		//printf("Encoded a Speex frame, size is : %d bytes.\n", tmp);	
	}
	
	printf("The complete speex data chunk is now %d bytes.\n",
		speex_buffer_size);


	// ===== Speex decoding =====
	printf("Decoding Speex to normal audio data...\n");

	float_buffer_size = 0;
	for (i = 0; i < speex_buffer_size; i += 90) {

		speex_bits_reset(&speex_dec_buffer);
		
		// Load one Speex frame from the data chunk.
		speex_bits_read_from(&speex_dec_buffer, speex_buffer + i, 90);
		//printf("Wrote %d bytes of speex-compressed data to buffer.\n", tmp);
		
		// Write the decoded audio to the float buffer
		float_buffer_size += 640;
		speex_decode(speex_decoder, &speex_dec_buffer,
			float_buffer + float_buffer_size);
		
		//printf("Decompressed float buffer size is %d bytes.\n", float_buffer_size);	
	}
	
	printf("Decompressed %d bytes of data into buffer.\n", float_buffer_size);
	
	printf("Buffer sample, float: %f\n", float_buffer[0]);
	
	printf("Converting data from floating point...\n");
	buffer_size = float_buffer_size;
	for (i = 0; i < float_buffer_size; ++i) {
		buffer[i] = (short) float_buffer[i];
	}

	printf("Buffer size is %d bytes.\n", buffer_size);
	
	printf("Playing back audio...\n");
	if ((err = snd_pcm_writei(play_handle, buffer, buffer_size))
			!= buffer_size) {
		fprintf(stderr, "Audio playback failed (%s)\n", snd_strerror(err));
		return err;
	}
	
	return 0;
}

