Tuning a MIDI harpsichord

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.
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"
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!
380 KILL "\cubase\scarlati\"+F$: GOTO 400
390  RESUME 400             :REM file not present is OK here
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
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