1 /*
2 SDL_mixer: An audio mixer library based on the SDL library
3 Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21
22 /* This file supports playing MIDI files with timidity */
23
24 #ifdef MUSIC_MID_TIMIDITY
25
26 #include "music_timidity.h"
27
28 #include "timidity/timidity.h"
29
30
31 typedef struct
32 {
33 int play_count;
34 MidiSong *song;
35 SDL_AudioStream *stream;
36 void *buffer;
37 Sint32 buffer_size;
38 } TIMIDITY_Music;
39
40
41 static int TIMIDITY_Seek(void *context, double position);
42 static void TIMIDITY_Delete(void *context);
43
TIMIDITY_Open(const SDL_AudioSpec * spec)44 static int TIMIDITY_Open(const SDL_AudioSpec *spec)
45 {
46 return Timidity_Init();
47 }
48
TIMIDITY_Close(void)49 static void TIMIDITY_Close(void)
50 {
51 Timidity_Exit();
52 }
53
TIMIDITY_CreateFromRW(SDL_RWops * src,int freesrc)54 void *TIMIDITY_CreateFromRW(SDL_RWops *src, int freesrc)
55 {
56 TIMIDITY_Music *music;
57 SDL_AudioSpec spec;
58 SDL_bool need_stream = SDL_FALSE;
59
60 music = (TIMIDITY_Music *)SDL_calloc(1, sizeof(*music));
61 if (!music) {
62 SDL_OutOfMemory();
63 return NULL;
64 }
65
66 SDL_memcpy(&spec, &music_spec, sizeof(spec));
67 if (spec.channels > 2) {
68 need_stream = SDL_TRUE;
69 spec.channels = 2;
70 }
71 music->song = Timidity_LoadSong(src, &spec);
72 if (!music->song) {
73 TIMIDITY_Delete(music);
74 return NULL;
75 }
76
77 if (need_stream) {
78 music->stream = SDL_NewAudioStream(spec.format, spec.channels, spec.freq,
79 music_spec.format, music_spec.channels, music_spec.freq);
80 if (!music->stream) {
81 TIMIDITY_Delete(music);
82 return NULL;
83 }
84
85 music->buffer_size = spec.samples * (SDL_AUDIO_BITSIZE(spec.format) / 8) * spec.channels;
86 music->buffer = SDL_malloc(music->buffer_size);
87 if (!music->buffer) {
88 SDL_OutOfMemory();
89 TIMIDITY_Delete(music);
90 return NULL;
91 }
92 }
93
94 if (freesrc) {
95 SDL_RWclose(src);
96 }
97 return music;
98 }
99
TIMIDITY_SetVolume(void * context,int volume)100 static void TIMIDITY_SetVolume(void *context, int volume)
101 {
102 TIMIDITY_Music *music = (TIMIDITY_Music *)context;
103 Timidity_SetVolume(music->song, volume);
104 }
105
TIMIDITY_Play(void * context,int play_count)106 static int TIMIDITY_Play(void *context, int play_count)
107 {
108 TIMIDITY_Music *music = (TIMIDITY_Music *)context;
109 music->play_count = play_count;
110 Timidity_Start(music->song);
111 return TIMIDITY_Seek(music, 0.0);
112 }
113
TIMIDITY_GetSome(void * context,void * data,int bytes,SDL_bool * done)114 static int TIMIDITY_GetSome(void *context, void *data, int bytes, SDL_bool *done)
115 {
116 TIMIDITY_Music *music = (TIMIDITY_Music *)context;
117 int filled, amount, expected;
118
119 if (music->stream) {
120 filled = SDL_AudioStreamGet(music->stream, data, bytes);
121 if (filled != 0) {
122 return filled;
123 }
124 }
125
126 if (!music->play_count) {
127 /* All done */
128 *done = SDL_TRUE;
129 return 0;
130 }
131
132 if (music->stream) {
133 expected = music->buffer_size;
134 amount = Timidity_PlaySome(music->song, music->buffer, music->buffer_size);
135 if (SDL_AudioStreamPut(music->stream, music->buffer, amount) < 0) {
136 return -1;
137 }
138 } else {
139 expected = bytes;
140 amount = Timidity_PlaySome(music->song, data, bytes);
141 }
142
143 if (amount < expected) {
144 if (music->play_count == 1) {
145 /* We didn't consume anything and we're done */
146 music->play_count = 0;
147 } else {
148 int play_count = -1;
149 if (music->play_count > 0) {
150 play_count = (music->play_count - 1);
151 }
152 if (TIMIDITY_Play(music, play_count) < 0) {
153 return -1;
154 }
155 }
156 }
157 if (music->stream) {
158 /* We'll pick it up from the stream next time around */
159 return 0;
160 } else {
161 /* We wrote output data */
162 return amount;
163 }
164 }
TIMIDITY_GetAudio(void * context,void * data,int bytes)165 static int TIMIDITY_GetAudio(void *context, void *data, int bytes)
166 {
167 return music_pcm_getaudio(context, data, bytes, MIX_MAX_VOLUME, TIMIDITY_GetSome);
168 }
169
TIMIDITY_Seek(void * context,double position)170 static int TIMIDITY_Seek(void *context, double position)
171 {
172 TIMIDITY_Music *music = (TIMIDITY_Music *)context;
173 Timidity_Seek(music->song, (Uint32)(position * 1000));
174 return 0;
175 }
176
TIMIDITY_Delete(void * context)177 static void TIMIDITY_Delete(void *context)
178 {
179 TIMIDITY_Music *music = (TIMIDITY_Music *)context;
180
181 if (music->song) {
182 Timidity_FreeSong(music->song);
183 }
184 if (music->stream) {
185 SDL_FreeAudioStream(music->stream);
186 }
187 if (music->buffer) {
188 SDL_free(music->buffer);
189 }
190 SDL_free(music);
191 }
192
193 Mix_MusicInterface Mix_MusicInterface_TIMIDITY =
194 {
195 "TIMIDITY",
196 MIX_MUSIC_TIMIDITY,
197 MUS_MID,
198 SDL_FALSE,
199 SDL_FALSE,
200
201 NULL, /* Load */
202 TIMIDITY_Open,
203 TIMIDITY_CreateFromRW,
204 NULL, /* CreateFromFile */
205 TIMIDITY_SetVolume,
206 TIMIDITY_Play,
207 NULL, /* IsPlaying */
208 TIMIDITY_GetAudio,
209 TIMIDITY_Seek,
210 NULL, /* Pause */
211 NULL, /* Resume */
212 NULL, /* Stop */
213 TIMIDITY_Delete,
214 TIMIDITY_Close,
215 NULL, /* Unload */
216 };
217
218 #endif /* MUSIC_MID_TIMIDITY */
219
220 /* vi: set ts=4 sw=4 expandtab: */
221