Ticket #7627: Midi.c

File Midi.c, 6.2 KB (added by raziel-, 16 years ago)
Line 
1/* A very simple program, that converts an AGI-song into a MIDI-song. */
2/* Feel free to use it for anything. */
3
4/* To compile (using DJGPP for MS/DOS) type: */
5/* gcc -lm -O3 snd2midi.c -o snd2midi */
6/* if you've got problems on unix, type: */
7/* gcc -lm -O3 -DUNIX snd2midi.c -o snd2midi */
8
9/* The default instrument is "piano" for all the channels, what gives */
10/* good results on most games. But I found, that some songs are interesting */
11/* with other instruments. If you want to experiment, modify the "instr" */
12/* array. */
13
14/* Timing is not perfect, yet. It plays correct, when I use the */
15/* Gravis-Midiplayer, but the songs are too fast when I use playmidi on */
16/* Linux. */
17
18/* (A Wavetable-card is handy, here :-) */
19
20/* ---------------------------------------------------------------------- */
21/* Original program developed by Jens. Christian Restemeier */
22
23/* Modifications for a speed choice: 4 September 1997, Lance Ewing. */
24/* If SLOW is defined, then the speed will be more suitable for the */
25/* majority of MIDI players. The initial speed used doesn't seem to be */
26/* standard. */
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <math.h>
31
32/* #define SLOW */
33
34#ifndef SLOW
35#define SPEED_FACTOR 4
36#else
37#define SPEED_FACTOR 6
38#endif
39
40unsigned char *snddata;
41
42/* channel / intrument setup: */
43
44/* most songs are good with this: */
45unsigned char instr[] = {0, 0, 0};
46
47/* cool for sq2:
48unsigned char instr[] = {50, 51, 19};
49*/
50
51void write_byte(FILE *f, long data)
52{
53 fwrite(&data,1,1,f);
54}
55
56void write_word(FILE *f, long data)
57{
58 write_byte(f,(data>>8)&255);
59 write_byte(f,data&255);
60}
61
62void write_long(FILE *f, long data)
63{
64 write_byte(f,(data>>24)&255);
65 write_byte(f,(data>>16)&255);
66 write_byte(f,(data>>8)&255);
67 write_byte(f,data&255);
68}
69
70void write_delta(FILE *f,long delta)
71{
72 long i;
73 i=delta>>21; if (i>0) write_byte(f,(i&127)|128);
74 i=delta>>14; if (i>0) write_byte(f,(i&127)|128);
75 i=delta>>7; if (i>0) write_byte(f,(i&127)|128);
76 write_byte(f,delta&127);
77}
78
79void write_midi(FILE *f)
80{
81 long lp,ep;
82 int n;
83 double ll;
84
85 ll=log10(pow(2.0,1.0/12.0));
86
87 /* Header */
88 fwrite("MThd",4,1,f);
89 write_long(f,6);
90 write_word(f,1); /* mode */
91 write_word(f,3); /* number of tracks */
92 write_word(f,192); /* ticks / quarter */
93
94 for (n=0;n<3;n++) {
95 unsigned short start, end, pos;
96
97 fwrite("MTrk",4,1,f);
98 lp=ftell(f);
99 write_long(f,0); /* chunklength */
100 write_delta(f,0); /* set instrument */
101 write_byte(f,0xc0+n);
102 write_byte(f,instr[n]);
103 start=snddata[n*2+0] | (snddata[n*2+1]<<8);
104 end=((snddata[n*2+2] | (snddata[n*2+3]<<8)))-5;
105
106 for (pos=start; pos<end; pos+=5) {
107 unsigned short freq, dur;
108 dur=(snddata[pos+0] | (snddata[pos+1]<<8))*SPEED_FACTOR;
109 freq=((snddata[pos+2] & 0x3F) << 4) + (snddata[pos+3] & 0x0F);
110 if (snddata[pos+2]>0) {
111 double fr;
112 int note;
113 /* I don't know, what frequency equals midi note 0 ... */
114 /* This moves the song 4 octaves down: */
115 fr=(log10(111860.0 / (double)freq) / ll) - 48;
116 note=floor(fr+0.5);
117 if (note<0) note=0;
118 if (note>127) note=127;
119 /* note on */
120 write_delta(f,0);
121 write_byte(f,144+n);
122 write_byte(f,note);
123 write_byte(f,100);
124 /* note off */
125 write_delta(f,dur);
126 write_byte(f,128+n);
127 write_byte(f,note);
128 write_byte(f,0);
129 } else {
130 /* note on */
131 write_delta(f,0);
132 write_byte(f,144+n);
133 write_byte(f,0);
134 write_byte(f,0);
135 /* note off */
136 write_delta(f,dur);
137 write_byte(f,128+n);
138 write_byte(f,0);
139 write_byte(f,0);
140 }
141 }
142 write_delta(f,0);
143 write_byte(f,0xff);
144 write_byte(f,0x2f);
145 write_byte(f,0x0);
146 ep=ftell(f);
147 fseek(f,lp,SEEK_SET);
148 write_long(f,(ep-lp)-4);
149 fseek(f,ep,SEEK_SET);
150 }
151}
152
153/* MS/DOS requires "b"-mode, but I heard, that some unix-systems don't */
154/* like it. */
155#ifndef UNIX
156#define O_RB "rb"
157#define O_WB "wb"
158#else
159#define O_RB "r"
160#define O_WB "w"
161#endif
162
163void main(int argc, char *argv[])
164{
165 FILE *fsnd, *fmid;
166 long size;
167
168 if (argc<3) {
169 printf("snd2midi <sndfile> <midifile>\n");
170 } else {
171 if ((fsnd=fopen(argv[1],"rb"))!=NULL) {
172 fseek(fsnd,0,SEEK_END);
173 size=ftell(fsnd);
174 fseek(fsnd,0,SEEK_SET);
175 snddata=malloc(size);
176 fread(snddata,1,size,fsnd);
177 fclose(fsnd);
178
179 if ((fmid=fopen(argv[2],"wb"))!=NULL) {
180 write_midi(fmid);
181 fclose(fmid);
182 printf("ok !\n");
183 } else printf("Error opening output !\n");
184 free(snddata);
185 } else printf("Error opening input !\n");
186 }
187}
188
189