/**
 * beep.c: beeper fuckin musicality bitches, beep buzz boop
 * 2014 by true
 *
 * ----
 *
 * TODO: remove freq lookup table, replace with freq defines by note (A4 = 440 for example)
 * TODO: let you know that the device we chose cannot play music; it is a buzzer.
 * TODO: do what you want 'cause a pirate is free
**/

#include <pirate.h>

#include "beep.h"
#include "../interface/gpio.h"


const tGPIO beep_pin = {GPIOA, GPIO_Pin_4, 4};

uint8_t  beep_buf_idx;
uint8_t  beep_play_idx;
uint16_t beep_dur_buf[BEEP_BUF_SIZE];
uint16_t beep_lev_buf[BEEP_BUF_SIZE];


/* beeper bullshit */
void beep_init()
{
	GPIO_InitTypeDef gpio;

	// set up beeper gpio struct
	gpio.GPIO_Mode = GPIO_Mode_OUT;
	gpio.GPIO_OType = GPIO_OType_PP;
	gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
	gpio.GPIO_Speed = GPIO_Speed_400KHz;
	gpio.GPIO_Pin = beep_pin.pin;

	// and set it up
	GPIO_Init(beep_pin.port, &gpio);

	// reset buffer index
	beep_buf_idx = 0;
}

void beep_update()
{
	uint16_t duration;
	uint16_t level;

	// do we have an active tone?
	if (beep_buf_idx & 0x80) {
		duration = beep_dur_buf[beep_play_idx];
		level = beep_lev_buf[beep_play_idx];

		if (duration) {
			if (level == 0) { 						// loudest
				GPIO_SetBits(beep_pin.port, beep_pin.pin);
			} else if (level == 32) { 				// silent
				GPIO_ResetBits(beep_pin.port, beep_pin.pin);
			} else if (duration % level == 0) { 	// tones
				GPIO_SetBits(beep_pin.port, beep_pin.pin);
			} else {
				GPIO_ResetBits(beep_pin.port, beep_pin.pin);
			}

			// count down the times
			beep_dur_buf[beep_play_idx]--;
		} else {
			// stop the tone
			GPIO_ResetBits(beep_pin.port, beep_pin.pin);

			// move to the next buffer
			beep_play_idx++;

			// is there any more? need to reset buffer if there isn't any more
			if (((beep_buf_idx & 0x7f) == beep_play_idx) || beep_play_idx >= BEEP_BUF_SIZE) {
				// nope, no more
				beep_buf_idx = 0;
				beep_play_idx = 0;
			}
		}
	}
}

/* beep control */
void beep_clear_queue()
{
	beep_buf_idx = 0;
	GPIO_ResetBits(beep_pin.port, beep_pin.pin);
}

uint16_t beep(uint16_t tone_id, uint16_t duration)
{
	if ((beep_buf_idx & 0x7f) < BEEP_BUF_SIZE) {
		// if not in use, reset the playback index
		if (beep_buf_idx == 0) {
			beep_play_idx = 0;
		}

		// mark the beeper as being used
		beep_buf_idx |= 0x80;

		// set the desired tone and duration
		beep_lev_buf[beep_buf_idx & 0x7f] = (tone_id < 32) ? 32 - tone_id : 0;
		beep_dur_buf[beep_buf_idx & 0x7f] = duration * 5;

		// prepare for the next buffer location
		beep_buf_idx++;

		return (beep_buf_idx & 0x7f);
	}

	return 0xff;
}
