/*
wildmidi_lib.c
Midi Wavetable Processing library
Copyright (C) Chris Ison 2001-2014
Copyright (C) Bret Curtis 2013-2014
This file is part of WildMIDI.
WildMIDI is free software: you can redistribute and/or modify the player
under the terms of the GNU General Public License and you can redistribute
and/or modify the library under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, either version 3 of
the licenses, or(at your option) any later version.
WildMIDI is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and
the GNU Lesser General Public License for more details.
You should have received a copy of the GNU General Public License and the
GNU Lesser General Public License along with WildMIDI. If not, see
.
*/
//#include "config.h"
#define UNUSED(x) (void)(x)
#include
#include
#include
#ifndef _WIN32
#include
#include
#include
#endif
#include
#include
#include
#include
#include
#include
/*
#ifdef _WIN32
#include
#include
*/
#undef strcasecmp
#define strcasecmp _stricmp
#undef strncasecmp
#define strncasecmp _strnicmp
#include "common.h"
#include "wm_error.h"
#include "file_io.h"
#include "lock.h"
#include "reverb.h"
#include "gus_pat.h"
#include "wildmidi_lib.h"
#define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\')
#ifdef _WIN32
#define HAS_DRIVE_SPEC(f) ((f)[0] && ((f)[1] == ':'))
#else
#define HAS_DRIVE_SPEC(f) (0)
#endif
#define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || HAS_DRIVE_SPEC((f)))
// Why is this shit even being used...? :(
#define __builtin_expect(a, b) a
/*
* =========================
* Global Data and Data Structs
* =========================
*/
#define MEM_CHUNK 8192
static int WM_Initialized = 0;
static signed short int WM_MasterVolume = 948;
static unsigned short int WM_MixerOptions = 0;
static char WM_Version[] = "WildMidi Processing Library";
unsigned short int _WM_SampleRate;
static struct _patch *patch[128];
static float reverb_room_width = 16.875f;
static float reverb_room_length = 22.5f;
static float reverb_listen_posx = 8.4375f;
static float reverb_listen_posy = 16.875f;
static int fix_release = 0;
static int auto_amp = 0;
static int auto_amp_with_amp = 0;
static int patch_lock;
struct _channel {
unsigned char bank;
struct _patch *patch;
unsigned char hold;
unsigned char volume;
unsigned char pressure;
unsigned char expression;
signed char balance;
signed char pan;
signed short int left_adjust;
signed short int right_adjust;
signed short int pitch;
signed short int pitch_range;
signed long int pitch_adjust;
unsigned short reg_data;
unsigned char reg_non;
unsigned char isdrum;
};
#define HOLD_OFF 0x02
struct _note {
unsigned short noteid;
unsigned char velocity;
struct _patch *patch;
struct _sample *sample;
unsigned long int sample_pos;
unsigned long int sample_inc;
signed long int env_inc;
unsigned char env;
signed long int env_level;
unsigned char modes;
unsigned char hold;
unsigned char active;
struct _note *replay;
struct _note *next;
unsigned long int vol_lvl;
unsigned char is_off;
};
struct _miditrack {
unsigned long int length;
unsigned long int ptr;
unsigned long int delta;
unsigned char running_event;
unsigned char EOT;
};
struct _mdi_patches {
struct _patch *patch;
struct _mdi_patch *next;
};
struct _event_data {
unsigned char channel;
unsigned long int data;
};
struct _mdi {
int lock;
unsigned long int samples_to_mix;
struct _event *events;
struct _event *current_event;
unsigned long int event_count;
unsigned long int events_size; /* try to stay optimally ahead to prevent reallocs */
unsigned short midi_master_vol;
struct _WM_Info info;
struct _WM_Info *tmp_info;
struct _channel channel[16];
struct _note *note;
struct _note note_table[2][16][128];
struct _patch **patches;
unsigned long int patch_count;
signed short int amp;
signed int *mix_buffer;
unsigned long int mix_buffer_size;
struct _rvb *reverb;
};
struct _event {
void (*do_event)(struct _mdi *mdi, struct _event_data *data);
struct _event_data event_data;
unsigned long int samples_to_next;
unsigned long int samples_to_next_fixed;
};
#define FPBITS 10
#define FPMASK ((1L<> 1);
int j;
int sign;
double ck;
double x, x_inc, xz;
double z[35];
double *gptr, *t;
_WM_Lock(&gauss_lock);
if (gauss_table) {
_WM_Unlock(&gauss_lock);
return;
}
newt_coeffs[0][0] = 1;
for (i = 0; i <= n; i++) {
newt_coeffs[i][0] = 1;
newt_coeffs[i][i] = 1;
if (i > 1) {
newt_coeffs[i][0] = newt_coeffs[i - 1][0] / i;
newt_coeffs[i][i] = newt_coeffs[i - 1][0] / i;
}
for (j = 1; j < i; j++) {
newt_coeffs[i][j] = newt_coeffs[i - 1][j - 1]
+ newt_coeffs[i - 1][j];
if (i > 1)
newt_coeffs[i][j] /= i;
}
z[i] = i / (4 * M_PI);
}
for (i = 0; i <= n; i++)
for (j = 0, sign = (int)pow(-1., i); j <= i; j++, sign *= -1)
newt_coeffs[i][j] *= sign;
t = (double*)malloc((1<event_count >= mdi->events_size) {
mdi->events_size += MEM_CHUNK;
mdi->events = (struct _event*)realloc(mdi->events,
(mdi->events_size * sizeof(struct _event)));
}
}
static void WM_InitPatches(void) {
int i;
for (i = 0; i < 128; i++) {
patch[i] = NULL;
}
}
static void WM_FreePatches(void) {
int i;
struct _patch * tmp_patch;
struct _sample * tmp_sample;
_WM_Lock(&patch_lock);
for (i = 0; i < 128; i++) {
while (patch[i]) {
while (patch[i]->first_sample) {
tmp_sample = patch[i]->first_sample->next;
free(patch[i]->first_sample->data);
free(patch[i]->first_sample);
patch[i]->first_sample = tmp_sample;
}
free(patch[i]->filename);
tmp_patch = patch[i]->next;
free(patch[i]);
patch[i] = tmp_patch;
}
}
_WM_Unlock(&patch_lock);
}
/* wm_strdup -- adds extra space for appending up to 4 chars */
static char *wm_strdup (const char *str) {
size_t l = strlen(str) + 5;
char *d = (char *) malloc(l * sizeof(char));
if (d) {
strcpy(d, str);
return d;
}
return NULL;
}
static inline int wm_isdigit(int c) {
return (c >= '0' && c <= '9');
}
#define TOKEN_CNT_INC 8
static char** WM_LC_Tokenize_Line(char *line_data) {
int line_length = strlen(line_data);
int token_data_length = 0;
int line_ofs = 0;
int token_start = 0;
char **token_data = NULL;
int token_count = 0;
if (line_length == 0)
return NULL;
do {
/* ignore everything after # */
if (line_data[line_ofs] == '#') {
break;
}
if ((line_data[line_ofs] == ' ') || (line_data[line_ofs] == '\t')) {
/* whitespace means we aren't in a token */
if (token_start) {
token_start = 0;
line_data[line_ofs] = '\0';
}
} else {
if (!token_start) {
/* the start of a token in the line */
token_start = 1;
if (token_count >= token_data_length) {
token_data_length += TOKEN_CNT_INC;
token_data = (char**)realloc(token_data, token_data_length * sizeof(char *));
if (token_data == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM,"to parse config", errno);
return NULL;
}
}
token_data[token_count] = &line_data[line_ofs];
token_count++;
}
}
line_ofs++;
} while (line_ofs != line_length);
/* if we have found some tokens then add a null token to the end */
if (token_count) {
if (token_count >= token_data_length) {
token_data = (char**)realloc(token_data,
((token_count + 1) * sizeof(char *)));
}
token_data[token_count] = NULL;
}
return token_data;
}
static int WM_LoadConfig(const char *config_file) {
unsigned long int config_size = 0;
char *config_buffer = NULL;
const char *dir_end = NULL;
char *config_dir = NULL;
unsigned long int config_ptr = 0;
unsigned long int line_start_ptr = 0;
unsigned short int patchid = 0;
struct _patch * tmp_patch;
char **line_tokens = NULL;
int token_count = 0;
config_buffer = (char *) _WM_BufferFile(config_file, &config_size);
if (!config_buffer) {
WM_FreePatches();
return -1;
}
// This part was rewritten because the original depended on a header that was GPL'd.
dir_end = strrchr(config_file, '/');
#ifdef _WIN32
const char *dir_end2 = strrchr(config_file, '\\');
if (dir_end2 > dir_end) dir_end = dir_end2;
#endif
if (dir_end) {
config_dir = (char*)malloc((dir_end - config_file + 2));
if (config_dir == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config",
errno);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
WM_FreePatches();
free(config_buffer);
return -1;
}
strncpy(config_dir, config_file, (dir_end - config_file + 1));
config_dir[dir_end - config_file + 1] = '\0';
}
config_ptr = 0;
line_start_ptr = 0;
/* handle files without a newline at the end: this relies on
* _WM_BufferFile() allocating the buffer with one extra byte */
config_buffer[config_size] = '\n';
while (config_ptr <= config_size) {
if (config_buffer[config_ptr] == '\r' ||
config_buffer[config_ptr] == '\n')
{
config_buffer[config_ptr] = '\0';
if (config_ptr != line_start_ptr) {
line_tokens = WM_LC_Tokenize_Line(&config_buffer[line_start_ptr]);
if (line_tokens) {
if (strcasecmp(line_tokens[0], "dir") == 0) {
free(config_dir);
if (!line_tokens[1]) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(missing name in dir line)", 0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
free(line_tokens);
free(config_buffer);
return -1;
} else if (!(config_dir = wm_strdup(line_tokens[1]))) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM,
"to parse config", errno);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
free(line_tokens);
free(config_buffer);
return -1;
}
if (!IS_DIR_SEPARATOR(config_dir[strlen(config_dir) - 1])) {
config_dir[strlen(config_dir) + 1] = '\0';
config_dir[strlen(config_dir)] = '/';
}
} else if (strcasecmp(line_tokens[0], "source") == 0) {
char *new_config = NULL;
if (!line_tokens[1]) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(missing name in source line)", 0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
free(line_tokens);
free(config_buffer);
return -1;
} else if (!IS_ABSOLUTE_PATH(line_tokens[1]) && config_dir) {
new_config = (char*)malloc(
strlen(config_dir) + strlen(line_tokens[1])
+ 1);
if (new_config == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM,
"to parse config", errno);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
return -1;
}
strcpy(new_config, config_dir);
strcpy(&new_config[strlen(config_dir)], line_tokens[1]);
} else {
if (!(new_config = wm_strdup(line_tokens[1]))) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM,
"to parse config", errno);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
free(line_tokens);
free(config_buffer);
return -1;
}
}
if (WM_LoadConfig(new_config) == -1) {
free(new_config);
free(line_tokens);
free(config_buffer);
free(config_dir);
return -1;
}
free(new_config);
} else if (strcasecmp(line_tokens[0], "bank") == 0) {
if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(syntax error in bank line)", 0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
return -1;
}
patchid = (atoi(line_tokens[1]) & 0xFF) << 8;
} else if (strcasecmp(line_tokens[0], "drumset") == 0) {
if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(syntax error in drumset line)", 0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
return -1;
}
patchid = ((atoi(line_tokens[1]) & 0xFF) << 8) | 0x80;
} else if (strcasecmp(line_tokens[0], "reverb_room_width") == 0) {
if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(syntax error in reverb_room_width line)",
0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
return -1;
}
reverb_room_width = (float) atof(line_tokens[1]);
if (reverb_room_width < 1.0f) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(reverb_room_width < 1 meter, setting to minimum of 1 meter)",
0);
reverb_room_width = 1.0f;
} else if (reverb_room_width > 100.0f) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(reverb_room_width > 100 meters, setting to maximum of 100 meters)",
0);
reverb_room_width = 100.0f;
}
} else if (strcasecmp(line_tokens[0], "reverb_room_length") == 0) {
if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(syntax error in reverb_room_length line)",
0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
return -1;
}
reverb_room_length = (float) atof(line_tokens[1]);
if (reverb_room_length < 1.0f) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(reverb_room_length < 1 meter, setting to minimum of 1 meter)",
0);
reverb_room_length = 1.0f;
} else if (reverb_room_length > 100.0f) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(reverb_room_length > 100 meters, setting to maximum of 100 meters)",
0);
reverb_room_length = 100.0f;
}
} else if (strcasecmp(line_tokens[0], "reverb_listener_posx") == 0) {
if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(syntax error in reverb_listen_posx line)",
0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
return -1;
}
reverb_listen_posx = (float) atof(line_tokens[1]);
if ((reverb_listen_posx > reverb_room_width)
|| (reverb_listen_posx < 0.0f)) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(reverb_listen_posx set outside of room)",
0);
reverb_listen_posx = reverb_room_width / 2.0f;
}
} else if (strcasecmp(line_tokens[0],
"reverb_listener_posy") == 0) {
if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(syntax error in reverb_listen_posy line)",
0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
return -1;
}
reverb_listen_posy = (float) atof(line_tokens[1]);
if ((reverb_listen_posy > reverb_room_width)
|| (reverb_listen_posy < 0.0f)) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(reverb_listen_posy set outside of room)",
0);
reverb_listen_posy = reverb_room_length * 0.75f;
}
} else if (strcasecmp(line_tokens[0],
"guspat_editor_author_cant_read_so_fix_release_time_for_me")
== 0) {
fix_release = 1;
} else if (strcasecmp(line_tokens[0], "auto_amp") == 0) {
auto_amp = 1;
} else if (strcasecmp(line_tokens[0], "auto_amp_with_amp")
== 0) {
auto_amp = 1;
auto_amp_with_amp = 1;
} else if (wm_isdigit(line_tokens[0][0])) {
patchid = (patchid & 0xFF80)
| (atoi(line_tokens[0]) & 0x7F);
if (patch[(patchid & 0x7F)] == NULL) {
patch[(patchid & 0x7F)] = (struct _patch*)malloc(
sizeof(struct _patch));
if (patch[(patchid & 0x7F)] == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM,
NULL, errno);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
return -1;
}
tmp_patch = patch[(patchid & 0x7F)];
tmp_patch->patchid = patchid;
tmp_patch->filename = NULL;
tmp_patch->amp = 1024;
tmp_patch->note = 0;
tmp_patch->next = NULL;
tmp_patch->first_sample = NULL;
tmp_patch->loaded = 0;
tmp_patch->inuse_count = 0;
} else {
tmp_patch = patch[(patchid & 0x7F)];
if (tmp_patch->patchid == patchid) {
free(tmp_patch->filename);
tmp_patch->filename = NULL;
tmp_patch->amp = 1024;
tmp_patch->note = 0;
} else {
if (tmp_patch->next) {
while (tmp_patch->next) {
if (tmp_patch->next->patchid == patchid)
break;
tmp_patch = tmp_patch->next;
}
if (tmp_patch->next == NULL) {
if ((tmp_patch->next = (struct _patch*)malloc(
sizeof(struct _patch)))
== NULL) {
_WM_ERROR(__FUNCTION__, __LINE__,
WM_ERR_MEM, NULL, 0);
_WM_ERROR(__FUNCTION__, __LINE__,
WM_ERR_LOAD, config_file,
0);
WM_FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
return -1;
}
tmp_patch = tmp_patch->next;
tmp_patch->patchid = patchid;
tmp_patch->filename = NULL;
tmp_patch->amp = 1024;
tmp_patch->note = 0;
tmp_patch->next = NULL;
tmp_patch->first_sample = NULL;
tmp_patch->loaded = 0;
tmp_patch->inuse_count = 0;
} else {
tmp_patch = tmp_patch->next;
free(tmp_patch->filename);
tmp_patch->filename = NULL;
tmp_patch->amp = 1024;
tmp_patch->note = 0;
}
} else {
tmp_patch->next = (struct _patch*)malloc(
sizeof(struct _patch));
if (tmp_patch->next == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__,
WM_ERR_MEM, NULL, errno);
_WM_ERROR(__FUNCTION__, __LINE__,
WM_ERR_LOAD, config_file, 0);
WM_FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
return -1;
}
tmp_patch = tmp_patch->next;
tmp_patch->patchid = patchid;
tmp_patch->filename = NULL;
tmp_patch->amp = 1024;
tmp_patch->note = 0;
tmp_patch->next = NULL;
tmp_patch->first_sample = NULL;
tmp_patch->loaded = 0;
tmp_patch->inuse_count = 0;
}
}
}
if (!line_tokens[1]) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(missing name in patch line)", 0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
return -1;
} else if (!IS_ABSOLUTE_PATH(line_tokens[1]) && config_dir) {
tmp_patch->filename = (char*)malloc(
strlen(config_dir) + strlen(line_tokens[1])
+ 5);
if (tmp_patch->filename == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM,
NULL, 0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
return -1;
}
strcpy(tmp_patch->filename, config_dir);
strcat(tmp_patch->filename, line_tokens[1]);
} else {
if (!(tmp_patch->filename = wm_strdup(line_tokens[1]))) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM,
NULL, 0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
return -1;
}
}
if (strncasecmp(
&tmp_patch->filename[strlen(tmp_patch->filename)
- 4], ".pat", 4) != 0) {
strcat(tmp_patch->filename, ".pat");
}
tmp_patch->env[0].set = 0x00;
tmp_patch->env[1].set = 0x00;
tmp_patch->env[2].set = 0x00;
tmp_patch->env[3].set = 0x00;
tmp_patch->env[4].set = 0x00;
tmp_patch->env[5].set = 0x00;
tmp_patch->keep = 0;
tmp_patch->remove = 0;
token_count = 0;
while (line_tokens[token_count]) {
if (strncasecmp(line_tokens[token_count], "amp=", 4)
== 0) {
if (!wm_isdigit(line_tokens[token_count][4])) {
_WM_ERROR(__FUNCTION__, __LINE__,
WM_ERR_INVALID_ARG,
"(syntax error in patch line)", 0);
} else {
tmp_patch->amp = (atoi(
&line_tokens[token_count][4]) << 10)
/ 100;
}
} else if (strncasecmp(line_tokens[token_count],
"note=", 5) == 0) {
if (!wm_isdigit(line_tokens[token_count][5])) {
_WM_ERROR(__FUNCTION__, __LINE__,
WM_ERR_INVALID_ARG,
"(syntax error in patch line)", 0);
} else {
tmp_patch->note = atoi(
&line_tokens[token_count][5]);
}
} else if (strncasecmp(line_tokens[token_count],
"env_time", 8) == 0) {
if ((!wm_isdigit(line_tokens[token_count][8]))
|| (!wm_isdigit(
line_tokens[token_count][10]))
|| (line_tokens[token_count][9] != '=')) {
_WM_ERROR(__FUNCTION__, __LINE__,
WM_ERR_INVALID_ARG,
"(syntax error in patch line)", 0);
} else {
unsigned int env_no = atoi(
&line_tokens[token_count][8]);
if (env_no > 5) {
_WM_ERROR(__FUNCTION__, __LINE__,
WM_ERR_INVALID_ARG,
"(syntax error in patch line)",
0);
} else {
tmp_patch->env[env_no].time =
(float) atof(
&line_tokens[token_count][10]);
if ((tmp_patch->env[env_no].time
> 45000.0f)
|| (tmp_patch->env[env_no].time
< 1.47f)) {
_WM_ERROR(__FUNCTION__, __LINE__,
WM_ERR_INVALID_ARG,
"(range error in patch line)",
0);
tmp_patch->env[env_no].set &= 0xFE;
} else {
tmp_patch->env[env_no].set |= 0x01;
}
}
}
} else if (strncasecmp(line_tokens[token_count],
"env_level", 9) == 0) {
if ((!wm_isdigit(line_tokens[token_count][9]))
|| (!wm_isdigit(
line_tokens[token_count][11]))
|| (line_tokens[token_count][10] != '=')) {
_WM_ERROR(__FUNCTION__, __LINE__,
WM_ERR_INVALID_ARG,
"(syntax error in patch line)", 0);
} else {
unsigned int env_no = atoi(
&line_tokens[token_count][9]);
if (env_no > 5) {
_WM_ERROR(__FUNCTION__, __LINE__,
WM_ERR_INVALID_ARG,
"(syntax error in patch line)",
0);
} else {
tmp_patch->env[env_no].level =
(float) atof(
&line_tokens[token_count][11]);
if ((tmp_patch->env[env_no].level > 1.0f)
|| (tmp_patch->env[env_no].level
< 0.0f)) {
_WM_ERROR(__FUNCTION__, __LINE__,
WM_ERR_INVALID_ARG,
"(range error in patch line)",
0);
tmp_patch->env[env_no].set &= 0xFD;
} else {
tmp_patch->env[env_no].set |= 0x02;
}
}
}
} else if (strcasecmp(line_tokens[token_count],
"keep=loop") == 0) {
tmp_patch->keep |= SAMPLE_LOOP;
} else if (strcasecmp(line_tokens[token_count],
"keep=env") == 0) {
tmp_patch->keep |= SAMPLE_ENVELOPE;
} else if (strcasecmp(line_tokens[token_count],
"remove=sustain") == 0) {
tmp_patch->remove |= SAMPLE_SUSTAIN;
} else if (strcasecmp(line_tokens[token_count],
"remove=clamped") == 0) {
tmp_patch->remove |= SAMPLE_CLAMPED;
}
token_count++;
}
}
}
/* free up tokens */
free(line_tokens);
}
line_start_ptr = config_ptr + 1;
}
config_ptr++;
}
free(config_buffer);
free(config_dir);
return 0;
}
/* sample loading */
static int load_sample(struct _patch *sample_patch) {
struct _sample *guspat = NULL;
struct _sample *tmp_sample = NULL;
unsigned int i = 0;
/* we only want to try loading the guspat once. */
sample_patch->loaded = 1;
if ((guspat = _WM_load_gus_pat(sample_patch->filename, fix_release)) == NULL) {
return -1;
}
if (auto_amp) {
signed short int tmp_max = 0;
signed short int tmp_min = 0;
signed short samp_max = 0;
signed short samp_min = 0;
tmp_sample = guspat;
do {
samp_max = 0;
samp_min = 0;
for (i = 0; i < (tmp_sample->data_length >> 10); i++) {
if (tmp_sample->data[i] > samp_max)
samp_max = tmp_sample->data[i];
if (tmp_sample->data[i] < samp_min)
samp_min = tmp_sample->data[i];
}
if (samp_max > tmp_max)
tmp_max = samp_max;
if (samp_min < tmp_min)
tmp_min = samp_min;
tmp_sample = tmp_sample->next;
} while (tmp_sample);
if (auto_amp_with_amp) {
if (tmp_max >= -tmp_min) {
sample_patch->amp = (sample_patch->amp
* ((32767 << 10) / tmp_max)) >> 10;
} else {
sample_patch->amp = (sample_patch->amp
* ((32768 << 10) / -tmp_min)) >> 10;
}
} else {
if (tmp_max >= -tmp_min) {
sample_patch->amp = (32767 << 10) / tmp_max;
} else {
sample_patch->amp = (32768 << 10) / -tmp_min;
}
}
}
sample_patch->first_sample = guspat;
if (sample_patch->patchid & 0x0080) {
if (!(sample_patch->keep & SAMPLE_LOOP)) {
do {
guspat->modes &= 0xFB;
guspat = guspat->next;
} while (guspat);
}
guspat = sample_patch->first_sample;
if (!(sample_patch->keep & SAMPLE_ENVELOPE)) {
do {
guspat->modes &= 0xBF;
guspat = guspat->next;
} while (guspat);
}
guspat = sample_patch->first_sample;
}
if (sample_patch->patchid == 47) {
do {
if (!(guspat->modes & SAMPLE_LOOP)) {
for (i = 3; i < 6; i++) {
guspat->env_target[i] = guspat->env_target[2];
guspat->env_rate[i] = guspat->env_rate[2];
}
}
guspat = guspat->next;
} while (guspat);
guspat = sample_patch->first_sample;
}
do {
if ((sample_patch->remove & SAMPLE_SUSTAIN)
&& (guspat->modes & SAMPLE_SUSTAIN)) {
guspat->modes ^= SAMPLE_SUSTAIN;
}
if ((sample_patch->remove & SAMPLE_CLAMPED)
&& (guspat->modes & SAMPLE_CLAMPED)) {
guspat->modes ^= SAMPLE_CLAMPED;
}
if (sample_patch->keep & SAMPLE_ENVELOPE) {
guspat->modes |= SAMPLE_ENVELOPE;
}
for (i = 0; i < 6; i++) {
if (guspat->modes & SAMPLE_ENVELOPE) {
if (sample_patch->env[i].set & 0x02) {
guspat->env_target[i] = 16448
* (signed long int) (255.0
* sample_patch->env[i].level);
}
if (sample_patch->env[i].set & 0x01) {
guspat->env_rate[i] = (signed long int) (4194303.0
/ ((float) _WM_SampleRate
* (sample_patch->env[i].time / 1000.0)));
}
} else {
guspat->env_target[i] = 4194303;
guspat->env_rate[i] = (signed long int) (4194303.0
/ ((float) _WM_SampleRate * env_time_table[63]));
}
}
guspat = guspat->next;
} while (guspat);
return 0;
}
static struct _patch *
get_patch_data(unsigned short patchid) {
struct _patch *search_patch;
_WM_Lock(&patch_lock);
search_patch = patch[patchid & 0x007F];
if (search_patch == NULL) {
_WM_Unlock(&patch_lock);
return NULL;
}
while (search_patch) {
if (search_patch->patchid == patchid) {
_WM_Unlock(&patch_lock);
return search_patch;
}
search_patch = search_patch->next;
}
if ((patchid >> 8) != 0) {
_WM_Unlock(&patch_lock);
return (get_patch_data(patchid & 0x00FF));
}
_WM_Unlock(&patch_lock);
return NULL;
}
static void load_patch(struct _mdi *mdi, unsigned short patchid) {
unsigned int i;
struct _patch *tmp_patch = NULL;
for (i = 0; i < mdi->patch_count; i++) {
if (mdi->patches[i]->patchid == patchid) {
return;
}
}
tmp_patch = get_patch_data(patchid);
if (tmp_patch == NULL) {
return;
}
_WM_Lock(&patch_lock);
if (!tmp_patch->loaded) {
if (load_sample(tmp_patch) == -1) {
_WM_Unlock(&patch_lock);
return;
}
}
if (tmp_patch->first_sample == NULL) {
_WM_Unlock(&patch_lock);
return;
}
mdi->patch_count++;
mdi->patches = (struct _patch**)realloc(mdi->patches,
(sizeof(struct _patch*) * mdi->patch_count));
mdi->patches[mdi->patch_count - 1] = tmp_patch;
tmp_patch->inuse_count++;
_WM_Unlock(&patch_lock);
}
static struct _sample *
get_sample_data(struct _patch *sample_patch, unsigned long int freq) {
struct _sample *last_sample = NULL;
struct _sample *return_sample = NULL;
_WM_Lock(&patch_lock);
if (sample_patch == NULL) {
_WM_Unlock(&patch_lock);
return NULL;
}
if (sample_patch->first_sample == NULL) {
_WM_Unlock(&patch_lock);
return NULL;
}
if (freq == 0) {
_WM_Unlock(&patch_lock);
return sample_patch->first_sample;
}
return_sample = sample_patch->first_sample;
last_sample = sample_patch->first_sample;
while (last_sample) {
if (freq > last_sample->freq_low) {
if (freq < last_sample->freq_high) {
_WM_Unlock(&patch_lock);
return last_sample;
} else {
return_sample = last_sample;
}
}
last_sample = last_sample->next;
}
_WM_Unlock(&patch_lock);
return return_sample;
}
static void do_note_off_extra(struct _note *nte) {
nte->is_off = 0;
if (nte->hold) {
nte->hold |= HOLD_OFF;
} else {
if (!(nte->modes & SAMPLE_ENVELOPE)) {
if (nte->modes & SAMPLE_LOOP) {
nte->modes ^= SAMPLE_LOOP;
}
nte->env_inc = 0;
} else if (nte->modes & SAMPLE_CLAMPED) {
if (nte->env < 5) {
nte->env = 5;
if (nte->env_level > nte->sample->env_target[5]) {
nte->env_inc = -nte->sample->env_rate[5];
} else {
nte->env_inc = nte->sample->env_rate[5];
}
}
#if 1
} else if (nte->modes & SAMPLE_SUSTAIN) {
if (nte->env < 3) {
nte->env = 3;
if (nte->env_level > nte->sample->env_target[3]) {
nte->env_inc = -nte->sample->env_rate[3];
} else {
nte->env_inc = nte->sample->env_rate[3];
}
}
#endif
} else if (nte->env < 4) {
nte->env = 4;
if (nte->env_level > nte->sample->env_target[4]) {
nte->env_inc = -nte->sample->env_rate[4];
} else {
nte->env_inc = nte->sample->env_rate[4];
}
}
}
}
static void do_note_off(struct _mdi *mdi, struct _event_data *data) {
struct _note *nte;
unsigned char ch = data->channel;
MIDI_EVENT_DEBUG(__FUNCTION__,ch);
nte = &mdi->note_table[0][ch][(data->data >> 8)];
if (!nte->active)
nte = &mdi->note_table[1][ch][(data->data >> 8)];
if (!nte->active) {
return;
}
if ((mdi->channel[ch].isdrum) && (!(nte->modes & SAMPLE_LOOP))) {
return;
}
if (nte->env == 0) {
nte->is_off = 1;
} else {
do_note_off_extra(nte);
}
}
static inline unsigned long int get_inc(struct _mdi *mdi, struct _note *nte) {
int ch = nte->noteid >> 8;
signed long int note_f;
unsigned long int freq;
if (__builtin_expect((nte->patch->note != 0), 0)) {
note_f = nte->patch->note * 100;
} else {
note_f = (nte->noteid & 0x7f) * 100;
}
note_f += mdi->channel[ch].pitch_adjust;
if (__builtin_expect((note_f < 0), 0)) {
note_f = 0;
} else if (__builtin_expect((note_f > 12700), 0)) {
note_f = 12700;
}
freq = freq_table[(note_f % 1200)] >> (10 - (note_f / 1200));
return (((freq / ((_WM_SampleRate * 100) / 1024)) * 1024
/ nte->sample->inc_div));
}
static inline unsigned long int get_volume(struct _mdi *mdi, unsigned char ch,
struct _note *nte) {
signed long int volume;
if (mdi->info.mixer_options & WM_MO_LOG_VOLUME) {
volume = (sqr_volume[mdi->channel[ch].volume]
* sqr_volume[mdi->channel[ch].expression]
* sqr_volume[nte->velocity]) / 1048576;
} else {
volume = (lin_volume[mdi->channel[ch].volume]
* lin_volume[mdi->channel[ch].expression]
* lin_volume[nte->velocity]) / 1048576;
}
volume = volume * nte->patch->amp / 100;
return (volume);
}
static void do_note_on(struct _mdi *mdi, struct _event_data *data) {
struct _note *nte;
struct _note *prev_nte;
struct _note *nte_array;
unsigned long int freq = 0;
struct _patch *patch;
struct _sample *sample;
unsigned char ch = data->channel;
unsigned char note = (unsigned char)(data->data >> 8);
unsigned char velocity = (data->data & 0xFF);
if (velocity == 0x00) {
do_note_off(mdi, data);
return;
}
MIDI_EVENT_DEBUG(__FUNCTION__,ch);
if (!mdi->channel[ch].isdrum) {
patch = mdi->channel[ch].patch;
if (patch == NULL) {
return;
}
freq = freq_table[(note % 12) * 100] >> (10 - (note / 12));
} else {
patch = get_patch_data(((mdi->channel[ch].bank << 8) | note | 0x80));
if (patch == NULL) {
return;
}
if (patch->note) {
freq = freq_table[(patch->note % 12) * 100]
>> (10 - (patch->note / 12));
} else {
freq = freq_table[(note % 12) * 100] >> (10 - (note / 12));
}
}
sample = get_sample_data(patch, (freq / 100));
if (sample == NULL) {
return;
}
nte = &mdi->note_table[0][ch][note];
if (nte->active) {
if ((nte->modes & SAMPLE_ENVELOPE) && (nte->env < 3)
&& (!(nte->hold & HOLD_OFF)))
return;
nte->replay = &mdi->note_table[1][ch][note];
nte->env = 6;
nte->env_inc = -nte->sample->env_rate[6];
nte = nte->replay;
} else {
if (mdi->note_table[1][ch][note].active) {
if ((nte->modes & SAMPLE_ENVELOPE) && (nte->env < 3)
&& (!(nte->hold & HOLD_OFF)))
return;
mdi->note_table[1][ch][note].replay = nte;
mdi->note_table[1][ch][note].env = 6;
mdi->note_table[1][ch][note].env_inc =
-mdi->note_table[1][ch][note].sample->env_rate[6];
} else {
nte_array = mdi->note;
if (nte_array == NULL) {
mdi->note = nte;
} else {
do {
prev_nte = nte_array;
nte_array = nte_array->next;
} while (nte_array);
prev_nte->next = nte;
}
nte->active = 1;
nte->next = NULL;
}
}
nte->noteid = (ch << 8) | note;
nte->patch = patch;
nte->sample = sample;
nte->sample_pos = 0;
nte->sample_inc = get_inc(mdi, nte);
nte->velocity = velocity;
nte->env = 0;
nte->env_inc = nte->sample->env_rate[0];
nte->env_level = 0;
nte->modes = sample->modes;
nte->hold = mdi->channel[ch].hold;
nte->vol_lvl = get_volume(mdi, ch, nte);
nte->replay = NULL;
nte->is_off = 0;
}
static void do_aftertouch(struct _mdi *mdi, struct _event_data *data) {
struct _note *nte;
unsigned char ch = data->channel;
MIDI_EVENT_DEBUG(__FUNCTION__,ch);
nte = &mdi->note_table[0][ch][(data->data >> 8)];
if (!nte->active) {
nte = &mdi->note_table[1][ch][(data->data >> 8)];
if (!nte->active) {
return;
}
}
nte->velocity = data->data & 0xff;
nte->vol_lvl = get_volume(mdi, ch, nte);
if (nte->replay) {
nte->replay->velocity = data->data & 0xff;
nte->replay->vol_lvl = get_volume(mdi, ch, nte->replay);
}
}
static void do_pan_adjust(struct _mdi *mdi, unsigned char ch) {
signed short int pan_adjust = mdi->channel[ch].balance
+ mdi->channel[ch].pan;
signed short int left, right;
int amp = 32;
if (pan_adjust > 63) {
pan_adjust = 63;
} else if (pan_adjust < -64) {
pan_adjust = -64;
}
pan_adjust += 64;
/* if (mdi->info.mixer_options & WM_MO_LOG_VOLUME) {*/
left = (pan_volume[127 - pan_adjust] * WM_MasterVolume * amp) / 1048576;
right = (pan_volume[pan_adjust] * WM_MasterVolume * amp) / 1048576;
/* } else {
left = (lin_volume[127 - pan_adjust] * WM_MasterVolume * amp) / 1048576;
right= (lin_volume[pan_adjust] * WM_MasterVolume * amp) / 1048576;
}*/
mdi->channel[ch].left_adjust = left;
mdi->channel[ch].right_adjust = right;
}
static void do_control_bank_select(struct _mdi *mdi, struct _event_data *data) {
unsigned char ch = data->channel;
mdi->channel[ch].bank = (unsigned char)data->data;
}
static void do_control_data_entry_course(struct _mdi *mdi,
struct _event_data *data) {
unsigned char ch = data->channel;
int data_tmp;
if ((mdi->channel[ch].reg_non == 0)
&& (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */
data_tmp = mdi->channel[ch].pitch_range % 100;
mdi->channel[ch].pitch_range = short(data->data * 100 + data_tmp);
/* printf("Data Entry Course: pitch_range: %i\n\r",mdi->channel[ch].pitch_range);*/
/* printf("Data Entry Course: data %li\n\r",data->data);*/
}
}
static void do_control_channel_volume(struct _mdi *mdi,
struct _event_data *data) {
struct _note *note_data = mdi->note;
unsigned char ch = data->channel;
mdi->channel[ch].volume = (unsigned char)data->data;
if (note_data) {
do {
if ((note_data->noteid >> 8) == ch) {
note_data->vol_lvl = get_volume(mdi, ch, note_data);
if (note_data->replay)
note_data->replay->vol_lvl = get_volume(mdi, ch,
note_data->replay);
}
note_data = note_data->next;
} while (note_data);
}
}
static void do_control_channel_balance(struct _mdi *mdi,
struct _event_data *data) {
unsigned char ch = data->channel;
mdi->channel[ch].balance = (signed char)(data->data - 64);
do_pan_adjust(mdi, ch);
}
static void do_control_channel_pan(struct _mdi *mdi, struct _event_data *data) {
unsigned char ch = data->channel;
mdi->channel[ch].pan = signed char(data->data - 64);
do_pan_adjust(mdi, ch);
}
static void do_control_channel_expression(struct _mdi *mdi,
struct _event_data *data) {
struct _note *note_data = mdi->note;
unsigned char ch = data->channel;
mdi->channel[ch].expression = (unsigned char)data->data;
if (note_data) {
do {
if ((note_data->noteid >> 8) == ch) {
note_data->vol_lvl = get_volume(mdi, ch, note_data);
if (note_data->replay)
note_data->replay->vol_lvl = get_volume(mdi, ch,
note_data->replay);
}
note_data = note_data->next;
} while (note_data);
}
}
static void do_control_data_entry_fine(struct _mdi *mdi,
struct _event_data *data) {
unsigned char ch = data->channel;
int data_tmp;
if ((mdi->channel[ch].reg_non == 0)
&& (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */
data_tmp = mdi->channel[ch].pitch_range / 100;
mdi->channel[ch].pitch_range = short((data_tmp * 100) + data->data);
/* printf("Data Entry Fine: pitch_range: %i\n\r",mdi->channel[ch].pitch_range);*/
/* printf("Data Entry Fine: data: %li\n\r", data->data);*/
}
}
static void do_control_channel_hold(struct _mdi *mdi, struct _event_data *data) {
struct _note *note_data = mdi->note;
unsigned char ch = data->channel;
if (data->data > 63) {
mdi->channel[ch].hold = 1;
} else {
mdi->channel[ch].hold = 0;
if (note_data) {
do {
if ((note_data->noteid >> 8) == ch) {
if (note_data->hold & HOLD_OFF) {
if (note_data->modes & SAMPLE_ENVELOPE) {
if (note_data->modes & SAMPLE_CLAMPED) {
if (note_data->env < 5) {
note_data->env = 5;
if (note_data->env_level
> note_data->sample->env_target[5]) {
note_data->env_inc =
-note_data->sample->env_rate[5];
} else {
note_data->env_inc =
note_data->sample->env_rate[5];
}
}
} else if (note_data->env < 4) {
note_data->env = 4;
if (note_data->env_level
> note_data->sample->env_target[4]) {
note_data->env_inc =
-note_data->sample->env_rate[4];
} else {
note_data->env_inc =
note_data->sample->env_rate[4];
}
}
} else {
if (note_data->modes & SAMPLE_LOOP) {
note_data->modes ^= SAMPLE_LOOP;
}
note_data->env_inc = 0;
}
}
note_data->hold = 0x00;
}
note_data = note_data->next;
} while (note_data);
}
}
}
static void do_control_data_increment(struct _mdi *mdi,
struct _event_data *data) {
unsigned char ch = data->channel;
if ((mdi->channel[ch].reg_non == 0)
&& (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */
if (mdi->channel[ch].pitch_range < 0x3FFF)
mdi->channel[ch].pitch_range++;
}
}
static void do_control_data_decrement(struct _mdi *mdi,
struct _event_data *data) {
unsigned char ch = data->channel;
if ((mdi->channel[ch].reg_non == 0)
&& (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */
if (mdi->channel[ch].pitch_range > 0)
mdi->channel[ch].pitch_range--;
}
}
static void do_control_non_registered_param(struct _mdi *mdi,
struct _event_data *data) {
unsigned char ch = data->channel;
mdi->channel[ch].reg_non = 1;
}
static void do_control_registered_param_fine(struct _mdi *mdi,
struct _event_data *data) {
unsigned char ch = data->channel;
mdi->channel[ch].reg_data = (unsigned short) ((mdi->channel[ch].reg_data & 0x3F80)
| data->data);
mdi->channel[ch].reg_non = 0;
}
static void do_control_registered_param_course(struct _mdi *mdi,
struct _event_data *data) {
unsigned char ch = data->channel;
mdi->channel[ch].reg_data = (unsigned short) ((mdi->channel[ch].reg_data & 0x7F)
| (data->data << 7));
mdi->channel[ch].reg_non = 0;
}
static void do_control_channel_sound_off(struct _mdi *mdi,
struct _event_data *data) {
struct _note *note_data = mdi->note;
unsigned char ch = data->channel;
if (note_data) {
do {
if ((note_data->noteid >> 8) == ch) {
note_data->active = 0;
if (note_data->replay) {
note_data->replay = NULL;
}
}
note_data = note_data->next;
} while (note_data);
}
}
static void do_control_channel_controllers_off(struct _mdi *mdi,
struct _event_data *data) {
struct _note *note_data = mdi->note;
unsigned char ch = data->channel;
mdi->channel[ch].expression = 127;
mdi->channel[ch].pressure = 127;
mdi->channel[ch].volume = 100;
mdi->channel[ch].pan = 0;
mdi->channel[ch].balance = 0;
mdi->channel[ch].reg_data = 0xffff;
mdi->channel[ch].pitch_range = 200;
mdi->channel[ch].pitch = 0;
mdi->channel[ch].pitch_adjust = 0;
mdi->channel[ch].hold = 0;
do_pan_adjust(mdi, ch);
if (note_data) {
do {
if ((note_data->noteid >> 8) == ch) {
note_data->sample_inc = get_inc(mdi, note_data);
note_data->velocity = 0;
note_data->vol_lvl = get_volume(mdi, ch, note_data);
note_data->hold = 0;
if (note_data->replay) {
note_data->replay->velocity = (unsigned char)data->data;
note_data->replay->vol_lvl = get_volume(mdi, ch,
note_data->replay);
}
}
note_data = note_data->next;
} while (note_data);
}
}
static void do_control_channel_notes_off(struct _mdi *mdi,
struct _event_data *data) {
struct _note *note_data = mdi->note;
unsigned char ch = data->channel;
if (mdi->channel[ch].isdrum)
return;
if (note_data) {
do {
if ((note_data->noteid >> 8) == ch) {
if (!note_data->hold) {
if (note_data->modes & SAMPLE_ENVELOPE) {
if (note_data->env < 5) {
if (note_data->env_level
> note_data->sample->env_target[5]) {
note_data->env_inc =
-note_data->sample->env_rate[5];
} else {
note_data->env_inc =
note_data->sample->env_rate[5];
}
note_data->env = 5;
}
}
} else {
note_data->hold |= HOLD_OFF;
}
}
note_data = note_data->next;
} while (note_data);
}
}
static void do_patch(struct _mdi *mdi, struct _event_data *data) {
unsigned char ch = data->channel;
MIDI_EVENT_DEBUG(__FUNCTION__,ch);
if (!mdi->channel[ch].isdrum) {
mdi->channel[ch].patch = get_patch_data((unsigned short)(((mdi->channel[ch].bank << 8) | data->data)));
} else {
mdi->channel[ch].bank = (unsigned char)data->data;
}
}
static void do_channel_pressure(struct _mdi *mdi, struct _event_data *data) {
struct _note *note_data = mdi->note;
unsigned char ch = data->channel;
MIDI_EVENT_DEBUG(__FUNCTION__,ch);
if (note_data) {
do {
if ((note_data->noteid >> 8) == ch) {
note_data->velocity = (unsigned char)data->data;
note_data->vol_lvl = get_volume(mdi, ch, note_data);
if (note_data->replay) {
note_data->replay->velocity = (unsigned char)data->data;
note_data->replay->vol_lvl = get_volume(mdi, ch,
note_data->replay);
}
}
note_data = note_data->next;
} while (note_data);
}
}
static void do_pitch(struct _mdi *mdi, struct _event_data *data) {
struct _note *note_data = mdi->note;
unsigned char ch = data->channel;
MIDI_EVENT_DEBUG(__FUNCTION__,ch);
mdi->channel[ch].pitch = short(data->data - 0x2000);
if (mdi->channel[ch].pitch < 0) {
mdi->channel[ch].pitch_adjust = mdi->channel[ch].pitch_range
* mdi->channel[ch].pitch / 8192;
} else {
mdi->channel[ch].pitch_adjust = mdi->channel[ch].pitch_range
* mdi->channel[ch].pitch / 8191;
}
if (note_data) {
do {
if ((note_data->noteid >> 8) == ch) {
note_data->sample_inc = get_inc(mdi, note_data);
}
note_data = note_data->next;
} while (note_data);
}
}
static void do_sysex_roland_drum_track(struct _mdi *mdi,
struct _event_data *data) {
unsigned char ch = data->channel;
MIDI_EVENT_DEBUG(__FUNCTION__,ch);
if (data->data > 0) {
mdi->channel[ch].isdrum = 1;
mdi->channel[ch].patch = NULL;
} else {
mdi->channel[ch].isdrum = 0;
mdi->channel[ch].patch = get_patch_data(0);
}
}
static void do_sysex_roland_reset(struct _mdi *mdi, struct _event_data *data) {
int i;
for (i = 0; i < 16; i++) {
mdi->channel[i].bank = 0;
if (i != 9) {
mdi->channel[i].patch = get_patch_data(0);
} else {
mdi->channel[i].patch = NULL;
}
mdi->channel[i].hold = 0;
mdi->channel[i].volume = 100;
mdi->channel[i].pressure = 127;
mdi->channel[i].expression = 127;
mdi->channel[i].balance = 0;
mdi->channel[i].pan = 0;
mdi->channel[i].left_adjust = 1;
mdi->channel[i].right_adjust = 1;
mdi->channel[i].pitch = 0;
mdi->channel[i].pitch_range = 200;
mdi->channel[i].reg_data = 0xFFFF;
mdi->channel[i].isdrum = 0;
do_pan_adjust(mdi, i);
}
mdi->channel[9].isdrum = 1;
UNUSED(data); /* NOOP, to please the compiler gods */
}
static void WM_ResetToStart(midi * handle) {
struct _mdi *mdi = (struct _mdi *) handle;
mdi->current_event = mdi->events;
mdi->samples_to_mix = 0;
mdi->info.current_sample = 0;
do_sysex_roland_reset(mdi, NULL);
}
static int midi_setup_noteoff(struct _mdi *mdi, unsigned char channel,
unsigned char note, unsigned char velocity) {
if ((mdi->event_count)
&& (mdi->events[mdi->event_count - 1].do_event == NULL)) {
mdi->events[mdi->event_count - 1].do_event = *do_note_off;
mdi->events[mdi->event_count - 1].event_data.channel = channel;
mdi->events[mdi->event_count - 1].event_data.data = (note << 8)
| velocity;
} else {
WM_CheckEventMemoryPool(mdi);
mdi->events[mdi->event_count].do_event = *do_note_off;
mdi->events[mdi->event_count].event_data.channel = channel;
mdi->events[mdi->event_count].event_data.data = (note << 8) | velocity;
mdi->events[mdi->event_count].samples_to_next = 0;
mdi->event_count++;
}
return 0;
}
static int midi_setup_noteon(struct _mdi *mdi, unsigned char channel,
unsigned char note, unsigned char velocity) {
if ((mdi->event_count)
&& (mdi->events[mdi->event_count - 1].do_event == NULL)) {
mdi->events[mdi->event_count - 1].do_event = *do_note_on;
mdi->events[mdi->event_count - 1].event_data.channel = channel;
mdi->events[mdi->event_count - 1].event_data.data = (note << 8)
| velocity;
} else {
WM_CheckEventMemoryPool(mdi);
mdi->events[mdi->event_count].do_event = *do_note_on;
mdi->events[mdi->event_count].event_data.channel = channel;
mdi->events[mdi->event_count].event_data.data = (note << 8) | velocity;
mdi->events[mdi->event_count].samples_to_next = 0;
mdi->event_count++;
}
if (mdi->channel[channel].isdrum)
load_patch(mdi, ((mdi->channel[channel].bank << 8) | (note | 0x80)));
return 0;
}
static int midi_setup_aftertouch(struct _mdi *mdi, unsigned char channel,
unsigned char note, unsigned char pressure) {
if ((mdi->event_count)
&& (mdi->events[mdi->event_count - 1].do_event == NULL)) {
mdi->events[mdi->event_count - 1].do_event = *do_aftertouch;
mdi->events[mdi->event_count - 1].event_data.channel = channel;
mdi->events[mdi->event_count - 1].event_data.data = (note << 8)
| pressure;
} else {
WM_CheckEventMemoryPool(mdi);
mdi->events[mdi->event_count].do_event = *do_aftertouch;
mdi->events[mdi->event_count].event_data.channel = channel;
mdi->events[mdi->event_count].event_data.data = (note << 8) | pressure;
mdi->events[mdi->event_count].samples_to_next = 0;
mdi->event_count++;
}
return 0;
}
static int midi_setup_control(struct _mdi *mdi, unsigned char channel,
unsigned char controller, unsigned char setting) {
void (*tmp_event)(struct _mdi *mdi, struct _event_data *data) = NULL;
switch (controller) {
case 0:
tmp_event = *do_control_bank_select;
mdi->channel[channel].bank = setting;
break;
case 6:
tmp_event = *do_control_data_entry_course;
break;
case 7:
tmp_event = *do_control_channel_volume;
mdi->channel[channel].volume = setting;
break;
case 8:
tmp_event = *do_control_channel_balance;
break;
case 10:
tmp_event = *do_control_channel_pan;
break;
case 11:
tmp_event = *do_control_channel_expression;
break;
case 38:
tmp_event = *do_control_data_entry_fine;
break;
case 64:
tmp_event = *do_control_channel_hold;
break;
case 96:
tmp_event = *do_control_data_increment;
break;
case 97:
tmp_event = *do_control_data_decrement;
break;
case 98:
case 99:
tmp_event = *do_control_non_registered_param;
break;
case 100:
tmp_event = *do_control_registered_param_fine;
break;
case 101:
tmp_event = *do_control_registered_param_course;
break;
case 120:
tmp_event = *do_control_channel_sound_off;
break;
case 121:
tmp_event = *do_control_channel_controllers_off;
break;
case 123:
tmp_event = *do_control_channel_notes_off;
break;
default:
return 0;
}
if ((mdi->event_count)
&& (mdi->events[mdi->event_count - 1].do_event == NULL)) {
mdi->events[mdi->event_count - 1].do_event = tmp_event;
mdi->events[mdi->event_count - 1].event_data.channel = channel;
mdi->events[mdi->event_count - 1].event_data.data = setting;
} else {
WM_CheckEventMemoryPool(mdi);
mdi->events[mdi->event_count].do_event = tmp_event;
mdi->events[mdi->event_count].event_data.channel = channel;
mdi->events[mdi->event_count].event_data.data = setting;
mdi->events[mdi->event_count].samples_to_next = 0;
mdi->event_count++;
}
return 0;
}
static int midi_setup_patch(struct _mdi *mdi, unsigned char channel,
unsigned char patch) {
if ((mdi->event_count)
&& (mdi->events[mdi->event_count - 1].do_event == NULL)) {
mdi->events[mdi->event_count - 1].do_event = *do_patch;
mdi->events[mdi->event_count - 1].event_data.channel = channel;
mdi->events[mdi->event_count - 1].event_data.data = patch;
} else {
WM_CheckEventMemoryPool(mdi);
mdi->events[mdi->event_count].do_event = *do_patch;
mdi->events[mdi->event_count].event_data.channel = channel;
mdi->events[mdi->event_count].event_data.data = patch;
mdi->events[mdi->event_count].samples_to_next = 0;
mdi->event_count++;
}
if (mdi->channel[channel].isdrum) {
mdi->channel[channel].bank = patch;
} else {
load_patch(mdi, ((mdi->channel[channel].bank << 8) | patch));
mdi->channel[channel].patch = get_patch_data(
((mdi->channel[channel].bank << 8) | patch));
}
return 0;
}
static int midi_setup_channel_pressure(struct _mdi *mdi, unsigned char channel,
unsigned char pressure) {
if ((mdi->event_count)
&& (mdi->events[mdi->event_count - 1].do_event == NULL)) {
mdi->events[mdi->event_count - 1].do_event = *do_channel_pressure;
mdi->events[mdi->event_count - 1].event_data.channel = channel;
mdi->events[mdi->event_count - 1].event_data.data = pressure;
} else {
WM_CheckEventMemoryPool(mdi);
mdi->events[mdi->event_count].do_event = *do_channel_pressure;
mdi->events[mdi->event_count].event_data.channel = channel;
mdi->events[mdi->event_count].event_data.data = pressure;
mdi->events[mdi->event_count].samples_to_next = 0;
mdi->event_count++;
}
return 0;
}
static int midi_setup_pitch(struct _mdi *mdi, unsigned char channel,
unsigned short pitch) {
if ((mdi->event_count)
&& (mdi->events[mdi->event_count - 1].do_event == NULL)) {
mdi->events[mdi->event_count - 1].do_event = *do_pitch;
mdi->events[mdi->event_count - 1].event_data.channel = channel;
mdi->events[mdi->event_count - 1].event_data.data = pitch;
} else {
WM_CheckEventMemoryPool(mdi);
mdi->events[mdi->event_count].do_event = *do_pitch;
mdi->events[mdi->event_count].event_data.channel = channel;
mdi->events[mdi->event_count].event_data.data = pitch;
mdi->events[mdi->event_count].samples_to_next = 0;
mdi->event_count++;
}
return 0;
}
static int midi_setup_sysex_roland_drum_track(struct _mdi *mdi,
unsigned char channel, unsigned short setting) {
if ((mdi->event_count)
&& (mdi->events[mdi->event_count - 1].do_event == NULL)) {
mdi->events[mdi->event_count - 1].do_event =
*do_sysex_roland_drum_track;
mdi->events[mdi->event_count - 1].event_data.channel = channel;
mdi->events[mdi->event_count - 1].event_data.data = setting;
} else {
WM_CheckEventMemoryPool(mdi);
mdi->events[mdi->event_count].do_event = *do_sysex_roland_drum_track;
mdi->events[mdi->event_count].event_data.channel = channel;
mdi->events[mdi->event_count].event_data.data = setting;
mdi->events[mdi->event_count].samples_to_next = 0;
mdi->event_count++;
}
if (setting > 0) {
mdi->channel[channel].isdrum = 1;
} else {
mdi->channel[channel].isdrum = 0;
}
return 0;
}
static int midi_setup_sysex_roland_reset(struct _mdi *mdi) {
if ((mdi->event_count)
&& (mdi->events[mdi->event_count - 1].do_event == NULL)) {
mdi->events[mdi->event_count - 1].do_event = *do_sysex_roland_reset;
mdi->events[mdi->event_count - 1].event_data.channel = 0;
mdi->events[mdi->event_count - 1].event_data.data = 0;
} else {
WM_CheckEventMemoryPool(mdi);
mdi->events[mdi->event_count].do_event = *do_sysex_roland_reset;
mdi->events[mdi->event_count].event_data.channel = 0;
mdi->events[mdi->event_count].event_data.data = 0;
mdi->events[mdi->event_count].samples_to_next = 0;
mdi->event_count++;
}
return 0;
}
static int add_handle(void * handle) {
struct _hndl *tmp_handle = NULL;
if (first_handle == NULL) {
first_handle = (struct _hndl*)malloc(sizeof(struct _hndl));
if (first_handle == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, " to get ram", errno);
return -1;
}
first_handle->handle = handle;
first_handle->prev = NULL;
first_handle->next = NULL;
} else {
tmp_handle = first_handle;
if (tmp_handle->next) {
while (tmp_handle->next)
tmp_handle = tmp_handle->next;
}
tmp_handle->next = (struct _hndl*)malloc(sizeof(struct _hndl));
if (tmp_handle->next == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, " to get ram", errno);
return -1;
}
tmp_handle->next->prev = tmp_handle;
tmp_handle = tmp_handle->next;
tmp_handle->next = NULL;
tmp_handle->handle = handle;
}
return 0;
}
static struct _mdi *
Init_MDI(void) {
struct _mdi *mdi;
mdi = (struct _mdi*)malloc(sizeof(struct _mdi));
memset(mdi, 0, (sizeof(struct _mdi)));
mdi->info.copyright = NULL;
mdi->info.mixer_options = WM_MixerOptions;
load_patch(mdi, 0x0000);
mdi->events_size = MEM_CHUNK;
mdi->events = (struct _event*)malloc(mdi->events_size * sizeof(struct _event));
mdi->events[0].do_event = NULL;
mdi->events[0].event_data.channel = 0;
mdi->events[0].event_data.data = 0;
mdi->events[0].samples_to_next = 0;
mdi->event_count++;
mdi->current_event = mdi->events;
mdi->samples_to_mix = 0;
mdi->info.current_sample = 0;
mdi->info.total_midi_time = 0;
mdi->info.approx_total_samples = 0;
do_sysex_roland_reset(mdi, NULL);
return mdi;
}
static void freeMDI(struct _mdi *mdi) {
struct _sample *tmp_sample;
unsigned long int i;
if (mdi->patch_count != 0) {
_WM_Lock(&patch_lock);
for (i = 0; i < mdi->patch_count; i++) {
mdi->patches[i]->inuse_count--;
if (mdi->patches[i]->inuse_count == 0) {
/* free samples here */
while (mdi->patches[i]->first_sample) {
tmp_sample = mdi->patches[i]->first_sample->next;
free(mdi->patches[i]->first_sample->data);
free(mdi->patches[i]->first_sample);
mdi->patches[i]->first_sample = tmp_sample;
}
mdi->patches[i]->loaded = 0;
}
}
_WM_Unlock(&patch_lock);
free(mdi->patches);
}
free(mdi->events);
free(mdi->tmp_info);
_WM_free_reverb(mdi->reverb);
free(mdi->mix_buffer);
free(mdi);
}
static unsigned long int get_decay_samples(struct _patch *patch, unsigned char note) {
struct _sample *sample = NULL;
unsigned long int freq = 0;
unsigned long int decay_samples = 0;
if (patch == NULL)
return 0;
/* first get the freq we need so we can check the right sample */
if (patch->patchid & 0x80) {
/* is a drum patch */
if (patch->note) {
freq = freq_table[(patch->note % 12) * 100]
>> (10 - (patch->note / 12));
} else {
freq = freq_table[(note % 12) * 100] >> (10 - (note / 12));
}
} else {
freq = freq_table[(note % 12) * 100] >> (10 - (note / 12));
}
/* get the sample */
sample = get_sample_data(patch, (freq / 100));
if (sample == NULL)
return 0;
if (patch->patchid & 0x80) {
float sratedata = ((float) sample->rate / (float) _WM_SampleRate)
* (float) (sample->data_length >> 10);
decay_samples = (unsigned long int) sratedata;
/* printf("Drums (%i / %i) * %lu = %f\n", sample->rate, _WM_SampleRate, (sample->data_length >> 10), sratedata);*/
} else if (sample->modes & SAMPLE_CLAMPED) {
decay_samples = (4194303 / sample->env_rate[5]);
/* printf("clamped 4194303 / %lu = %lu\n", sample->env_rate[5], decay_samples);*/
} else {
decay_samples =
((4194303 - sample->env_target[4]) / sample->env_rate[4])
+ (sample->env_target[4] / sample->env_rate[5]);
/* printf("NOT clamped ((4194303 - %lu) / %lu) + (%lu / %lu)) = %lu\n", sample->env_target[4], sample->env_rate[4], sample->env_target[4], sample->env_rate[5], decay_samples);*/
}
return decay_samples;
}
static struct _mdi *
WM_ParseNewMidi(unsigned char *midi_data, unsigned int midi_size) {
struct _mdi *mdi;
unsigned int tmp_val;
unsigned int midi_type;
unsigned int track_size;
unsigned char **tracks;
unsigned int end_of_tracks = 0;
unsigned int no_tracks;
unsigned int i;
unsigned int divisions = 96;
unsigned int tempo = 500000;
float samples_per_delta_f = 0.0;
float microseconds_per_pulse = 0.0;
float pulses_per_second = 0.0;
unsigned long int sample_count = 0;
float sample_count_tmp = 0;
float sample_remainder = 0;
unsigned char *sysex_store = NULL;
unsigned long int sysex_store_len = 0;
unsigned long int *track_delta;
unsigned char *track_end;
unsigned long int smallest_delta = 0;
unsigned long int subtract_delta = 0;
unsigned long int tmp_length = 0;
unsigned char current_event = 0;
unsigned char current_event_ch = 0;
unsigned char *running_event;
unsigned long int decay_samples = 0;
if (memcmp(midi_data, "RIFF", 4) == 0) {
midi_data += 20;
midi_size -= 20;
}
if (memcmp(midi_data, "MThd", 4)) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_MIDI, NULL, 0);
return NULL;
}
midi_data += 4;
midi_size -= 4;
if (midi_size < 10) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0);
return NULL;
}
/*
* Get Midi Header Size - must always be 6
*/
tmp_val = *midi_data++ << 24;
tmp_val |= *midi_data++ << 16;
tmp_val |= *midi_data++ << 8;
tmp_val |= *midi_data++;
midi_size -= 4;
if (tmp_val != 6) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, NULL, 0);
return NULL;
}
/*
* Get Midi Format - we only support 0, 1 & 2
*/
tmp_val = *midi_data++ << 8;
tmp_val |= *midi_data++;
midi_size -= 2;
if (tmp_val > 2) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, NULL, 0);
return NULL;
}
midi_type = tmp_val;
/*
* Get No. of Tracks
*/
tmp_val = *midi_data++ << 8;
tmp_val |= *midi_data++;
midi_size -= 2;
if (tmp_val < 1) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(no tracks)", 0);
return NULL;
}
no_tracks = tmp_val;
/*
* Check that type 0 midi file has only 1 track
*/
if ((midi_type == 0) && (no_tracks > 1)) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(expected 1 track for type 0 midi file, found more)", 0);
return NULL;
}
/*
* Get Divisions
*/
divisions = *midi_data++ << 8;
divisions |= *midi_data++;
midi_size -= 2;
if (divisions & 0x00008000) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, NULL, 0);
return NULL;
}
if ((WM_MixerOptions & WM_MO_WHOLETEMPO)) {
float bpm_f = (float) (60000000 / tempo);
tempo = 60000000 / (unsigned long int) bpm_f;
} else if ((WM_MixerOptions & WM_MO_ROUNDTEMPO)) {
float bpm_fr = (float) (60000000 / tempo) + 0.5f;
tempo = 60000000 / (unsigned long int) bpm_fr;
}
/* Slow but needed for accuracy */
microseconds_per_pulse = (float) tempo / (float) divisions;
pulses_per_second = 1000000.0f / microseconds_per_pulse;
samples_per_delta_f = (float) _WM_SampleRate / pulses_per_second;
mdi = Init_MDI();
tracks = (unsigned char**)malloc(sizeof(unsigned char *) * no_tracks);
track_delta = (unsigned long*)malloc(sizeof(unsigned long int) * no_tracks);
track_end = (unsigned char*)malloc(sizeof(unsigned char) * no_tracks);
running_event = (unsigned char*)malloc(sizeof(unsigned char) * no_tracks);
for (i = 0; i < no_tracks; i++) {
if (midi_size < 8) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0);
goto _end;
}
if (memcmp(midi_data, "MTrk", 4) != 0) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(missing track header)", 0);
goto _end;
}
midi_data += 4;
midi_size -= 4;
track_size = *midi_data++ << 24;
track_size |= *midi_data++ << 16;
track_size |= *midi_data++ << 8;
track_size |= *midi_data++;
midi_size -= 4;
if (midi_size < track_size) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0);
goto _end;
}
if ((midi_data[track_size - 3] != 0xFF)
|| (midi_data[track_size - 2] != 0x2F)
|| (midi_data[track_size - 1] != 0x00)) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(missing EOT)", 0);
goto _end;
}
tracks[i] = midi_data;
midi_data += track_size;
midi_size -= track_size;
track_end[i] = 0;
running_event[i] = 0;
track_delta[i] = 0;
decay_samples = 0;
while (*tracks[i] > 0x7F) {
track_delta[i] = (track_delta[i] << 7) + (*tracks[i] & 0x7F);
tracks[i]++;
}
track_delta[i] = (track_delta[i] << 7) + (*tracks[i] & 0x7F);
tracks[i]++;
}
/*
* Handle type 0 & 1 the same, but type 2 differently
*/
switch (midi_type) {
case 0:
case 1:
/* Type 0 & 1 can use the same code */
while (end_of_tracks != no_tracks) {
smallest_delta = 0;
for (i = 0; i < no_tracks; i++) {
if (track_end[i])
continue;
if (track_delta[i]) {
track_delta[i] -= subtract_delta;
if (track_delta[i]) {
if ((!smallest_delta)
|| (smallest_delta > track_delta[i])) {
smallest_delta = track_delta[i];
}
continue;
}
}
do {
if (*tracks[i] > 0x7F) {
current_event = *tracks[i];
tracks[i]++;
} else {
current_event = running_event[i];
if (running_event[i] < 0x80) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(missing event)", 0);
goto _end;
}
}
current_event_ch = current_event & 0x0F;
switch (current_event >> 4) {
case 0x8:
NOTEOFF: midi_setup_noteoff(mdi, current_event_ch,
tracks[i][0], tracks[i][1]);
/* To better calculate samples needed after the end of midi,
* we calculate samples for decay for note off */
{
unsigned long int tmp_decay_samples = 0;
struct _patch *tmp_patch = NULL;
if (mdi->channel[current_event_ch].isdrum) {
tmp_patch = get_patch_data(
((mdi->channel[current_event_ch].bank << 8)
| tracks[i][0] | 0x80));
/* if (tmp_patch == NULL)
printf("Drum patch not loaded 0x%02x on channel %i\n",((mdi->channel[current_event_ch].bank << 8) | tracks[i][0] | 0x80),current_event_ch);*/
} else {
tmp_patch = mdi->channel[current_event_ch].patch;
/* if (tmp_patch == NULL)
printf("Channel %i patch not loaded\n", current_event_ch);*/
}
tmp_decay_samples = get_decay_samples(tmp_patch,
tracks[i][0]);
/* if the note off decay is more than the decay we currently tracking then
* we set it to new decay. */
if (tmp_decay_samples > decay_samples) {
decay_samples = tmp_decay_samples;
}
}
tracks[i] += 2;
running_event[i] = current_event;
break;
case 0x9:
if (tracks[i][1] == 0) {
goto NOTEOFF;
}
midi_setup_noteon(mdi, (current_event & 0x0F), tracks[i][0],
tracks[i][1]);
tracks[i] += 2;
running_event[i] = current_event;
break;
case 0xA:
midi_setup_aftertouch(mdi, (current_event & 0x0F),
tracks[i][0], tracks[i][1]);
tracks[i] += 2;
running_event[i] = current_event;
break;
case 0xB:
midi_setup_control(mdi, (current_event & 0x0F),
tracks[i][0], tracks[i][1]);
tracks[i] += 2;
running_event[i] = current_event;
break;
case 0xC:
midi_setup_patch(mdi, (current_event & 0x0F), *tracks[i]);
tracks[i]++;
running_event[i] = current_event;
break;
case 0xD:
midi_setup_channel_pressure(mdi, (current_event & 0x0F),
*tracks[i]);
tracks[i]++;
running_event[i] = current_event;
break;
case 0xE:
midi_setup_pitch(mdi, (current_event & 0x0F),
((tracks[i][1] << 7) | (tracks[i][0] & 0x7F)));
tracks[i] += 2;
running_event[i] = current_event;
break;
case 0xF: /* Meta Event */
if (current_event == 0xFF) {
if (tracks[i][0] == 0x02) { /* Copyright Event */
/* Get Length */
tmp_length = 0;
tracks[i]++;
while (*tracks[i] > 0x7f) {
tmp_length = (tmp_length << 7)
+ (*tracks[i] & 0x7f);
tracks[i]++;
}
tmp_length = (tmp_length << 7)
+ (*tracks[i] & 0x7f);
/* Copy copyright info in the getinfo struct */
if (mdi->info.copyright) {
mdi->info.copyright = (char*)realloc(
mdi->info.copyright,
(strlen(mdi->info.copyright) + 1
+ tmp_length + 1));
strncpy(
&mdi->info.copyright[strlen(
mdi->info.copyright) + 1],
(char *) tracks[i], tmp_length);
mdi->info.copyright[strlen(mdi->info.copyright)
+ 1 + tmp_length] = '\0';
mdi->info.copyright[strlen(mdi->info.copyright)] = '\n';
} else {
mdi->info.copyright = (char*)malloc(tmp_length + 1);
strncpy(mdi->info.copyright, (char *) tracks[i],
tmp_length);
mdi->info.copyright[tmp_length] = '\0';
}
tracks[i] += tmp_length + 1;
} else if ((tracks[i][0] == 0x2F)
&& (tracks[i][1] == 0x00)) {
/* End of Track */
end_of_tracks++;
track_end[i] = 1;
goto NEXT_TRACK;
} else if ((tracks[i][0] == 0x51)
&& (tracks[i][1] == 0x03)) {
/* Tempo */
tempo = (tracks[i][2] << 16) + (tracks[i][3] << 8)
+ tracks[i][4];
tracks[i] += 5;
if (!tempo)
tempo = 500000;
if ((WM_MixerOptions & WM_MO_WHOLETEMPO)) {
float bpm_f = (float) (60000000 / tempo);
tempo = 60000000
/ (unsigned long int) bpm_f;
} else if ((WM_MixerOptions & WM_MO_ROUNDTEMPO)) {
float bpm_fr = (float) (60000000 / tempo)
+ 0.5f;
tempo = 60000000
/ (unsigned long int) bpm_fr;
}
/* Slow but needed for accuracy */
microseconds_per_pulse = (float) tempo
/ (float) divisions;
pulses_per_second = 1000000.0f
/ microseconds_per_pulse;
samples_per_delta_f = (float) _WM_SampleRate
/ pulses_per_second;
} else {
tmp_length = 0;
tracks[i]++;
while (*tracks[i] > 0x7f) {
tmp_length = (tmp_length << 7)
+ (*tracks[i] & 0x7f);
tracks[i]++;
}
tmp_length = (tmp_length << 7)
+ (*tracks[i] & 0x7f);
tracks[i] += tmp_length + 1;
}
} else if ((current_event == 0xF0)
|| (current_event == 0xF7)) {
/* Roland Sysex Events */
unsigned long int sysex_len = 0;
while (*tracks[i] > 0x7F) {
sysex_len = (sysex_len << 7) + (*tracks[i] & 0x7F);
tracks[i]++;
}
sysex_len = (sysex_len << 7) + (*tracks[i] & 0x7F);
tracks[i]++;
running_event[i] = 0;
sysex_store = (unsigned char*)realloc(sysex_store,
sizeof(unsigned char)
* (sysex_store_len + sysex_len));
memcpy(&sysex_store[sysex_store_len], tracks[i],
sysex_len);
sysex_store_len += sysex_len;
if (sysex_store[sysex_store_len - 1] == 0xF7) {
unsigned char tmpsysexdata[] = { 0x41, 0x10, 0x42, 0x12 };
if (memcmp(tmpsysexdata, sysex_store, 4) == 0) {
/* checksum */
unsigned char sysex_cs = 0;
unsigned int sysex_ofs = 4;
do {
sysex_cs += sysex_store[sysex_ofs];
if (sysex_cs > 0x7F) {
sysex_cs -= 0x80;
}
sysex_ofs++;
} while (sysex_store[sysex_ofs + 1] != 0xF7);
sysex_cs = 128 - sysex_cs;
/* is roland sysex message valid */
if (sysex_cs == sysex_store[sysex_ofs]) {
/* process roland sysex event */
if (sysex_store[4] == 0x40) {
if (((sysex_store[5] & 0xF0) == 0x10)
&& (sysex_store[6] == 0x15)) {
/* Roland Drum Track Setting */
unsigned char sysex_ch = 0x0F
& sysex_store[5];
if (sysex_ch == 0x00) {
sysex_ch = 0x09;
} else if (sysex_ch <= 0x09) {
sysex_ch -= 1;
}
midi_setup_sysex_roland_drum_track(
mdi, sysex_ch,
sysex_store[7]);
} else if ((sysex_store[5] == 0x00)
&& (sysex_store[6] == 0x7F)
&& (sysex_store[7] == 0x00)) {
/* Roland GS Reset */
midi_setup_sysex_roland_reset(mdi);
}
}
}
}
free(sysex_store);
sysex_store = NULL;
sysex_store_len = 0;
}
tracks[i] += sysex_len;
} else {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(unrecognized meta event)", 0);
goto _end;
}
break;
default:
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(unrecognized event)", 0);
goto _end;
}
while (*tracks[i] > 0x7F) {
track_delta[i] = (track_delta[i] << 7)
+ (*tracks[i] & 0x7F);
tracks[i]++;
}
track_delta[i] = (track_delta[i] << 7) + (*tracks[i] & 0x7F);
tracks[i]++;
} while (!track_delta[i]);
if ((!smallest_delta) || (smallest_delta > track_delta[i])) {
smallest_delta = track_delta[i];
}
NEXT_TRACK: continue;
}
subtract_delta = smallest_delta;
sample_count_tmp = (((float) smallest_delta * samples_per_delta_f)
+ sample_remainder);
sample_count = (unsigned long int) sample_count_tmp;
sample_remainder = sample_count_tmp - (float) sample_count;
if ((mdi->event_count)
&& (mdi->events[mdi->event_count - 1].do_event == NULL)) {
mdi->events[mdi->event_count - 1].samples_to_next += sample_count;
} else {
WM_CheckEventMemoryPool(mdi);
mdi->events[mdi->event_count].do_event = NULL;
mdi->events[mdi->event_count].event_data.channel = 0;
mdi->events[mdi->event_count].event_data.data = 0;
mdi->events[mdi->event_count].samples_to_next = sample_count;
mdi->event_count++;
}
mdi->info.approx_total_samples += sample_count;
/* printf("Decay Samples = %lu\n",decay_samples);*/
if (decay_samples > sample_count) {
decay_samples -= sample_count;
} else {
decay_samples = 0;
}
}
break;
case 2: /* Type 2 has to be handled differently */
for (i = 0; i < no_tracks; i++) {
sample_remainder = 0.0;
decay_samples = 0;
track_delta[i] = 0;
do {
if(track_delta[i]) {
sample_count_tmp = (((float) track_delta[i] * samples_per_delta_f)
+ sample_remainder);
sample_count = (unsigned long int) sample_count_tmp;
sample_remainder = sample_count_tmp - (float) sample_count;
if ((mdi->event_count)
&& (mdi->events[mdi->event_count - 1].do_event == NULL)) {
mdi->events[mdi->event_count - 1].samples_to_next += sample_count;
} else {
WM_CheckEventMemoryPool(mdi);
mdi->events[mdi->event_count].do_event = NULL;
mdi->events[mdi->event_count].event_data.channel = 0;
mdi->events[mdi->event_count].event_data.data = 0;
mdi->events[mdi->event_count].samples_to_next = sample_count;
mdi->event_count++;
}
mdi->info.approx_total_samples += sample_count;
/* printf("Decay Samples = %lu\n",decay_samples);*/
if (decay_samples > sample_count) {
decay_samples -= sample_count;
} else {
decay_samples = 0;
}
}
if (*tracks[i] > 0x7F) {
current_event = *tracks[i];
tracks[i]++;
} else {
current_event = running_event[i];
if (running_event[i] < 0x80) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(missing event)", 0);
goto _end;
}
}
current_event_ch = current_event & 0x0F;
switch (current_event >> 4) {
case 0x8:
NOTEOFF2: midi_setup_noteoff(mdi, current_event_ch,
tracks[i][0], tracks[i][1]);
/* To better calculate samples needed after the end of midi,
* we calculate samples for decay for note off */
{
unsigned long int tmp_decay_samples = 0;
struct _patch *tmp_patch = NULL;
if (mdi->channel[current_event_ch].isdrum) {
tmp_patch = get_patch_data(
((mdi->channel[current_event_ch].bank << 8)
| tracks[i][0] | 0x80));
/* if (tmp_patch == NULL)
printf("Drum patch not loaded 0x%02x on channel %i\n",((mdi->channel[current_event_ch].bank << 8) | tracks[i][0] | 0x80),current_event_ch);*/
} else {
tmp_patch = mdi->channel[current_event_ch].patch;
/* if (tmp_patch == NULL)
printf("Channel %i patch not loaded\n", current_event_ch);*/
}
tmp_decay_samples = get_decay_samples(tmp_patch,
tracks[i][0]);
/* if the note off decay is more than the decay we currently tracking then
* we set it to new decay. */
if (tmp_decay_samples > decay_samples) {
decay_samples = tmp_decay_samples;
}
}
tracks[i] += 2;
running_event[i] = current_event;
break;
case 0x9:
if (tracks[i][1] == 0) {
goto NOTEOFF2;
}
midi_setup_noteon(mdi, (current_event & 0x0F), tracks[i][0],
tracks[i][1]);
tracks[i] += 2;
running_event[i] = current_event;
break;
case 0xA:
midi_setup_aftertouch(mdi, (current_event & 0x0F),
tracks[i][0], tracks[i][1]);
tracks[i] += 2;
running_event[i] = current_event;
break;
case 0xB:
midi_setup_control(mdi, (current_event & 0x0F),
tracks[i][0], tracks[i][1]);
tracks[i] += 2;
running_event[i] = current_event;
break;
case 0xC:
midi_setup_patch(mdi, (current_event & 0x0F), *tracks[i]);
tracks[i]++;
running_event[i] = current_event;
break;
case 0xD:
midi_setup_channel_pressure(mdi, (current_event & 0x0F),
*tracks[i]);
tracks[i]++;
running_event[i] = current_event;
break;
case 0xE:
midi_setup_pitch(mdi, (current_event & 0x0F),
((tracks[i][1] << 7) | (tracks[i][0] & 0x7F)));
tracks[i] += 2;
running_event[i] = current_event;
break;
case 0xF: /* Meta Event */
if (current_event == 0xFF) {
if (tracks[i][0] == 0x02) { /* Copyright Event */
/* Get Length */
tmp_length = 0;
tracks[i]++;
while (*tracks[i] > 0x7f) {
tmp_length = (tmp_length << 7)
+ (*tracks[i] & 0x7f);
tracks[i]++;
}
tmp_length = (tmp_length << 7)
+ (*tracks[i] & 0x7f);
/* Copy copyright info in the getinfo struct */
if (mdi->info.copyright) {
mdi->info.copyright = (char*)realloc(
mdi->info.copyright,
(strlen(mdi->info.copyright) + 1
+ tmp_length + 1));
strncpy(
&mdi->info.copyright[strlen(
mdi->info.copyright) + 1],
(char *) tracks[i], tmp_length);
mdi->info.copyright[strlen(mdi->info.copyright)
+ 1 + tmp_length] = '\0';
mdi->info.copyright[strlen(mdi->info.copyright)] = '\n';
} else {
mdi->info.copyright = (char*)malloc(tmp_length + 1);
strncpy(mdi->info.copyright, (char *) tracks[i],
tmp_length);
mdi->info.copyright[tmp_length] = '\0';
}
tracks[i] += tmp_length + 1;
} else if ((tracks[i][0] == 0x2F)
&& (tracks[i][1] == 0x00)) {
/* End of Track */
end_of_tracks++;
track_end[i] = 1;
goto NEXT_TRACK2;
} else if ((tracks[i][0] == 0x51)
&& (tracks[i][1] == 0x03)) {
/* Tempo */
tempo = (tracks[i][2] << 16) + (tracks[i][3] << 8)
+ tracks[i][4];
tracks[i] += 5;
if (!tempo)
tempo = 500000;
if ((WM_MixerOptions & WM_MO_WHOLETEMPO)) {
float bpm_f = (float) (60000000 / tempo);
tempo = 60000000
/ (unsigned long int) bpm_f;
} else if ((WM_MixerOptions & WM_MO_ROUNDTEMPO)) {
float bpm_fr = (float) (60000000 / tempo)
+ 0.5f;
tempo = 60000000
/ (unsigned long int) bpm_fr;
}
/* Slow but needed for accuracy */
microseconds_per_pulse = (float) tempo
/ (float) divisions;
pulses_per_second = 1000000.0f
/ microseconds_per_pulse;
samples_per_delta_f = (float) _WM_SampleRate
/ pulses_per_second;
} else {
tmp_length = 0;
tracks[i]++;
while (*tracks[i] > 0x7f) {
tmp_length = (tmp_length << 7)
+ (*tracks[i] & 0x7f);
tracks[i]++;
}
tmp_length = (tmp_length << 7)
+ (*tracks[i] & 0x7f);
tracks[i] += tmp_length + 1;
}
} else if ((current_event == 0xF0)
|| (current_event == 0xF7)) {
/* Roland Sysex Events */
unsigned long int sysex_len = 0;
while (*tracks[i] > 0x7F) {
sysex_len = (sysex_len << 7) + (*tracks[i] & 0x7F);
tracks[i]++;
}
sysex_len = (sysex_len << 7) + (*tracks[i] & 0x7F);
tracks[i]++;
running_event[i] = 0;
sysex_store = (unsigned char*)realloc(sysex_store,
sizeof(unsigned char)
* (sysex_store_len + sysex_len));
memcpy(&sysex_store[sysex_store_len], tracks[i],
sysex_len);
sysex_store_len += sysex_len;
if (sysex_store[sysex_store_len - 1] == 0xF7) {
unsigned char tmpsysexdata[] = { 0x41, 0x10, 0x42, 0x12 };
if (memcmp(tmpsysexdata, sysex_store, 4) == 0) {
/* checksum */
unsigned char sysex_cs = 0;
unsigned int sysex_ofs = 4;
do {
sysex_cs += sysex_store[sysex_ofs];
if (sysex_cs > 0x7F) {
sysex_cs -= 0x80;
}
sysex_ofs++;
} while (sysex_store[sysex_ofs + 1] != 0xF7);
sysex_cs = 128 - sysex_cs;
/* is roland sysex message valid */
if (sysex_cs == sysex_store[sysex_ofs]) {
/* process roland sysex event */
if (sysex_store[4] == 0x40) {
if (((sysex_store[5] & 0xF0) == 0x10)
&& (sysex_store[6] == 0x15)) {
/* Roland Drum Track Setting */
unsigned char sysex_ch = 0x0F
& sysex_store[5];
if (sysex_ch == 0x00) {
sysex_ch = 0x09;
} else if (sysex_ch <= 0x09) {
sysex_ch -= 1;
}
midi_setup_sysex_roland_drum_track(
mdi, sysex_ch,
sysex_store[7]);
} else if ((sysex_store[5] == 0x00)
&& (sysex_store[6] == 0x7F)
&& (sysex_store[7] == 0x00)) {
/* Roland GS Reset */
midi_setup_sysex_roland_reset(mdi);
}
}
}
}
free(sysex_store);
sysex_store = NULL;
sysex_store_len = 0;
}
tracks[i] += sysex_len;
} else {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(unrecognized meta event)", 0);
goto _end;
}
break;
default:
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(unrecognized event)", 0);
goto _end;
}
track_delta[i] = 0;
while (*tracks[i] > 0x7F) {
track_delta[i] = (track_delta[i] << 7)
+ (*tracks[i] & 0x7F);
tracks[i]++;
}
track_delta[i] = (track_delta[i] << 7) + (*tracks[i] & 0x7F);
tracks[i]++;
NEXT_TRACK2:
smallest_delta = track_delta[i]; /* Added just to keep Xcode happy */
UNUSED(smallest_delta); /* Added to just keep clang happy */
} while (track_end[i] == 0);
/*
* Add decay at the end of each song
*/
if (decay_samples) {
if ((mdi->event_count)
&& (mdi->events[mdi->event_count - 1].do_event == NULL)) {
mdi->events[mdi->event_count - 1].samples_to_next += decay_samples;
} else {
WM_CheckEventMemoryPool(mdi);
mdi->events[mdi->event_count].do_event = NULL;
mdi->events[mdi->event_count].event_data.channel = 0;
mdi->events[mdi->event_count].event_data.data = 0;
mdi->events[mdi->event_count].samples_to_next = decay_samples;
mdi->event_count++;
}
}
}
break;
default: break; /* Don't expect to get here, added for completeness */
}
if ((mdi->event_count)
&& (mdi->events[mdi->event_count - 1].do_event == NULL)) {
mdi->info.approx_total_samples -=
mdi->events[mdi->event_count - 1].samples_to_next;
mdi->event_count--;
}
/* Set total MIDI time to 1/1000's seconds */
mdi->info.total_midi_time = (mdi->info.approx_total_samples * 1000)
/ _WM_SampleRate;
/*mdi->info.approx_total_samples += _WM_SampleRate * 3;*/
/* Add additional samples needed for decay */
mdi->info.approx_total_samples += decay_samples;
/*printf("decay_samples = %lu\n",decay_samples);*/
if ((mdi->reverb = _WM_init_reverb(_WM_SampleRate, reverb_room_width,
reverb_room_length, reverb_listen_posx, reverb_listen_posy))
== NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to init reverb", 0);
goto _end;
}
mdi->info.current_sample = 0;
mdi->current_event = &mdi->events[0];
mdi->samples_to_mix = 0;
mdi->note = NULL;
WM_ResetToStart(mdi);
_end: free(sysex_store);
free(track_end);
free(track_delta);
free(running_event);
free(tracks);
if (mdi->reverb) return mdi;
freeMDI(mdi);
return NULL;
}
static int *WM_Mix_Linear(midi * handle, int * buffer, unsigned long int count)
{
struct _mdi *mdi = (struct _mdi *)handle;
unsigned long int data_pos;
signed int premix, left_mix, right_mix;
signed int vol_mul;
struct _note *note_data = NULL;
do {
note_data = mdi->note;
left_mix = right_mix = 0;
if (__builtin_expect((note_data != NULL), 1)) {
while (note_data) {
/*
* ===================
* resample the sample
* ===================
*/
data_pos = note_data->sample_pos >> FPBITS;
vol_mul = ((note_data->vol_lvl
* (note_data->env_level >> 12)) >> FPBITS);
premix = (note_data->sample->data[data_pos]
+ ((note_data->sample->data[data_pos + 1]
- note_data->sample->data[data_pos])
* (signed long int) (note_data->sample_pos
& FPMASK)>> FPBITS)) * vol_mul
/ 1024;
left_mix += premix
* mdi->channel[note_data->noteid >> 8].left_adjust;
right_mix += premix
* mdi->channel[note_data->noteid >> 8].right_adjust;
/*
* ========================
* sample position checking
* ========================
*/
note_data->sample_pos += note_data->sample_inc;
if (__builtin_expect(
(note_data->sample_pos > note_data->sample->loop_end),
0)) {
if (note_data->modes & SAMPLE_LOOP) {
note_data->sample_pos =
note_data->sample->loop_start
+ ((note_data->sample_pos
- note_data->sample->loop_start)
% note_data->sample->loop_size);
} else if (__builtin_expect(
(note_data->sample_pos
>= note_data->sample->data_length),
0)) {
if (__builtin_expect((note_data->replay == NULL), 1)) {
goto KILL_NOTE;
}
goto RESTART_NOTE;
}
}
if (__builtin_expect((note_data->env_inc == 0), 0)) {
note_data = note_data->next;
continue;
}
note_data->env_level += note_data->env_inc;
if (__builtin_expect((note_data->env_level > 4194304), 0)) {
note_data->env_level =
note_data->sample->env_target[note_data->env];
}
if (__builtin_expect(
((note_data->env_inc < 0)
&& (note_data->env_level
> note_data->sample->env_target[note_data->env]))
|| ((note_data->env_inc > 0)
&& (note_data->env_level
< note_data->sample->env_target[note_data->env])),
1)) {
note_data = note_data->next;
continue;
}
note_data->env_level =
note_data->sample->env_target[note_data->env];
switch (note_data->env) {
case 0:
#if 0
if (!(note_data->modes & SAMPLE_ENVELOPE)) {
note_data->env_inc = 0;
note_data = note_data->next;
continue;
}
#endif
break;
case 2:
if (note_data->modes & SAMPLE_SUSTAIN) {
note_data->env_inc = 0;
note_data = note_data->next;
continue;
} else if (note_data->modes & SAMPLE_CLAMPED) {
note_data->env = 5;
if (note_data->env_level
> note_data->sample->env_target[5]) {
note_data->env_inc =
-note_data->sample->env_rate[5];
} else {
note_data->env_inc =
note_data->sample->env_rate[5];
}
continue;
}
break;
case 5:
if (__builtin_expect((note_data->env_level == 0), 1)) {
goto KILL_NOTE;
}
/* sample release */
if (note_data->modes & SAMPLE_LOOP)
note_data->modes ^= SAMPLE_LOOP;
note_data->env_inc = 0;
note_data = note_data->next;
continue;
case 6:
if (__builtin_expect((note_data->replay != NULL), 1)) {
RESTART_NOTE: note_data->active = 0;
{
struct _note *prev_note = NULL;
struct _note *nte_array = mdi->note;
if (nte_array != note_data) {
do {
prev_note = nte_array;
nte_array = nte_array->next;
} while (nte_array != note_data);
}
if (prev_note) {
prev_note->next = note_data->replay;
} else {
mdi->note = note_data->replay;
}
note_data->replay->next = note_data->next;
note_data = note_data->replay;
note_data->active = 1;
}
} else {
KILL_NOTE: note_data->active = 0;
{
struct _note *prev_note = NULL;
struct _note *nte_array = mdi->note;
if (nte_array != note_data) {
do {
prev_note = nte_array;
nte_array = nte_array->next;
} while ((nte_array != note_data)
&& (nte_array));
}
if (prev_note) {
prev_note->next = note_data->next;
} else {
mdi->note = note_data->next;
}
note_data = note_data->next;
}
}
continue;
}
note_data->env++;
if (note_data->is_off == 1) {
do_note_off_extra(note_data);
}
if (note_data->env_level
> note_data->sample->env_target[note_data->env]) {
note_data->env_inc =
-note_data->sample->env_rate[note_data->env];
} else {
note_data->env_inc =
note_data->sample->env_rate[note_data->env];
}
note_data = note_data->next;
continue;
}
/*
* =========================
* mix the channels together
* =========================
*/
left_mix /= 1024;
right_mix /= 1024;
}
*buffer++ = left_mix;
*buffer++ = right_mix;
} while (--count);
return buffer;
}
static int WM_GetOutput_Linear(midi * handle, char * buffer,
unsigned long int size) {
unsigned long int buffer_used = 0;
unsigned long int i;
struct _mdi *mdi = (struct _mdi *) handle;
unsigned long int real_samples_to_mix = 0;
struct _event *event = mdi->current_event;
int *tmp_buffer;
int *out_buffer;
int left_mix, right_mix;
_WM_Lock(&mdi->lock);
buffer_used = 0;
memset(buffer, 0, size);
if ( (size / 2) > mdi->mix_buffer_size) {
if ( (size / 2) <= ( mdi->mix_buffer_size * 2 )) {
mdi->mix_buffer_size += MEM_CHUNK;
} else {
mdi->mix_buffer_size = size / 2;
}
mdi->mix_buffer = (int*)realloc(mdi->mix_buffer, mdi->mix_buffer_size * sizeof(signed int));
}
tmp_buffer = mdi->mix_buffer;
memset(tmp_buffer, 0, ((size / 2) * sizeof(signed long int)));
out_buffer = tmp_buffer;
do {
if (__builtin_expect((!mdi->samples_to_mix), 0)) {
while ((!mdi->samples_to_mix) && (event->do_event)) {
event->do_event(mdi, &event->event_data);
event++;
mdi->samples_to_mix = event->samples_to_next;
mdi->current_event = event;
}
if (!mdi->samples_to_mix) {
if (mdi->info.current_sample
>= mdi->info.approx_total_samples) {
break;
} else if ((mdi->info.approx_total_samples
- mdi->info.current_sample) > (size >> 2)) {
mdi->samples_to_mix = size >> 2;
} else {
mdi->samples_to_mix = mdi->info.approx_total_samples
- mdi->info.current_sample;
}
}
}
if (__builtin_expect((mdi->samples_to_mix > (size >> 2)), 1)) {
real_samples_to_mix = size >> 2;
} else {
real_samples_to_mix = mdi->samples_to_mix;
if (real_samples_to_mix == 0) {
continue;
}
}
/* do mixing here */
tmp_buffer = WM_Mix_Linear(handle, tmp_buffer, real_samples_to_mix);
buffer_used += real_samples_to_mix * 4;
size -= (real_samples_to_mix << 2);
mdi->info.current_sample += real_samples_to_mix;
mdi->samples_to_mix -= real_samples_to_mix;
} while (size);
tmp_buffer = out_buffer;
if (mdi->info.mixer_options & WM_MO_REVERB) {
_WM_do_reverb(mdi->reverb, tmp_buffer, (buffer_used / 2));
}
for (i = 0; i < buffer_used; i += 4) {
left_mix = *tmp_buffer++;
right_mix = *tmp_buffer++;
if (left_mix > 32767) {
left_mix = 32767;
} else if (left_mix < -32768) {
left_mix = -32768;
}
if (right_mix > 32767) {
right_mix = 32767;
} else if (right_mix < -32768) {
right_mix = -32768;
}
/*
* ===================
* Write to the buffer
* ===================
*/
(*buffer++) = left_mix & 0xff;
(*buffer++) = ((left_mix >> 8) & 0x7f) | ((left_mix >> 24) & 0x80);
(*buffer++) = right_mix & 0xff;
(*buffer++) = ((right_mix >> 8) & 0x7f) | ((right_mix >> 24) & 0x80);
}
_WM_Unlock(&mdi->lock);
return buffer_used;
}
static int *WM_Mix_Gauss(midi * handle, int * buffer, unsigned long int count)
{
struct _mdi *mdi = (struct _mdi *)handle;
unsigned long int data_pos;
signed int premix, left_mix, right_mix;
signed int vol_mul;
struct _note *note_data = NULL;
signed short int *sptr;
double y, xd;
double *gptr, *gend;
int left, right, temp_n;
int ii, jj;
do {
note_data = mdi->note;
left_mix = right_mix = 0;
if (__builtin_expect((note_data != NULL), 1)) {
while (note_data) {
/*
* ===================
* resample the sample
* ===================
*/
data_pos = note_data->sample_pos >> FPBITS;
vol_mul = ((note_data->vol_lvl
* (note_data->env_level >> 12)) >> FPBITS);
/* check to see if we're near one of the ends */
left = data_pos;
right = (note_data->sample->data_length >> FPBITS) - left
- 1;
temp_n = (right << 1) - 1;
if (temp_n <= 0)
temp_n = 1;
if (temp_n > (left << 1) + 1)
temp_n = (left << 1) + 1;
/* use Newton if we can't fill the window */
if (temp_n < gauss_n) {
xd = note_data->sample_pos & FPMASK;
xd /= (1L << FPBITS);
xd += temp_n >> 1;
y = 0;
sptr = note_data->sample->data
+ (note_data->sample_pos >> FPBITS)
- (temp_n >> 1);
for (ii = temp_n; ii;) {
for (jj = 0; jj <= ii; jj++)
y += sptr[jj] * newt_coeffs[ii][jj];
y *= xd - --ii;
}
y += *sptr;
} else { /* otherwise, use Gauss as usual */
y = 0;
gptr = &gauss_table[(note_data->sample_pos & FPMASK) *
(gauss_n + 1)];
gend = gptr + gauss_n;
sptr = note_data->sample->data
+ (note_data->sample_pos >> FPBITS)
- (gauss_n >> 1);
do {
y += *(sptr++) * *(gptr++);
} while (gptr <= gend);
}
premix = (long) (y * vol_mul / 1024);
left_mix += premix
* mdi->channel[note_data->noteid >> 8].left_adjust;
right_mix += premix
* mdi->channel[note_data->noteid >> 8].right_adjust;
/*
* ========================
* sample position checking
* ========================
*/
note_data->sample_pos += note_data->sample_inc;
if (__builtin_expect(
(note_data->sample_pos > note_data->sample->loop_end),
0)) {
if (note_data->modes & SAMPLE_LOOP) {
note_data->sample_pos =
note_data->sample->loop_start
+ ((note_data->sample_pos
- note_data->sample->loop_start)
% note_data->sample->loop_size);
} else if (__builtin_expect(
(note_data->sample_pos
>= note_data->sample->data_length),
0)) {
if (__builtin_expect((note_data->replay == NULL), 1)) {
goto KILL_NOTE;
}
goto RESTART_NOTE;
}
}
if (__builtin_expect((note_data->env_inc == 0), 0)) {
note_data = note_data->next;
continue;
}
note_data->env_level += note_data->env_inc;
if (__builtin_expect((note_data->env_level > 4194304), 0)) {
note_data->env_level =
note_data->sample->env_target[note_data->env];
}
if (__builtin_expect(
((note_data->env_inc < 0)
&& (note_data->env_level
> note_data->sample->env_target[note_data->env]))
|| ((note_data->env_inc > 0)
&& (note_data->env_level
< note_data->sample->env_target[note_data->env])),
1)) {
note_data = note_data->next;
continue;
}
note_data->env_level =
note_data->sample->env_target[note_data->env];
switch (note_data->env) {
case 0:
#if 0
if (!(note_data->modes & SAMPLE_ENVELOPE)) {
note_data->env_inc = 0;
note_data = note_data->next;
continue;
}
#endif
break;
case 2:
if (note_data->modes & SAMPLE_SUSTAIN) {
note_data->env_inc = 0;
note_data = note_data->next;
continue;
} else if (note_data->modes & SAMPLE_CLAMPED) {
note_data->env = 5;
if (note_data->env_level
> note_data->sample->env_target[5]) {
note_data->env_inc =
-note_data->sample->env_rate[5];
} else {
note_data->env_inc =
note_data->sample->env_rate[5];
}
continue;
}
break;
case 5:
if (__builtin_expect((note_data->env_level == 0), 1)) {
goto KILL_NOTE;
}
/* sample release */
if (note_data->modes & SAMPLE_LOOP)
note_data->modes ^= SAMPLE_LOOP;
note_data->env_inc = 0;
note_data = note_data->next;
continue;
case 6:
if (__builtin_expect((note_data->replay != NULL), 1)) {
RESTART_NOTE: note_data->active = 0;
{
struct _note *prev_note = NULL;
struct _note *nte_array = mdi->note;
if (nte_array != note_data) {
do {
prev_note = nte_array;
nte_array = nte_array->next;
} while (nte_array != note_data);
}
if (prev_note) {
prev_note->next = note_data->replay;
} else {
mdi->note = note_data->replay;
}
note_data->replay->next = note_data->next;
note_data = note_data->replay;
note_data->active = 1;
}
} else {
KILL_NOTE: note_data->active = 0;
{
struct _note *prev_note = NULL;
struct _note *nte_array = mdi->note;
if (nte_array != note_data) {
do {
prev_note = nte_array;
nte_array = nte_array->next;
} while ((nte_array != note_data)
&& (nte_array));
}
if (prev_note) {
prev_note->next = note_data->next;
} else {
mdi->note = note_data->next;
}
note_data = note_data->next;
}
}
continue;
}
note_data->env++;
if (note_data->is_off == 1) {
do_note_off_extra(note_data);
}
if (note_data->env_level
> note_data->sample->env_target[note_data->env]) {
note_data->env_inc =
-note_data->sample->env_rate[note_data->env];
} else {
note_data->env_inc =
note_data->sample->env_rate[note_data->env];
}
note_data = note_data->next;
continue;
}
/*
* =========================
* mix the channels together
* =========================
*/
left_mix /= 1024;
right_mix /= 1024;
}
*buffer++ = left_mix;
*buffer++ = right_mix;
} while (--count);
return buffer;
}
static int WM_GetOutput_Gauss(midi * handle, char * buffer,
unsigned long int size) {
unsigned long int buffer_used = 0;
unsigned long int i;
struct _mdi *mdi = (struct _mdi *) handle;
unsigned long int real_samples_to_mix = 0;
struct _event *event = mdi->current_event;
signed int *tmp_buffer;
signed int *out_buffer;
signed int left_mix, right_mix;
_WM_Lock(&mdi->lock);
buffer_used = 0;
memset(buffer, 0, size);
if ( (size / 2) > mdi->mix_buffer_size) {
if ( (size / 2) <= ( mdi->mix_buffer_size * 2 )) {
mdi->mix_buffer_size += MEM_CHUNK;
} else {
mdi->mix_buffer_size = size / 2;
}
mdi->mix_buffer = (int*)realloc(mdi->mix_buffer, mdi->mix_buffer_size * sizeof(signed int));
}
tmp_buffer = mdi->mix_buffer;
memset(tmp_buffer, 0, ((size / 2) * sizeof(signed long int)));
out_buffer = tmp_buffer;
do {
if (__builtin_expect((!mdi->samples_to_mix), 0)) {
while ((!mdi->samples_to_mix) && (event->do_event)) {
event->do_event(mdi, &event->event_data);
event++;
mdi->samples_to_mix = event->samples_to_next;
mdi->current_event = event;
}
if (!mdi->samples_to_mix) {
if (mdi->info.current_sample
>= mdi->info.approx_total_samples) {
break;
} else if ((mdi->info.approx_total_samples
- mdi->info.current_sample) > (size >> 2)) {
mdi->samples_to_mix = size >> 2;
} else {
mdi->samples_to_mix = mdi->info.approx_total_samples
- mdi->info.current_sample;
}
}
}
if (__builtin_expect((mdi->samples_to_mix > (size >> 2)), 1)) {
real_samples_to_mix = size >> 2;
} else {
real_samples_to_mix = mdi->samples_to_mix;
if (real_samples_to_mix == 0) {
continue;
}
}
/* do mixing here */
tmp_buffer = WM_Mix_Gauss(handle, tmp_buffer, real_samples_to_mix);
buffer_used += real_samples_to_mix * 4;
size -= (real_samples_to_mix << 2);
mdi->info.current_sample += real_samples_to_mix;
mdi->samples_to_mix -= real_samples_to_mix;
} while (size);
tmp_buffer = out_buffer;
if (mdi->info.mixer_options & WM_MO_REVERB) {
_WM_do_reverb(mdi->reverb, tmp_buffer, (buffer_used / 2));
}
for (i = 0; i < buffer_used; i += 4) {
left_mix = *tmp_buffer++;
right_mix = *tmp_buffer++;
if (left_mix > 32767) {
left_mix = 32767;
} else if (left_mix < -32768) {
left_mix = -32768;
}
if (right_mix > 32767) {
right_mix = 32767;
} else if (right_mix < -32768) {
right_mix = -32768;
}
/*
* ===================
* Write to the buffer
* ===================
*/
(*buffer++) = left_mix & 0xff;
(*buffer++) = ((left_mix >> 8) & 0x7f) | ((left_mix >> 24) & 0x80);
(*buffer++) = right_mix & 0xff;
(*buffer++) = ((right_mix >> 8) & 0x7f) | ((right_mix >> 24) & 0x80);
}
_WM_Unlock(&mdi->lock);
return buffer_used;
}
/*
* =========================
* External Functions
* =========================
*/
WM_SYMBOL const char *
WildMidi_GetString(unsigned short int info) {
switch (info) {
case WM_GS_VERSION:
return WM_Version;
}
return NULL;
}
WM_SYMBOL int WildMidi_Init(const char * config_file, unsigned short int rate,
unsigned short int options) {
if (WM_Initialized) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_ALR_INIT, NULL, 0);
return -1;
}
if (config_file == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(NULL config file pointer)", 0);
return -1;
}
WM_InitPatches();
if (WM_LoadConfig(config_file) == -1) {
return -1;
}
if (options & 0x5FF8) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid option)",
0);
WM_FreePatches();
return -1;
}
WM_MixerOptions = options;
if (rate < 11025) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(rate out of bounds, range is 11025 - 65535)", 0);
WM_FreePatches();
return -1;
}
_WM_SampleRate = rate;
gauss_lock = 0;
patch_lock = 0;
WM_Initialized = 1;
return 0;
}
WM_SYMBOL int WildMidi_MasterVolume(unsigned char master_volume) {
struct _mdi *mdi = NULL;
struct _hndl * tmp_handle = first_handle;
int i = 0;
if (!WM_Initialized) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
return -1;
}
if (master_volume > 127) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(master volume out of range, range is 0-127)", 0);
return -1;
}
WM_MasterVolume = lin_volume[master_volume];
if (tmp_handle) {
while (tmp_handle) {
mdi = (struct _mdi *) tmp_handle->handle;
for (i = 0; i < 16; i++) {
do_pan_adjust(mdi, i);
}
tmp_handle = tmp_handle->next;
}
}
return 0;
}
WM_SYMBOL int WildMidi_Close(midi * handle) {
struct _mdi *mdi = (struct _mdi *) handle;
struct _hndl * tmp_handle;
if (!WM_Initialized) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
return -1;
}
if (handle == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)",
0);
return -1;
}
if (first_handle == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(no midi's open)",
0);
return -1;
}
_WM_Lock(&mdi->lock);
if (first_handle->handle == handle) {
tmp_handle = first_handle->next;
free(first_handle);
first_handle = tmp_handle;
if (first_handle)
first_handle->prev = NULL;
} else {
tmp_handle = first_handle;
while (tmp_handle->handle != handle) {
tmp_handle = tmp_handle->next;
if (tmp_handle == NULL) {
break;
}
}
if (tmp_handle) {
tmp_handle->prev->next = tmp_handle->next;
if (tmp_handle->next) {
tmp_handle->next->prev = tmp_handle->prev;
}
free(tmp_handle);
}
}
freeMDI(mdi);
return 0;
}
WM_SYMBOL midi *
WildMidi_Open(const char *midifile) {
unsigned char *mididata = NULL;
unsigned long int midisize = 0;
midi * ret = NULL;
if (!WM_Initialized) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
return NULL;
}
if (midifile == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL filename)",
0);
return NULL;
}
if ((mididata = _WM_BufferFile(midifile, &midisize)) == NULL) {
return NULL;
}
ret = (void *) WM_ParseNewMidi(mididata, midisize);
free(mididata);
if (ret) {
if (add_handle(ret) != 0) {
WildMidi_Close(ret);
ret = NULL;
}
}
return ret;
}
WM_SYMBOL midi *
WildMidi_OpenBuffer(unsigned char *midibuffer, unsigned long int size) {
midi * ret = NULL;
if (!WM_Initialized) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
return NULL;
}
if (midibuffer == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(NULL midi data buffer)", 0);
return NULL;
}
if (size > WM_MAXFILESIZE) {
/* don't bother loading suspiciously long files */
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LONGFIL, NULL, 0);
return NULL;
}
ret = (void *) WM_ParseNewMidi(midibuffer, size);
if (ret) {
if (add_handle(ret) != 0) {
WildMidi_Close(ret);
ret = NULL;
}
}
return ret;
}
WM_SYMBOL int WildMidi_FastSeek(midi * handle, unsigned long int *sample_pos) {
struct _mdi *mdi;
struct _event *event;
struct _note *note_data;
unsigned long int real_samples_to_mix;
unsigned long int count;
if (!WM_Initialized) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
return -1;
}
if (handle == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)",
0);
return -1;
}
if (sample_pos == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(NULL seek position pointer)", 0);
return -1;
}
mdi = (struct _mdi *) handle;
_WM_Lock(&mdi->lock);
event = mdi->current_event;
/* make sure we havent asked for a positions beyond the end of the song. */
if (*sample_pos > mdi->info.approx_total_samples) {
/* if so set the position to the end of the song */
*sample_pos = mdi->info.approx_total_samples;
}
/* was end of song requested and are we are there? */
if (*sample_pos == mdi->info.current_sample) {
/* yes */
_WM_Unlock(&mdi->lock);
return 0;
}
/* did we want to fast forward? */
if (mdi->info.current_sample < *sample_pos) {
/* yes */
count = *sample_pos - mdi->info.current_sample;
} else {
/* no, reset values to start as the beginning */
count = *sample_pos;
WM_ResetToStart(handle);
event = mdi->current_event;
}
/* clear the reverb buffers since we not gonna be using them here */
_WM_reset_reverb(mdi->reverb);
while (count) {
if (__builtin_expect((!mdi->samples_to_mix), 0)) {
while ((!mdi->samples_to_mix) && (event->do_event)) {
event->do_event(mdi, &event->event_data);
event++;
mdi->samples_to_mix = event->samples_to_next;
mdi->current_event = event;
}
if (!mdi->samples_to_mix) {
if (event->do_event == NULL) {
mdi->samples_to_mix = mdi->info.approx_total_samples
- *sample_pos;
} else {
mdi->samples_to_mix = count;
}
}
}
if (__builtin_expect((mdi->samples_to_mix > count), 0)) {
real_samples_to_mix = count;
} else {
real_samples_to_mix = mdi->samples_to_mix;
}
if (real_samples_to_mix == 0) {
break;
}
count -= real_samples_to_mix;
mdi->info.current_sample += real_samples_to_mix;
mdi->samples_to_mix -= real_samples_to_mix;
}
note_data = mdi->note;
if (note_data) {
do {
note_data->active = 0;
if (note_data->replay) {
note_data->replay = NULL;
}
note_data = note_data->next;
} while (note_data);
}
mdi->note = NULL;
_WM_Unlock(&mdi->lock);
return 0;
}
WM_SYMBOL int WildMidi_GetOutput(midi * handle, char *buffer, unsigned long int size) {
if (__builtin_expect((!WM_Initialized), 0)) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
return -1;
}
if (__builtin_expect((handle == NULL), 0)) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)",
0);
return -1;
}
if (__builtin_expect((buffer == NULL), 0)) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(NULL buffer pointer)", 0);
return -1;
}
if (__builtin_expect((size == 0), 0)) {
return 0;
}
if (__builtin_expect((!!(size % 4)), 0)) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(size not a multiple of 4)", 0);
return -1;
}
if (((struct _mdi *) handle)->info.mixer_options & WM_MO_ENHANCED_RESAMPLING) {
if (!gauss_table) init_gauss();
return WM_GetOutput_Gauss(handle, buffer, size);
}
return WM_GetOutput_Linear(handle, buffer, size);
}
WM_SYMBOL int WildMidi_SetOption(midi * handle, unsigned short int options,
unsigned short int setting) {
struct _mdi *mdi;
int i;
if (!WM_Initialized) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
return -1;
}
if (handle == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)",
0);
return -1;
}
mdi = (struct _mdi *) handle;
_WM_Lock(&mdi->lock);
if ((!(options & 0x0007)) || (options & 0xFFF8)) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid option)",
0);
_WM_Unlock(&mdi->lock);
return -1;
}
if (setting & 0xFFF8) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(invalid setting)", 0);
_WM_Unlock(&mdi->lock);
return -1;
}
mdi->info.mixer_options = ((mdi->info.mixer_options & (0x00FF ^ options))
| (options & setting));
if (options & WM_MO_LOG_VOLUME) {
struct _note *note_data = mdi->note;
for (i = 0; i < 16; i++) {
do_pan_adjust(mdi, i);
}
if (note_data) {
do {
note_data->vol_lvl = get_volume(mdi, (note_data->noteid >> 8),
note_data);
if (note_data->replay)
note_data->replay->vol_lvl = get_volume(mdi,
(note_data->noteid >> 8), note_data->replay);
note_data = note_data->next;
} while (note_data);
}
} else if (options & WM_MO_REVERB) {
_WM_reset_reverb(mdi->reverb);
}
_WM_Unlock(&mdi->lock);
return 0;
}
WM_SYMBOL struct _WM_Info *
WildMidi_GetInfo(midi * handle) {
struct _mdi *mdi = (struct _mdi *) handle;
if (!WM_Initialized) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
return NULL;
}
if (handle == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)",
0);
return NULL;
}
_WM_Lock(&mdi->lock);
if (mdi->tmp_info == NULL) {
mdi->tmp_info = (struct _WM_Info*)malloc(sizeof(struct _WM_Info));
if (mdi->tmp_info == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to set info", 0);
_WM_Unlock(&mdi->lock);
return NULL;
}
mdi->tmp_info->copyright = NULL;
}
mdi->tmp_info->current_sample = mdi->info.current_sample;
mdi->tmp_info->approx_total_samples = mdi->info.approx_total_samples;
mdi->tmp_info->mixer_options = mdi->info.mixer_options;
if (mdi->info.copyright) {
free(mdi->tmp_info->copyright);
mdi->tmp_info->copyright = (char*)malloc(strlen(mdi->info.copyright) + 1);
strcpy(mdi->tmp_info->copyright, mdi->info.copyright);
} else {
mdi->tmp_info->copyright = NULL;
}
_WM_Unlock(&mdi->lock);
return mdi->tmp_info;
}
WM_SYMBOL int WildMidi_Shutdown(void) {
if (!WM_Initialized) {
// No error if trying to shut down an uninitialized device.
return 0;
}
while (first_handle) {
/* closes open handle and rotates the handles list. */
WildMidi_Close((struct _mdi *) first_handle->handle);
}
WM_FreePatches();
free_gauss();
/* reset the globals */
WM_MasterVolume = 948;
WM_MixerOptions = 0;
fix_release = 0;
auto_amp = 0;
auto_amp_with_amp = 0;
reverb_room_width = 16.875f;
reverb_room_length = 22.5f;
reverb_listen_posx = 8.4375f;
reverb_listen_posy = 16.875f;
WM_Initialized = 0;
return 0;
}