Mercurial > SDL_sound_CoreAudio
comparison decoders/midi.c @ 200:f75ed2d72238
Rewrite to call an included, patched version of Timidity routines directly.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Fri, 04 Jan 2002 06:50:37 +0000 |
parents | 47cc2de2ae36 |
children | c9772a9f5271 |
comparison
equal
deleted
inserted
replaced
199:2d887640d300 | 200:f75ed2d72238 |
---|---|
16 * License along with this library; if not, write to the Free Software | 16 * License along with this library; if not, write to the Free Software |
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18 */ | 18 */ |
19 | 19 |
20 /* | 20 /* |
21 * MIDI decoder for SDL_sound, using TiMidity. | 21 * MIDI decoder for SDL_sound. |
22 * | 22 * |
23 * This is a rough, proof-of-concept implementation. A number of things need | 23 * This driver handles MIDI data through a stripped-down version of TiMidity. |
24 * to be fixed or at the very least looked at: | 24 * See the documentation in the timidity subdirectory. |
25 * | |
26 * - I have only tested this with TiMidity++. Does TiMidity use the same | |
27 * command-line options? | |
28 * - No attempt is made to ensure that the input really is MIDI. Eek. Does | |
29 * anyone here know how to recognize MIDI? | |
30 * - Error handling is a joke. | |
31 * - Did I make any stupid errors in the process communication? | |
32 * - TiMidity++ spews a number of messages (to stderr?) which should be | |
33 * silenced eventually. | |
34 * - This has to be the least portable decoder that has ever been written. | |
35 * - Etc. | |
36 * | |
37 * Oh, and the whole thing should be rewritten as a generic "send data to | |
38 * external decoder" thing so this version should either be scrapped or | |
39 * cannibalized for ideas. :-) | |
40 * | 25 * |
41 * Please see the file COPYING in the source's root directory. | 26 * Please see the file COPYING in the source's root directory. |
42 * | 27 * |
43 * This file written by Torbjörn Andersson. (d91tan@Update.UU.SE) | 28 * This file written by Torbjörn Andersson. (d91tan@Update.UU.SE) |
44 */ | 29 */ |
51 | 36 |
52 #include <stdio.h> | 37 #include <stdio.h> |
53 #include <stdlib.h> | 38 #include <stdlib.h> |
54 #include <string.h> | 39 #include <string.h> |
55 #include <assert.h> | 40 #include <assert.h> |
56 #include <unistd.h> | 41 |
57 #include <signal.h> | |
58 #include <sys/types.h> | |
59 #include <sys/wait.h> | |
60 #include "SDL_sound.h" | 42 #include "SDL_sound.h" |
61 | 43 |
62 #define __SDL_SOUND_INTERNAL__ | 44 #define __SDL_SOUND_INTERNAL__ |
63 #include "SDL_sound_internal.h" | 45 #include "SDL_sound_internal.h" |
46 | |
47 #include "timidity.h" | |
64 | 48 |
65 | 49 |
66 static int MIDI_init(void); | 50 static int MIDI_init(void); |
67 static void MIDI_quit(void); | 51 static void MIDI_quit(void); |
68 static int MIDI_open(Sound_Sample *sample, const char *ext); | 52 static int MIDI_open(Sound_Sample *sample, const char *ext); |
69 static void MIDI_close(Sound_Sample *sample); | 53 static void MIDI_close(Sound_Sample *sample); |
70 static Uint32 MIDI_read(Sound_Sample *sample); | 54 static Uint32 MIDI_read(Sound_Sample *sample); |
71 | 55 |
72 static const char *extensions_midi[] = { "MIDI", NULL }; | 56 static const char *extensions_midi[] = { "MIDI", "MID", NULL }; |
73 const Sound_DecoderFunctions __Sound_DecoderFunctions_MIDI = | 57 const Sound_DecoderFunctions __Sound_DecoderFunctions_MIDI = |
74 { | 58 { |
75 { | 59 { |
76 extensions_midi, | 60 extensions_midi, |
77 "MIDI music through the TiMidity MIDI to WAVE converter", | 61 "MIDI decoder, using a subset of TiMidity", |
78 "Torbjörn Andersson <d91tan@Update.UU.SE>", | 62 "Torbjörn Andersson <d91tan@Update.UU.SE>", |
79 "http://www.goice.co.jp/member/mo/timidity/" | 63 "http://www.icculus.org/SDL_sound/" |
80 }, | 64 }, |
81 | 65 |
82 MIDI_init, /* init() method */ | 66 MIDI_init, /* init() method */ |
83 MIDI_quit, /* quit() method */ | 67 MIDI_quit, /* quit() method */ |
84 MIDI_open, /* open() method */ | 68 MIDI_open, /* open() method */ |
85 MIDI_close, /* close() method */ | 69 MIDI_close, /* close() method */ |
86 MIDI_read /* read() method */ | 70 MIDI_read /* read() method */ |
87 }; | 71 }; |
88 | 72 |
89 /* this is what we store in our internal->decoder_private field... */ | |
90 typedef struct | |
91 { | |
92 int fd_audio; | |
93 } midi_t; | |
94 | |
95 | |
96 static void sig_pipe(int signo) | |
97 { | |
98 signal(signo, sig_pipe); | |
99 } /* sig_pipe */ | |
100 | |
101 | 73 |
102 static int MIDI_init(void) | 74 static int MIDI_init(void) |
103 { | 75 { |
104 if (signal(SIGPIPE, sig_pipe) == SIG_ERR) | 76 BAIL_IF_MACRO(Timidity_Init() < 0, "MIDI: Could not initialise", 0); |
105 { | |
106 Sound_SetError("MIDI: Could not set up SIGPIPE signal handler."); | |
107 return(0); | |
108 } /* if */ | |
109 return(1); | 77 return(1); |
110 } /* MIDI_init */ | 78 } /* MIDI_init */ |
111 | 79 |
112 | 80 |
113 static void MIDI_quit(void) | 81 static void MIDI_quit(void) |
114 { | 82 { |
115 /* it's a no-op. */ | 83 Timidity_Exit(); |
116 } /* MIDI_quit */ | 84 } /* MIDI_quit */ |
117 | 85 |
118 | 86 |
119 static int MIDI_open(Sound_Sample *sample, const char *ext) | 87 static int MIDI_open(Sound_Sample *sample, const char *ext) |
120 { | 88 { |
121 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | 89 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; |
122 SDL_RWops *rw = internal->rw; | 90 SDL_RWops *rw = internal->rw; |
123 midi_t *m; | 91 SDL_AudioSpec spec; |
124 Uint8 buf[BUFSIZ]; | 92 MidiSong *song; |
125 ssize_t retval; | |
126 int fd1[2]; | |
127 int fd2[2]; | |
128 pid_t pid; | |
129 | 93 |
130 /* | 94 spec.channels = 2; |
131 * Use the desired format if available, or pick sensible defaults. | 95 spec.format = AUDIO_S16SYS; |
132 * TiMidity does all the conversions we need. | 96 spec.freq = 44100; |
133 */ | 97 spec.samples = 4096; |
134 if (sample->desired.rate == 0) | 98 |
135 { | 99 song = Timidity_LoadSong(rw, &spec); |
136 sample->actual.channels = 2; | 100 BAIL_IF_MACRO(song == NULL, "MIDI: Not a MIDI file.", 0); |
137 sample->actual.rate = 44100; | 101 Timidity_SetVolume(song, 100); |
138 sample->actual.format = AUDIO_S16SYS; | 102 Timidity_Start(song); |
139 } /* if */ | |
140 else | |
141 memcpy(&sample->actual, &sample->desired, sizeof (Sound_AudioInfo)); | |
142 sample->flags = SOUND_SAMPLEFLAG_NONE; | |
143 | |
144 if (pipe(fd1) < 0 || pipe(fd2) < 0) | |
145 { | |
146 Sound_SetError("MIDI: Pipe error."); | |
147 return(0); | |
148 } /* if */ | |
149 | |
150 pid = fork(); | |
151 if (pid < 0) | |
152 { | |
153 Sound_SetError("MIDI: Fork error."); | |
154 return(0); | |
155 } /* if */ | |
156 | |
157 if (pid == 0) | |
158 { | |
159 /* child */ | |
160 char arg_freq[10]; /* !!! FIXME: Large enough? */ | |
161 char arg_format[10]; | |
162 char *arg_list[] = | |
163 { "timidity", "-s", NULL, NULL, "-o", "-", "-", NULL }; | |
164 | |
165 arg_list[2] = arg_freq; | |
166 arg_list[3] = arg_format; | |
167 | |
168 close(fd1[1]); | |
169 close(fd2[0]); | |
170 | |
171 /* !!! FIXME: Errors should be propagated back to the parent */ | |
172 | |
173 if (fd1[0] != STDIN_FILENO) | |
174 { | |
175 if (dup2(fd1[0], STDIN_FILENO) != STDIN_FILENO) | |
176 return(0); | |
177 close(fd1[0]); | |
178 } /* if */ | |
179 | |
180 if (fd2[1] != STDOUT_FILENO) | |
181 { | |
182 if (dup2(fd2[1], STDOUT_FILENO) != STDOUT_FILENO) | |
183 return(0); | |
184 close(fd2[1]); | |
185 } /* if */ | |
186 | |
187 /* Construct command-line arguments matching the audio format */ | |
188 sprintf(arg_freq, "%d", sample->actual.rate); | |
189 sprintf(arg_format, "-Or%c%c%cl", | |
190 (sample->actual.format & 0x0008) ? '8' : '1', | |
191 (sample->actual.format & 0x8000) ? 's' : 'u', | |
192 (sample->actual.channels == 1) ? 'M' : 'S'); | |
193 if ((sample->actual.format & 0x0010) && | |
194 (sample->actual.format & 0x7fff) != (AUDIO_S16SYS & 0x7fff)) | |
195 strcat(arg_format, "x"); | |
196 | |
197 if (execvp("timidity", arg_list) < 0) | |
198 return(0); | |
199 | |
200 SNDDBG(("MIDI: Is this line ever reached?")); | |
201 return(1); | |
202 } /* if */ | |
203 | |
204 /* parent */ | |
205 close(fd1[0]); | |
206 close(fd2[1]); | |
207 | |
208 /* Copy the entire file to the child process */ | |
209 for (;;) | |
210 { | |
211 retval = SDL_RWread(internal->rw, buf, 1, BUFSIZ); | |
212 if (retval == 0) | |
213 break; | |
214 | |
215 if (write(fd1[1], buf, retval) != retval) | |
216 { | |
217 Sound_SetError("MIDI: Could not send data to child process."); | |
218 return(0); | |
219 } | |
220 } /* for */ | |
221 | |
222 close(fd1[1]); | |
223 | |
224 /* !!! FIXME: We still need some way of recognizing valid MIDI data */ | |
225 | |
226 m = (midi_t *) malloc(sizeof(midi_t)); | |
227 BAIL_IF_MACRO(m == NULL, ERR_OUT_OF_MEMORY, 0); | |
228 internal->decoder_private = (void *) m; | |
229 | |
230 /* This is where we'll read the converted audio data. */ | |
231 m->fd_audio = fd2[0]; | |
232 | 103 |
233 SNDDBG(("MIDI: Accepting data stream.\n")); | 104 SNDDBG(("MIDI: Accepting data stream.\n")); |
105 | |
106 internal->decoder_private = (void *) song; | |
107 | |
108 sample->actual.channels = 2; | |
109 sample->actual.rate = 44100; | |
110 sample->actual.format = AUDIO_S16SYS; | |
111 | |
112 sample->flags = SOUND_SAMPLEFLAG_NONE; | |
234 return(1); /* we'll handle this data. */ | 113 return(1); /* we'll handle this data. */ |
235 } /* MIDI_open */ | 114 } /* MIDI_open */ |
236 | 115 |
237 | 116 |
238 static void MIDI_close(Sound_Sample *sample) | 117 static void MIDI_close(Sound_Sample *sample) |
239 { | 118 { |
240 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | 119 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; |
120 MidiSong *song = (MidiSong *) internal->decoder_private; | |
241 | 121 |
242 close(((midi_t *) internal->decoder_private)->fd_audio); | 122 Timidity_FreeSong(song); |
243 free(internal->decoder_private); | |
244 } /* MIDI_close */ | 123 } /* MIDI_close */ |
245 | 124 |
246 | 125 |
247 static Uint32 MIDI_read(Sound_Sample *sample) | 126 static Uint32 MIDI_read(Sound_Sample *sample) |
248 { | 127 { |
249 ssize_t retval; | 128 Uint32 retval; |
250 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | 129 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; |
251 midi_t *m = (midi_t *) internal->decoder_private; | 130 MidiSong *song = (MidiSong *) internal->decoder_private; |
252 | 131 |
253 /* | 132 retval = Timidity_PlaySome(song, internal->buffer, internal->buffer_size); |
254 * We don't actually do any decoding, so we read the converted data | |
255 * directly into the internal buffer. | |
256 */ | |
257 retval = read(m->fd_audio, internal->buffer, internal->buffer_size); | |
258 | 133 |
259 /* Make sure the read went smoothly... */ | 134 /* Make sure the read went smoothly... */ |
260 if (retval == 0) | 135 if (retval == 0) |
261 sample->flags |= SOUND_SAMPLEFLAG_EOF; | 136 sample->flags |= SOUND_SAMPLEFLAG_EOF; |
262 | 137 |
270 return(retval); | 145 return(retval); |
271 } /* MIDI_read */ | 146 } /* MIDI_read */ |
272 | 147 |
273 #endif /* SOUND_SUPPORTS_MIDI */ | 148 #endif /* SOUND_SUPPORTS_MIDI */ |
274 | 149 |
150 | |
275 /* end of midi.c ... */ | 151 /* end of midi.c ... */ |
276 | 152 |