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