/* tuning.c -- a scale definition program */

/*****************************************************************************
*	    Change Log
*  Date	    | Change
*-----------+-----------------------------------------------------------------
* 16-Jun-86 | Created changelog
* 12-Aug-86 | Fixed an off-by-12 indexing bug, use userio.h to open file
*****************************************************************************/

#include "cext.h"
#include "stdio.h"
#include "pitch.h"
#include "userio.h"
#include "cmdline.h"

#define MINPITCH -12
#define MAXPITCH 115

/****************************************************************************
*	Routines local to this module
****************************************************************************/
private char	getcommand();
private	void	getoctave();
private	void	getonepitch();
private	void	getrange();
private	void	readpitch();
private	void	writepitch();
private void	ratio_to_cents();

/****************************************************************************
* Data for command line parsing
****************************************************************************/
#define nswitches 0
#define switches NULL
#define noptions 0
#define options NULL

/****************************************************************************
*				getcommand
* Outputs:
*	returns char: character typed by user
* Effect:
*	prompts for command and gets it
****************************************************************************/

private char getcommand()
{
    char result;

    printf("\nr - enter a range of values\n");
    printf("o - enter one octave (that will be generalized)\n");
    printf("p - enter one pitch (you choose)\n");
    printf("q - quit\n\n");
    printf("Command >>");

    while ((result = getchar()) == '\n') ;
    readln(stdin);	/* flush the newline and any other chars */
    return result;
}

/****************************************************************************
*				getoctave
* Outputs:
*	pitch_table pit_tab[]: array of pitch data
* Effect:
*	prompts user for one octave of data, then transposes to get others
****************************************************************************/

private void getoctave(pit_tab)
pitch_table pit_tab[];
{
    float ratio[12], num, denom;
    int pitch[12], bend[12], base, key, i, j, count, choice;

    printf("\n\nYou will input the information for one octave and that\n");
    printf("  will be generalized for all octaves.\n");
    printf("\nWould you like to enter the information as\n");
    printf("   (1)  frequency ratios of notes to a tonic\n");
    printf("or (2)  semitones (integer) + pitchbend (in cents)\n\n");
    printf(" >>");
    scanf("%d\n", &choice);
    if (choice == 1) {
	printf("Please enter ratio x/y as   x y	  where x and y are floating point numbers.\n\n");
	printf("Note 0 (tonic)	ratio is  1/1  (1 1)\n");
	ratio[0] = 1.0;
	for (i = 1; i < 12; i++) {
	    printf("note %d ratio >>", i);
	    scanf("%f %f\n", &num,&denom);
	    ratio[i] = num/denom;
	    printf("ratio[%d] = %f\n",i,ratio[i]);
	}
	ratio_to_cents(ratio, bend, pitch);
    } else {
	printf("\n\nFor the given note number, enter the desired pitch\n");
	printf("and the amount of pitchbend in cents based on given tonic\n");
	printf("\n1 unit of pitch = 1 semitone -- the tonic (note 0) has pitch 0 and bend 0.\n");
	printf("   Bend range =	 -100 .. 100 cents,  0 for no pitch bend\n");
	printf("     (100 cents = 1 semitone)\n");
	printf("\n\n");
	bend[0] = pitch[0] = 0;
	for (i = 1; i <12; i++)
	{
	    printf("Octave note %d\n", i);
	    printf("   pitch >>");
	    scanf("%d\n", &pitch[i]);
	    printf("   bend >>");
	    scanf("%d\n", &bend[i]);
	}
    }
    printf("\nWhat note would you like your octave to start on?\n\n");
    printf("C	C#  D	D#  E	F   F#	G   G#	A   A#	B\n");
    printf("0	1   2	3   4	5   6	7   8	9   10	11\n");
    printf(" >>");
    scanf("%d\n", &key);
    for (i = 0; i < 12; i++) {
	base = i + key;
	if (base > 11) {
	    base -= 12; key -=12;
	}
	/* 0 <= base <= 11 */
	/* remember that pitches range from -12 to 115: */
	for (count = -1, j = base+MINPITCH; j <= MAXPITCH; j+=12) {
	    pit_tab[j-MINPITCH].ppitch = key + pitch[i] + count*12;
	    pit_tab[j-MINPITCH].pbend = bend[i];
	    count++;
	}
    }
}

/****************************************************************************
*				getonepitch
* Inputs:
*	pitch_table pit_tab[]: table to be modified
* Outputs:
*	pitch_table pit_tab[]: table with modifications is returned
* Effect: 
*	prompts user for a note to change, gets data, modifies table
****************************************************************************/

private void getonepitch(pit_tab)
pitch_table pit_tab[128];
{
    int	 pitch, bend, index, finished = 0;

    while (!finished) {
	printf("Note range is -12..115.  Which note would you like to enter ?");
	printf("\n (Q to quit)\n");
	printf("Note >>");
	if (scanf("%d\n", &index) == 0) {
	    finished = true;
	    readln(stdin);
	} else {
	    printf("\n\nFor the given pitch number, enter pitch paramaters\n");
	    printf("   Bend range =  -100 .. 100 cents,	 0 for no pitch bend\n");
	    printf("	 (100 cents = 1 semitone)\n");
	    printf("   Pitch range = -12 .. 115, 48 is middle C\n\n\n");

	    printf("Adagio note %d\n", index);
	    printf("   pitch >>");
	    scanf("%d\n", &pitch);
	    printf("   bend >>");
	    scanf("%d\n", &bend);
	    pit_tab[index - MINPITCH].pbend = bend;
	    pit_tab[index - MINPITCH].ppitch = pitch;
	}
    }
}

/****************************************************************************
*				getrange
* Inputs:
*	pitch_table pit_tab[128]: a table of pitch info
* Outputs:
*	pitch_table pit_tab[128]: modified table of pitch info
* Effect: 
*	asks user for a range, then prompts for pitch data within that
*	range
****************************************************************************/

private void getrange(pit_tab)
    pitch_table pit_tab[128];
{
    int i,low,high,bend,pitch;

    printf("Adagio note range is 1..128\nWhat range of notes would you like to enter ?\n");
    printf("From >>");
    scanf("%d\n", &low);
    printf("  To >>");
    scanf("%d\n", &high);

    printf("\n\nFor the given Adagio note number, enter the desired pitch\n");
    printf("and the amount of pitchbend in cents\n");
    printf("   Bend range =  -100 .. 100,  '0' for no pitch bend\n");
    printf("	 (100 cents = 1 semitone)\n");
    printf("   Pitch range = 1 .. 128, 48 is middle C\n");
    printf("\n\n");
    for (i = low; i <= high; i++)
    {
	printf("Adagio Note %d\n", i);
	printf("   pitch >>");
	scanf("%d\n", &pitch);
	printf("   bend >>");
	scanf("%d\n", &bend);
	pit_tab[i - MINPITCH].pbend = bend;
	pit_tab[i - MINPITCH].ppitch = pitch;
    }
}

/****************************************************************************
*				main
* Inputs:
*	int argc: number of command line arguments
*	char *argv[]: vector of command line arguments
* Effect: main program
****************************************************************************/

void main(argc, argv)
    int	 argc;
    char *argv[];
{
    int i, done = 0;
    FILE *fp;
    char *s;	/* the filename argument, if any */
    char filename[100];
    pitch_table pit_tab[128];

    cl_init(switches, nswitches, options, noptions, argv, argc);

    /* get output file: */
	if ((s = cl_arg(1)) != NULL) strcpy(filename, s);
	else strcpy(filename, "");
	fp = fileopen(filename, "tun", "w", "Name of tuning file");

    for (i = MINPITCH; i <= MAXPITCH; i++) { /* initialize pit_tab */
	pit_tab[i - MINPITCH].pbend = 0;
	pit_tab[i - MINPITCH].ppitch = i;
    }

    printf("You will now create a pitch file.\n\n");
    while (!done) {
	switch (getcommand()) {
	    case 'r': getrange(pit_tab); break;
	    case 'p': getonepitch(pit_tab); break;
	    case 'o': getoctave(pit_tab); break;
	    case 'q': done = 1; break;
	    default: continue;
	}
    }
    writepitch(fp, pit_tab);
}

/****************************************************************************
*				readpitch
* Inputs:
*	FILE *fp: file from which to read
* Outputs:
*	pitch_table pit_tab[128]: this table is initialized by reading fp
****************************************************************************/

private void readpitch(fp, pit_tab)
FILE *fp;
pitch_table pit_tab[128];
{
    int i, j, pitch, bend;

    for ( j = MINPITCH; j <= MAXPITCH; j++)
    {
	fscanf(fp,"%d %d %d\n", &i, &pitch, &bend);
	pit_tab[i].ppitch = pitch;
	pit_tab[i].pbend = bend;
    }
}

/****************************************************************************
*				writepitch
* Inputs:
*	FILE *fp: the file to write
*	pitch_table pit_tab[128]: the table to write
* Effect: 
*	writes pit_tab to file *fp
****************************************************************************/

void writepitch(fp, pit_tab)
    FILE *fp;
    pitch_table pit_tab[128];
{
    int i;

    for ( i = MINPITCH; i <= MAXPITCH; i++) {
	fprintf(fp,"%d %d %d\n", i, pit_tab[i-MINPITCH].ppitch,
		pit_tab[i-MINPITCH].pbend);
    }
}

/****************************************************************************
*				ratio_to_cents
* Inputs:
*	float ratio[]: an octave of rations to convert
* Outputs:
*	int bend[]: an octave of pitch bends
*	int pitch[]: an octave of pitches
* Effect:
*	converts 12 ratios to pitch + bend pairs
****************************************************************************/

private void ratio_to_cents(ratio, bend, pitch)
float ratio[];
int bend[], pitch[];
{
    int i;
    float cents;

    for (i = 0; i < 12; i++) {
	cents = 1200 * log(ratio[i])/log(2);
	pitch[i] = (int)(cents/100);
	bend[i] = (int)(cents - (pitch[i] * 100) + 0.5);
    }
}
