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