/* Extended Module Player
 * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include "common.h"
#include "period.h"

#include <math.h>

#ifdef LIBXMP_PAULA_SIMULATOR
/*
 * Period table from the Protracker V2.1A play routine
 */
static uint16 pt_period_table[16][36] = {
	/* Tuning 0, Normal */
	{
		856,808,762,720,678,640,604,570,538,508,480,453,
		428,404,381,360,339,320,302,285,269,254,240,226,
		214,202,190,180,170,160,151,143,135,127,120,113
	},
	/* Tuning 1 */
	{
		850,802,757,715,674,637,601,567,535,505,477,450,
		425,401,379,357,337,318,300,284,268,253,239,225,
		213,201,189,179,169,159,150,142,134,126,119,113
	},
	/* Tuning 2 */
	{
		844,796,752,709,670,632,597,563,532,502,474,447,
		422,398,376,355,335,316,298,282,266,251,237,224,
		211,199,188,177,167,158,149,141,133,125,118,112
	},
	/* Tuning 3 */
	{
		838,791,746,704,665,628,592,559,528,498,470,444,
		419,395,373,352,332,314,296,280,264,249,235,222,
		209,198,187,176,166,157,148,140,132,125,118,111
	},
	/* Tuning 4 */
	{
		832,785,741,699,660,623,588,555,524,495,467,441,
		416,392,370,350,330,312,294,278,262,247,233,220,
		208,196,185,175,165,156,147,139,131,124,117,110
	},
	/* Tuning 5 */
	{
		826,779,736,694,655,619,584,551,520,491,463,437,
		413,390,368,347,328,309,292,276,260,245,232,219,
		206,195,184,174,164,155,146,138,130,123,116,109
	},
	/* Tuning 6 */
	{
		820,774,730,689,651,614,580,547,516,487,460,434,
		410,387,365,345,325,307,290,274,258,244,230,217,
		205,193,183,172,163,154,145,137,129,122,115,109
	},
	/* Tuning 7 */
	{
		814,768,725,684,646,610,575,543,513,484,457,431,
		407,384,363,342,323,305,288,272,256,242,228,216,
		204,192,181,171,161,152,144,136,128,121,114,108
	},
	/* Tuning -8 */
	{
		907,856,808,762,720,678,640,604,570,538,508,480,
		453,428,404,381,360,339,320,302,285,269,254,240,
		226,214,202,190,180,170,160,151,143,135,127,120
	},
	/* Tuning -7 */
	{
		900,850,802,757,715,675,636,601,567,535,505,477,
		450,425,401,379,357,337,318,300,284,268,253,238,
		225,212,200,189,179,169,159,150,142,134,126,119
	},
	/* Tuning -6 */
	{
		894,844,796,752,709,670,632,597,563,532,502,474,
		447,422,398,376,355,335,316,298,282,266,251,237,
		223,211,199,188,177,167,158,149,141,133,125,118
	},
	/* Tuning -5 */
	{
		887,838,791,746,704,665,628,592,559,528,498,470,
		444,419,395,373,352,332,314,296,280,264,249,235,
		222,209,198,187,176,166,157,148,140,132,125,118
	},
	/* Tuning -4 */
	{
		881,832,785,741,699,660,623,588,555,524,494,467,
		441,416,392,370,350,330,312,294,278,262,247,233,
		220,208,196,185,175,165,156,147,139,131,123,117
	},
	/* Tuning -3 */
	{
		875,826,779,736,694,655,619,584,551,520,491,463,
		437,413,390,368,347,328,309,292,276,260,245,232,
		219,206,195,184,174,164,155,146,138,130,123,116
	},
	/* Tuning -2 */
	{
		868,820,774,730,689,651,614,580,547,516,487,460,
		434,410,387,365,345,325,307,290,274,258,244,230,
		217,205,193,183,172,163,154,145,137,129,122,115
	},
	/* Tuning -1 */
	{
		862,814,768,725,684,646,610,575,543,513,484,457,
		431,407,384,363,342,323,305,288,272,256,242,228,
		216,203,192,181,171,161,152,144,136,128,121,114
	}
};
#endif

#ifndef M_LN2
#define M_LN2	0.69314718055994530942
#endif
#if !defined(HAVE_ROUND) || defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DJGPP__)
static inline double libxmp_round(double val)
{
	return (val >= 0.0)? floor(val + 0.5) : ceil(val - 0.5);
}
#else
#define libxmp_round round
#endif

#ifdef LIBXMP_PAULA_SIMULATOR
/* Get period from note using Protracker tuning */
static inline int libxmp_note_to_period_pt(int n, int f)
{
	if (n < MIN_NOTE_MOD || n > MAX_NOTE_MOD) {
		return -1;
	}

	n -= 48;
	f >>= 4;
	if (f < -8 || f > 7) {
		return 0;
	}

	if (f < 0) {
		f += 16;
	}

	return (int)pt_period_table[f][n];
}
#endif

/* Get period from note */
double libxmp_note_to_period(struct context_data *ctx, int n, int f, double adj)
{
	double d, per;
	struct module_data *m = &ctx->m;
#ifdef LIBXMP_PAULA_SIMULATOR
	struct player_data *p = &ctx->p;

	/* If mod replayer, modrng and Amiga mixing are active */
	if (p->flags & XMP_FLAGS_A500) {
		if (IS_AMIGA_MOD()) {
			return libxmp_note_to_period_pt(n, f);
		}
	}
#endif

	d = (double)n + (double)f / 128;

	switch (m->period_type) {
	case PERIOD_LINEAR:
		per = (240.0 - d) * 16;			/* Linear */
		break;
	case PERIOD_CSPD:
		per = 8363.0 * pow(2, n / 12) / 32 + f;	/* Hz */
		break;
	default:
		per = PERIOD_BASE / pow(2, d / 12);	/* Amiga */
	}

#ifndef LIBXMP_CORE_PLAYER
	if (adj > 0.1) {
		per *= adj;
	}
#endif

	return per;
}

/* For the software mixer */
double libxmp_note_to_period_mix(int n, int b)
{
	double d = (double)n + (double)b / 12800;
	return PERIOD_BASE / pow(2, d / 12);
}

/* Get note from period */
/* This function is used only by the MOD loader */
int libxmp_period_to_note(int p)
{
	if (p <= 0) {
		return 0;
	}

	return libxmp_round(12.0 * log(PERIOD_BASE / p) / M_LN2) + 1;
}

/* Get pitchbend from base note and amiga period */
int libxmp_period_to_bend(struct context_data *ctx, double p, int n, double adj)
{
	struct module_data *m = &ctx->m;
	double d;

	if (n == 0) {
		return 0;
	}

	switch (m->period_type) {
	case PERIOD_LINEAR:
		return 100 * (8 * (((240 - n) << 4) - p));
	case PERIOD_CSPD:
		d = libxmp_note_to_period(ctx, n, 0, adj);
		return libxmp_round(100.0 * (1536.0 / M_LN2) * log(p / d));
	default:
		/* Amiga */
		d = libxmp_note_to_period(ctx, n, 0, adj);
		return libxmp_round(100.0 * (1536.0 / M_LN2) * log(d / p));
	}
}

/* Convert finetune = 1200 * log2(C2SPD/8363))
 *
 *      c = (1200.0 * log(c2spd) - 1200.0 * log(c4_rate)) / M_LN2;
 *      xpo = c/100;
 *      fin = 128 * (c%100) / 100;
 */
void libxmp_c2spd_to_note(int c2spd, int *n, int *f)
{
	int c;

	if (c2spd == 0) {
		*n = *f = 0;
		return;
	}

	c = (int)(1536.0 * log((double)c2spd / 8363) / M_LN2);
	*n = c / 128;
	*f = c % 128;
}
