📄 m2m.c
字号:
/* TiMidity++ -- MIDI to WAVE converter and player Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp> Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*//* * EAW -- Quick and dirty hack to convert the TiMidity events generated from * a MOD file into events suitable for writing to a midi file, then write * the midi file. * * DONE LIST (various odd things needed for MOD->MIDI format conversion): * 1) MIDI does not support pitch bends > 2 octaves... * scale pitch bends for a sensitivy of 24 instead of 128, * pitch bends > 2 octaves wrapped back an octave and continue as normal * 2) moved all drum voices onto the drum channel, and all non-drum voices * off of the drum channel to the first available channel (which could be * a channel that has been freed by moving all of it's drum voices off * of it) * 3) moved/duped events on to other channels to handle the drum related stuff * 4) force all drums to center pan, since drums originally on different * channels could mess up each other's pans when they are merged on to a * single channel * 5) added in extra stuff to keep track of and scale expression events as * necessary, so that each sample can have it's own amplification setting * 6) turn all drum channel expression stuff into initial note volumes * 7) experimented with turning expression events into keypressure events, * but this didn't sound good, because my XG card doesn't handle increases * in keypressure, and the expression events are also needed to boost the * volumes of note decays (since keypressure doesn't work on dead notes) * 8) emit chords for chord samples * 9) issue bank change events, as specified in the cfg file * 10) ignore redundant program changes, which often happen from drum moves * 11) ability to silence a sample, so it emits no note or program events * 12) issue port events to make midi with > 16 channels * 13) skip over the drum channel on the 2nd port * 14) kill non-looped mod samples when the play past sample end * 15) extended bend range to 4 octaves using note+pitchbend shifting * 16) automatic sample chord/pitch assignment and cfg file generation !!! :) * 17) can offset pitchbends to undo out of tune sample tuning (detuning) * 18) converts linear mod volumes into non-linear midi volumes * 19) other more minor things that may or may not be commented * * TODO LIST (likely to be done eventually) * 1) correctly implement fine tuning tweaks via extra pitch bends * 2) maybe issue a SYSEX event to make channel 26 non-drum for regular use? * 3) clean up the code some more * * WISH LIST (far less likely to ever be done): * 1) make an alternate output mode that outputs a separate track for every * combination of sample and channel it is used on * 2) possibly have an automated channel merger, but it might have to strip * out some pans, expression events, and pitch bends to do it * 3) maybe handle > 4 octave pitch bends in a better way */#ifdef HAVE_CONFIG_H#include "config.h"#endif /* HAVE_CONFIG_H */#include <stdio.h>#include <stdlib.h>#include <math.h>#ifndef NO_STRING_H#include <string.h>#else#include <strings.h>#endif#include "timidity.h"#include "common.h"#include "instrum.h"#include "playmidi.h"#include "readmidi.h"#include "output.h"#include "controls.h"#include "tables.h"#include "freq.h"static char *outname = NULL;static char *actual_outname;static char *cfgname = NULL;static unsigned char header[14] = { 0x4D, 0x54, 0x68, 0x64, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00};static unsigned char mtrk[4] = { 0x4D, 0x54, 0x72, 0x6B };static unsigned char event[11];static unsigned char dt_array[4];static unsigned char *track_events[34];static unsigned char *p_track_event;static uint32 last_track_event_time[34];static uint32 track_size[34];static int tracks_enabled[34];static int tracks_useless[34];static int current_track_sample[34];static int orig_track_expr[34];static int current_channel_expr[34];static int current_channel_bank[34];static int current_channel_program[34];static int current_channel_note[34];static int current_track_note[34];static int track_pans[34];static uint32 kill_early_time[34];static int kill_early_note[34];static int kill_early_velocity[34];static int kill_early_ch[34];static int tweak_note_offset[34];static int tweak_pb_offset[34];static uint32 length;static uint16 divisions, orig_divisions;static double divisions_ratio;static int num_dt_bytes;static int32 value;static int num_tracks = 0;static int32 tempo = 500000;static uint32 maxtime = 0;static uint32 num_killed_early = 0;static uint32 num_big_pitch_slides = 0;static uint32 num_huge_pitch_slides = 0;static int pb_sensitivity = 24;static int old_pb_sensitivity = 128;static float notes_per_pb = 24.0 / 8191.0;static float pb_per_note = 8191.0 / 24.0;static int rpn_msb = 0, rpn_lsb = 0;static int min_enabled_track = -1, max_enabled_track, first_free_track = -1;static int non_drums_on_drums = 0;#define MAX_PB_SENSITIVITY 24static int silent_samples[256];static int sample_chords[256];static int sample_to_program[256];static int banks[256];static int transpose[256];static int is_drum_sample[256];static int vol_amp[256];static char linestring[256];static int fine_tune[256];static double samples_per_tick;static int maxsample;static char chord_letters[4] = { 'M', 'm', 'd', 'f' };#define ROUND(frac) (floor(frac + 0.5))/* mod volumes are linear, midi volumes are x^1.66 (for generic hardware) * scale the mod volumes so they sound linear on non-linear midi devices * EXPRESSION events are the new corrected volume levels * MAINVOLUME events fine tune the volume correction * * lookup_table[mod_vol][expression, volume] */static char vol_nonlin_to_lin[128][2] = { 0, 127, 7, 125, 11, 120, 14, 121, 16, 126, 19, 121, 21, 122, 23, 122, 25, 122, 26, 126, 28, 125, 30, 123, 31, 126, 33, 124, 34, 126, 36, 124, 37, 125, 38, 126, 40, 124, 41, 125, 42, 126, 43, 127, 45, 125, 46, 125, 47, 126, 48, 126, 49, 127, 50, 127, 52, 125, 53, 125, 54, 125, 55, 125, 56, 126, 57, 126, 58, 126, 59, 126, 60, 126, 61, 126, 62, 126, 63, 126, 64, 126, 65, 126, 66, 126, 67, 125, 68, 125, 69, 125, 69, 127, 70, 127, 71, 126, 72, 126, 73, 126, 74, 126, 75, 126, 76, 125, 76, 127, 77, 127, 78, 126, 79, 126, 80, 126, 81, 126, 81, 127, 82, 126, 83, 126, 84, 126, 85, 126, 85, 127, 86, 126, 87, 126, 88, 126, 88, 127, 89, 127, 90, 126, 91, 126, 91, 127, 92, 127, 93, 126, 94, 126, 94, 127, 95, 127, 96, 126, 97, 126, 97, 127, 98, 126, 99, 126, 100, 126, 100, 127, 101, 126, 102, 126, 102, 127, 103, 126, 104, 126, 104, 127, 105, 127, 106, 126, 106, 127, 107, 127, 108, 126, 108, 127, 109, 127, 110, 126, 110, 127, 111, 127, 112, 126, 112, 127, 113, 127, 114, 126, 114, 127, 115, 127, 116, 126, 116, 127, 117, 126, 118, 126, 118, 127, 119, 126, 120, 126, 120, 127, 121, 126, 121, 127, 122, 126, 123, 126, 123, 127, 124, 126, 124, 127, 125, 127, 126, 126, 126, 127, 127, 126, 127, 127 };/* * Uses the volume curve specified by the -V or --volume-curve option. * * The resulting midi will sound correct on hardware / software that uses * the volume curve selected by the user. * * -V 1.661, or (1/log10(4)), is the default timidity volume curve for GM/XG, * and is thus the default for MOD->MIDI output as well, so that the * resulting midi will sound correct when played using default timidity * options. * * -V 2 should be used to generate midi for playback on MIDI hardware, since * the GM/GS/XG standards all specify a volume curve of X^2 * * -V 1.661 will still sound good on MIDI hardware, though, and should sound * better than -V 2 on softsynths that (incorrectly) use linear volume curves * (such as TiMidity++ versions <= 2.11.3). * * Use -V 1 if you want to generate midi ONLY for linear volume devices. * It will sound bad when played with anything else. */void fill_vol_nonlin_to_lin_table(void){ int i; int coarse, fine; double power = 0; double inverse; double temp; double log_127; /* derive the power used to generate the user volume table */ log_127 = log(127); for (i = 1; i <= 126; i++) power += (log(user_vol_table[i]) - log_127) / (log(i) - log_127); power /= 126; /* use the inverse of the power to generate the new table */ for (i = 1; i <= 127; i++) { inverse = pow(i / 127.0, 1.0 / power); temp = 127 * inverse; coarse = floor(temp + 0.5); if (coarse < temp) coarse += 1; fine = floor(127 * temp / coarse + 0.5); vol_nonlin_to_lin[i][0] = coarse; vol_nonlin_to_lin[i][1] = fine; }}/* generate the names of the output and cfg files from the input file *//* modified from auto_wav_output_open() in wave_a.c */static int auto_names(const char *input_filename){ char *ext, *p; outname = (char *) safe_realloc(outname, sizeof(char) * (strlen(input_filename) + 5)); cfgname = (char *) safe_realloc(cfgname, sizeof(char) * (strlen(input_filename) + 5)); strcpy(outname, input_filename); if ((ext = strrchr(outname, '.')) == NULL) ext = outname + strlen(outname); else { /* strip ".???" */ *ext = '\0'; } /* replace '.' and '#' before ext */ for (p = outname; p < ext; p++) if (*p == '.' || *p == '#') *p = '_'; strcpy(cfgname, outname); strcat(outname, ".mid"); strcat(cfgname, ".m2m"); actual_outname = outname; return 0;}void initialize_m2m_stuff(void){ int i; memset(track_events, 0, 34 * sizeof(unsigned char *)); memset(last_track_event_time, 0, 34 * sizeof(uint32)); memset(track_size, 0, 34 * sizeof(uint32)); memset(tracks_enabled, 0, 34 * sizeof(int)); memset(tracks_useless, 0, 34 * sizeof(int)); memset(current_track_sample, 0, 34 * sizeof(int)); memset(orig_track_expr, 0, 34 * sizeof(int)); memset(current_channel_expr, 0, 34 * sizeof(int)); memset(current_channel_bank, 0, 34 * sizeof(int)); memset(current_channel_program, 0, 34 * sizeof(int)); memset(current_channel_note, 0, 34 * sizeof(int)); memset(current_track_note, 0, 34 * sizeof(int)); memset(banks, 0, 256 * sizeof(int)); memset(transpose, 0, 256 * sizeof(int)); memset(is_drum_sample, 0, 256 * sizeof(int)); memset(silent_samples, 0, 256 * sizeof(int)); memset(fine_tune, 0, 256 * sizeof(int)); /* get the names of the output and cfg files */ auto_names(current_file_info->filename); if (play_mode->name != NULL) actual_outname = play_mode->name; ctl->cmsg(CMSG_INFO, VERB_NORMAL, "Output %s", actual_outname); for (i = 0; i < 256; i++) { sample_to_program[i] = i; if (i > 127) sample_to_program[i] = i - 127; sample_chords[i] = -1; vol_amp[i] = 100; } for (i = 0; i < 34; i++) { tracks_useless[i] = 1; current_track_sample[i] = 255; current_channel_note[i] = -1; current_track_note[i] = -1; } /* support to increase the number of divisions, this may be useful */ orig_divisions = current_file_info->divisions; divisions = 480; /* maximum number of divisions */ divisions_ratio = (divisions / orig_divisions); num_tracks = 0; tempo = 500000; maxtime = 0; num_killed_early = 0; num_big_pitch_slides = 0; num_huge_pitch_slides = 0; pb_sensitivity = 24; old_pb_sensitivity = 128; notes_per_pb = 24.0 / 8191.0; pb_per_note = 8191.0 / 24.0; rpn_msb = 0; rpn_lsb = 0; min_enabled_track = -1; first_free_track = -1; non_drums_on_drums = 0; for (i = 1, maxsample = 0; i < 256; i++) { if (special_patch[i]) maxsample = i; }}int create_m2m_cfg_file(char *cfgname){ FILE *cfgout; int i, chord, chord_type, chord_subtype; char line[81]; char chord_str[3]; char program_str[17]; cfgout = fopen(cfgname, "wb"); if (!cfgout) { ctl->cmsg(CMSG_INFO, VERB_NORMAL, "Could not open cfg file %s for writing", cfgname); return 0; } fprintf(cfgout, "%s\t%s\t\t%s\t%s\t%s\n\n", "# Sample", "Program", "Transpose", "FineTuning", "%Volume"); for (i = 1; i <= maxsample; i++) { memset(chord_str, 0, 3 * sizeof(char)); if (special_patch[i]) { chord = sample_chords[i]; if (chord >= 0) { chord_type = chord / 3; chord_subtype = chord % 3; chord_str[0] = chord_letters[chord_type]; if (chord_subtype) chord_str[1] = '0' + chord_subtype; } sprintf(program_str, "%d%s", sample_to_program[i], chord_str); sprintf(line, "%d\t\t%s\t\t%d\t\t!%.6f\t100\n", i, program_str, transpose[i], fine_tune[i] * notes_per_pb); } else { sprintf(line, "# %d unused\n", i); } fprintf(cfgout, "%s", line); } fclose(cfgout); return 1;}void read_m2m_cfg_file(void){ FILE *mod_cfg_file; float freq; int i, pitch; char *chord_str; int chord, chord_type, chord_subtype; char line[81]; int mod_sample, program, trans, amp; char program_str[20], finetune_str[20]; char *slash_pos; /* read in cfg file stuff, create new one if doesn't exist */ mod_cfg_file = fopen(cfgname, "rb"); if (!mod_cfg_file) { ctl->cmsg(CMSG_INFO, VERB_NORMAL, "Couldn't open '%s' cfg file. Creating %s ...", cfgname, cfgname); for (i = 1; i <= maxsample; i++) { if (special_patch[i]) { chord = special_patch[i]->sample->chord; freq = special_patch[i]->sample->root_freq_detected; pitch = assign_pitch_to_freq(freq); fine_tune[i] = (-36.37631656f + 17.31234049f * log(freq) - pitch) * pb_per_note; sprintf(line, "Sample %3d Freq %10.4f Pitch %3d Transpose %4d", i, freq, pitch, special_patch[i]->sample->transpose_detected); if (chord >= 0) { sprintf(line, "%s Chord %c Subtype %d", line, chord_letters[chord / 3], chord % 3); } ctl->cmsg(CMSG_INFO, VERB_NORMAL, "%s", line); transpose[i] = special_patch[i]->sample->transpose_detected; sample_chords[i] = chord; } } create_m2m_cfg_file(cfgname); mod_cfg_file = fopen(cfgname, "rb"); } if (mod_cfg_file) { while (fgets(linestring, 256, mod_cfg_file) != NULL) { if (*linestring == '#' || *linestring == '\n' || *linestring == '\r') continue; sscanf(linestring, "%d %s %d %s %d\n", &mod_sample, program_str, &trans, finetune_str, &); if (strchr(program_str, '!')) silent_samples[mod_sample] = 1; program = labs(atoi(program_str)); if ((slash_pos = strchr(program_str, '/'))) { banks[mod_sample] = program; program = labs(atoi(slash_pos + 1)); } sample_to_program[mod_sample] = program; transpose[mod_sample] = trans; if (strchr(finetune_str, '!')) fine_tune[mod_sample] = 0; else fine_tune[mod_sample] = atof(finetune_str) * pb_per_note; vol_amp[mod_sample] = amp; if (strchr(program_str, '*')) is_drum_sample[mod_sample] = 1; else if ((chord_str = strchr(program_str, 'M'))) { chord_type = strchr(chord_letters, 'M') - chord_letters; chord_subtype = atoi(chord_str + 1); sample_chords[mod_sample] = chord_type * 3 + chord_subtype; } else if ((chord_str = strchr(program_str, 'm'))) { chord_type = strchr(chord_letters, 'm') - chord_letters; chord_subtype = atoi(chord_str + 1); sample_chords[mod_sample] = chord_type * 3 + chord_subtype; } else if ((chord_str = strchr(program_str, 'd'))) { chord_type = strchr(chord_letters, 'd') - chord_letters; chord_subtype = atoi(chord_str + 1); sample_chords[mod_sample] = chord_type * 3 + chord_subtype; } else if ((chord_str = strchr(program_str, 'f'))) { chord_type = strchr(chord_letters, 'f') - chord_letters; chord_subtype = atoi(chord_str + 1); sample_chords[mod_sample] = chord_type * 3 + chord_subtype; } } fclose(mod_cfg_file); } else ctl->cmsg(CMSG_INFO, VERB_NORMAL,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -