/* 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; }