/*
 * Music for the 4K intro.
 *
 * Coded by:
 *  s_tec
 */

#include <windows.h>
#include "math4k.h"

void bell(short *buffer, float f);
const float pi = 3.141592654;

/*
 * Constants:
 */
#define WAVE_CHAN   (1)                         /* Channels */
#define WAVE_SPS    (44100)                     /* Samples per second */
#define WAVE_BITS   (16)                        /* Bits per sample */
#define WAVE_ALIGN  (WAVE_CHAN*WAVE_BITS/8)     /* Bytes per sample */
#define WAVE_SIZE   (120*WAVE_SPS*WAVE_CHAN)    /* Buffer size in samples */

/*
 * Data structures:
 */
short wave_buffer[WAVE_SIZE];
HWAVEOUT wave_handle;

WAVEFORMATEX wave_format = {
	WAVE_FORMAT_PCM,                          /* wFormatTag */
	WAVE_CHAN,                                /* nChannels */
	WAVE_SPS,                                 /* nSamplesPerSec */
	WAVE_ALIGN * WAVE_SPS,                    /* nAvgBytesPerSec */
	WAVE_ALIGN,                               /* nBlockAlign */
	WAVE_BITS                                 /* wBitsPerSample */
};

WAVEHDR wave_header = {
	(char*)wave_buffer,                       /* lpData */
	WAVE_SIZE * WAVE_BITS / 8,                /* dwBufferLength	*/
	0,                                        /* dwBytesRecorded */
	0,                                        /* dwUser */
	0,                                        /* dwFlags */
	0,                                        /* dwLoops */
	0,                                        /* lpNext */
	0                                         /* reserved */
};

/**
 * Generates and plays music.
 */
void music()
{
  int i;
  short *p = wave_buffer;

  /* Some musical frequencies: */
  const float note_a = 220.0000000 / WAVE_SPS;
  const float note_c = 261.6255653 / WAVE_SPS;
  const float note_d = 293.6647679 / WAVE_SPS;
  const float note_e = 329.6275569 / WAVE_SPS;

  /* Main body: */
  for (i = 0; i < 3; ++i) {
    bell(p, note_c);
    p += 10000;
    bell(p, note_d);
    p += 10000;
    bell(p, note_a);
    p += 10000;
    bell(p, note_d);
    p += 10000;
  }

  /* Closing: */
  bell(p, note_e);
  p += 10000;
  bell(p, note_c);
  p += 10000;
  bell(p, note_a);
  p += 10000;
  bell(p, note_c);
  p += 10000;
  bell(p, note_a);
  p += 10000;

  /* Play the audio: */
	waveOutOpen(&wave_handle, WAVE_MAPPER, &wave_format, 0, 0, 0);
	waveOutPrepareHeader(wave_handle,	&wave_header,	sizeof(WAVEHDR));
	waveOutWrite(wave_handle, &wave_header, sizeof(WAVEHDR));
}

/* Data structure used to define envelopes: */
typedef struct {
  float value; /* Amplitude at this time*/
  int time;    /* Time difference until the next key */
} key;

/**
 * Finds any arbitrary point along an envelope.
 */
float envelope(key keys[], int time)
{
  while (time > keys[0].time) {
    time -= keys[0].time;
    keys++;
  }
  return keys[0].value + time*(keys[1].value - keys[0].value)/keys[0].time;
}

/**
 * Systhesizes a simple bell sound
 */
void bell(short *buffer, float f)
{
  int i;
  float count1 = 0, count2 = 0;
  float osc1, osc2;
  float sample;

  /* Changes the volume over time: */
  static key env[] = {
    {0.0f,     1000},
    {20000.0f, 3000},
    {10000.0f, 12000},
    {0.0f}
  };

  /* Render the note in a loop: */
  for (i = 0; i < 16000; ++i) {
     /* Oscilator 1 generates a modulation frequency: */
    count1 += f * 0.77;
    osc1 = sin(2*pi * count1);

    /* Oscilator 2 generates a swooping frequency: */
    count2 += f * (1 + 0.7*osc1);
    osc2 = sin(2*pi * count2);

    /* Mix with the envelope: */
    sample = osc2 * envelope(env, i);

    /* Write to the output buffer: */
    buffer[i] += sFtol(sample);
  }
}
