<commit_before>/*
 * This is Standard MIDI File format implementation, tempo map related part.
 *
 * For questions and comments, contact <NAME> <<EMAIL>>.
 * This code is public domain, you can do with it whatever you want.
 */

#include <stdlib.h>
#include <assert.h>
#include "smf.h"
#include "smf_private.h"

static void
maybe_add_to_tempo_map(smf_event_t *event)
{
	int new_tempo;

	if (!smf_event_is_metadata(event))
		return;

	/* Not a Tempo Change? */
	if (event->midi_buffer[1] != 0x51)
		return;

	assert(event->track != NULL);
	assert(event->track->smf != NULL);

	new_tempo = (event->midi_buffer[3] << 16) + (event->midi_buffer[4] << 8) + event->midi_buffer[5];
	if (new_tempo <= 0) {
		g_critical("Ignoring invalid tempo change.");
		return;
	}

	smf_tempo_add(event->track->smf, event->time_pulses, new_tempo);
	g_debug("Setting tempo (microseconds per quarter note) to %d.",
		smf_get_tempo_by_position(event->track->smf, event->time_pulses)->microseconds_per_quarter_note);

	return;
}

/* XXX: Add tempo map. */
static double
seconds_between_events(smf_t *smf, int previous_pulses, int new_pulses)
{
	int pulses, tempo;

	pulses = new_pulses - previous_pulses;

	tempo = smf_get_tempo_by_position(smf, previous_pulses)->microseconds_per_quarter_note;

	assert(pulses >= 0);

	return pulses * ((double)tempo / ((double)smf->ppqn * 1000000.0));
}

/*
 * Computes value of event->time_seconds for all events in smf.
 * Warning: rewinds the smf.
 */
int
smf_compute_seconds(smf_t *smf)
{
	smf_event_t *event, *previous = NULL;

	smf_rewind(smf);
	smf_remove_tempos(smf);

	for (;;) {
		event = smf_get_next_event(smf);
		
		if (event == NULL)
			return 0;

		maybe_add_to_tempo_map(event);

		if (event->event_number == 1) {
			event->time_seconds = seconds_between_events(smf, 0, event->time_pulses);

		} else {
			previous = smf_get_event_by_number(event->track, event->event_number - 1);

			assert(previous);
			assert(previous->time_seconds >= 0);

			event->time_seconds = previous->time_seconds + seconds_between_events(smf,
				previous->time_pulses, event->time_pulses);
		}
	}
}

/*
 * XXX: Sort entries by ->time_pulses.  Remove duplicates (two tempos starting at the same time,
 * at pulse 0, for example.
 */
int
smf_tempo_add(smf_t *smf, int pulses, int new_tempo)
{
	smf_tempo_t *tempo = malloc(sizeof(smf_tempo_t));
	if (tempo == NULL) {
		g_critical("Malloc failed.");
		return -1;
	}

	tempo->time_pulses = pulses;
#if 0
	tempo->time_seconds = seconds_between_events(smf, 0, pulses);
#endif
	tempo->microseconds_per_quarter_note = new_tempo;

	g_ptr_array_add(smf->tempo_array, tempo);

	return 0;<commit_msg>almost correct tempo implementation.<commit_after>/*
 * This is Standard MIDI File format implementation, tempo map related part.
 *
 * For questions and comments, contact <NAME> <<EMAIL>>.
 * This code is public domain, you can do with it whatever you want.
 */

#include <stdlib.h>
#include <assert.h>
#include "smf.h"
#include "smf_private.h"

static void
maybe_add_to_tempo_map(smf_event_t *event)
{
	int new_tempo;

	if (!smf_event_is_metadata(event))
		return;

	/* Not a Tempo Change? */
	if (event->midi_buffer[1] != 0x51)
		return;

	assert(event->track != NULL);
	assert(event->track->smf != NULL);

	new_tempo = (event->midi_buffer[3] << 16) + (event->midi_buffer[4] << 8) + event->midi_buffer[5];
	if (new_tempo <= 0) {
		g_critical("Ignoring invalid tempo change.");
		return;
	}

	smf_tempo_add(event->track->smf, event->time_pulses, new_tempo);
	g_debug("Setting tempo (microseconds per quarter note) to %d.",
		smf_get_tempo_by_position(event->track->smf, event->time_pulses)->microseconds_per_quarter_note);

	return;
}

static double
seconds_from_pulses(smf_t *smf, int pulses)
{
	double seconds = 0.0;
	smf_tempo_t *tempo;

	for (;;) {
		tempo = smf_get_tempo_by_position(smf, pulses);
		assert(tempo);
		assert(tempo->time_pulses <= pulses);

		seconds += (double)(pulses - tempo->time_pulses) * (tempo->microseconds_per_quarter_note / ((double)smf->ppqn * 1000000.0));

		if (tempo->time_pulses == 0)
			return seconds;

		/* XXX: -1? */
		pulses = tempo->time_pulses - 1;
	}

	/* Not reached. */
	return -1;
}

/*
 * Computes value of event->time_seconds for all events in smf.
 * Warning: rewinds the smf.
 */
int
smf_compute_seconds(smf_t *smf)
{
	smf_event_t *event;

	smf_rewind(smf);
	smf_remove_tempos(smf);

	for (;;) {
		event = smf_get_next_event(smf);
		
		if (event == NULL)
			return 0;

		maybe_add_to_tempo_map(event);

		event->time_seconds = seconds_from_pulses(smf, event->time_pulses);
	}

	/* Not reached. */
	return -1;
}

/*
 * XXX: Sort entries by ->time_pulses.  Remove duplicates (two tempos starting at the same time,
 * at pulse 0, for example.
 */
int
smf_tempo_add(smf_t *smf, int pulses, int new_tempo)
{
	smf_tempo_t *tempo = malloc(sizeof(smf_tempo_t));
	if (tempo == NULL) {
		g_critical("Malloc failed.");
		return -1;
	}

	tempo->time_pulses = pulses;
#if 0
	tempo->time_seconds = seconds_between_events(smf, 0, pulses);
#endif
	tempo->microseconds_per_quarter_note = new_tempo;

	g_ptr_array_add(smf->tempo_array, tempo);

	return 0;
{'fraction_non_alphanumeric': 0.1403235236795946, 'fraction_numerical': 0.012083414539076203, 'mean_word_length': 4.524219590958019, 'pattern_counts': {'":': 0, '<': 24, '<?xml version=': 0, '>': 75, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1150098', 'n_tokens_mistral': 2151, 'n_tokens_neox': 1966, 'n_words': 435}