The following program is what I use to tune my MIDI recordings so they sound the same as when I play them on my real harpsichord. You will have to be a programmer used to working with binary files to adapt it for your specific compiler and sequencer.
The format used for my MIDI files is type 0 (single block). Each of the 12 notes of the scale is placed in its own channel, then a pitch bend applied for that channel appropriate for the tuning, assuming the default ±2 semitones bend range of the General MIDI specification. The "set GM" code is not inserted because, paradoxically, it gives problems on some systems. The output format of this program plays correctly on all GM-compatible players of which I am aware (and everything else I tried gave problems on a lot of systems). However, most sequencer programs insist on modifying it on file output. You must edit your original file, reexport it as type 0 MIDI, then run this program on the new output.
The following is Freeware, and it's worth every penny :-) Do not contact me with any problem you experience on your system - it will be different from mine.
John Sankey
other notes on computing
10 REM ALEMBERT: add name to Cubase Compact type 0 MIDI file 20 REM set channel 0-8,10-12 instrument to harpsichord 30 REM split notes into separate channels, tune to d'Alembert 40 REM no change of velocities 50 REM Cubase time signature & null trackname are removed 60 REM NOTICE: this works with my sequencer and Basic compiler 70 REM it WILL require modification with other programs. 80 DEFLNG N 90 REM ********** build string to add to file ********** 100 T0$="" :REM put your name here 110 T$=T$+CHR$(0)+CHR$(255)+CHR$(2)+CHR$(LEN(T0$))+T0$ 120 FOR I%=0 TO 11 130 IF I%<9 THEN J%=I% ELSE J%=I%+1 :REM GM channel 140 T$=T$+CHR$(0)+CHR$(192+J%)+CHR$(6) :REM harpsichord 150 T$=T$+CHR$(0)+CHR$(224+J%) :REM pitch bend 160 IF I%=0 THEN V=0 ELSE READ V :REM channel pitch cents 170 K%=(V-I%*100!)*40.96+8192 :REM bend GM steps 180 T$=T$+CHR$(K% AND 127)+CHR$(K%\128) 190 NEXT I% 200 REM data for D'Alembert (French ordinary temperament) 210 DATA 85.59,193.36,291.39,386.32,498.04,584.75,696.83,787.5,888.67,994.93,1086.51 220 REM ********** get midi file ********* 230 INPUT "?.MID";F$: F$=F$+".MID" 240 OPEN F$ FOR BINARY ACCESS READ AS 1 250 H$=INPUT$(18,1) :REM header 260 IF ASC(MID$(H$,12,1))<>1 THEN PRINT "not type 0 MIDI file!": GOTO 750 270 N=0 280 FOR I%=1 TO 4 :REM track length 290 N=N*256+ASC(INPUT$(1,1)) 300 NEXT I% 310 N=N+LEN(T$)-12 :REM new music track length 320 H$=H$+CHR$(((N\256)\256)\256)+CHR$(((N\256)\256) AND 255) 330 H$=H$+CHR$((N\256) AND 255)+CHR$(N AND 255) 340 C$=H$+T$ 350 REM ********** create output file ********** 360 REM file length is not decreased by QuickBasic binary open, just increased! 370 ON ERROR GOTO 390 380 KILL "\cubase\scarlati\"+F$: GOTO 400 390 RESUME 400 :REM file not present is OK here 400 ON ERROR GOTO 0 410 OPEN "\cubase\scarlati\"+F$ FOR BINARY ACCESS WRITE AS 2 420 REM ********** split music track notes into channels ********** 430 PUT#2,,C$: C$="" :REM output current working string 440 FOR X%=1 TO 4 :REM get deltatime (variable length) 450 V$=INPUT$(1,1) 460 C$=C$+V$ :REM transfer to output 470 IF (ASC(V$) AND 128)=0 THEN 490 :REM end of deltatime 480 NEXT X% 490 V$=INPUT$(1,1): C%=ASC(V$) :REM get next event 500 IF (C% AND 128)=0 THEN PRINT "Invalid event!": GOTO 750 510 IF C%<>255 THEN 610 520 E%=ASC(INPUT$(1,1)) :REM meta event 530 M%=ASC(INPUT$(1,1)) :REM data length 540 C$=C$+V$+CHR$(E%)+CHR$(M%) 550 FOR I%=1 TO M% 560 C$=C$+INPUT$(1,1) 570 NEXT I% 580 IF E%=3 AND M%=0 THEN Z%=Z%+3: C$="": GOTO 440 :REM omit trackname 590 IF E%=88 THEN Z%=Z%+5: C$="": GOTO 440 :REM omit timesig 600 IF E%=47 THEN 710 ELSE 420 :REM end-of-track or continue 610 S%=((C%\16) AND 7): IF S%<>0 THEN 660 620 K%=ASC(INPUT$(1,1)) :REM note off event 630 C%=(K% MOD 12): IF C%>8 THEN C%=C%+1 :REM skip Standard channel 640 C$=C$+CHR$(128+C%)+CHR$(K%)+INPUT$(1,1) :REM new command 650 GOTO 420 660 IF S%<>1 THEN 420 670 K%=ASC(INPUT$(1,1)) :REM note on event 680 C%=(K% MOD 12): IF C%>8 THEN C%=C%+1 690 C$=C$+CHR$(144+C%)+CHR$(K%)+INPUT$(1,1) :REM new command 700 GOTO 420 710 PUT#2,,C$ :REM output end-of-track 720 CLOSE 730 IF Z%=8 THEN 760 :REM normal termination 740 PRINT "trackname &/or timesig problem!" 750 IF LEN(INKEY$)=0 THEN 750 :REM error termination 760 END