1 /* sdl_synth.c -- SDL music synthesizer
2 * $Id: sdl_synth.c,v 1.3 2005/06/29 03:20:34 kvance Exp $
3 * Copyright (C) 2001 Kev Vance <kvance@kvance.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place Suite 330; Boston, MA 02111-1307, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <math.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include "SDL.h"
28
29 #include "notes.h"
30 #include "sdl_synth.h"
31
32 Uint8 *masterplaybuffer = NULL;
33 static size_t playbuffersize = 0, playbufferloc = 0, playbuffermax = 0;
34
OpenSynth(SDL_AudioSpec * spec)35 int OpenSynth(SDL_AudioSpec * spec)
36 {
37 SDL_AudioSpec desired, obtained;
38
39 if (!SDL_WasInit(SDL_INIT_AUDIO)) {
40 /* If the audio subsystem isn't ready, initialize it */
41 if(SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
42 fprintf(stderr, "SDL Error: %s\n", SDL_GetError());
43 return 1;
44 }
45 }
46
47 /* Set desired sound opts */
48 desired.freq = 44100;
49 desired.format = AUDIO_U16SYS;
50 desired.channels = 1;
51 desired.samples = 4096;
52 desired.callback = AudioCallback;
53 desired.userdata = &obtained;
54
55 /* Open audio device */
56 if(SDL_OpenAudio(&desired, &obtained) < 0) {
57 fprintf(stderr, "SDL Error: %s\n", SDL_GetError());
58 return 1;
59 }
60 SDL_PauseAudio(0);
61
62 (*spec) = obtained;
63
64 return 0;
65 }
66
CloseSynth(void)67 void CloseSynth(void)
68 {
69 /* Silence, close the audio, and clean up the memory we used. */
70 SDL_PauseAudio(1);
71 SDL_CloseAudio();
72
73 AudioCleanUp();
74 }
75
IsSynthBufferEmpty()76 int IsSynthBufferEmpty()
77 {
78 return playbufferloc == playbuffermax;
79 }
80
SynthPlayNote(SDL_AudioSpec audiospec,musicalNote note,musicSettings settings)81 void SynthPlayNote(SDL_AudioSpec audiospec, musicalNote note, musicSettings settings)
82 {
83 /* Find the frequency and duration (wait) in seconds */
84 float frequency = noteFrequency(note, settings);
85 float wait = noteDuration(note, settings) / 1000;
86 float spacing = noteSpacing(note, settings) / 1000;
87
88 if (note.type == NOTETYPE_NOTE) {
89 /* Add the sound to the buffer */
90 AddToBuffer(audiospec, frequency, wait);
91 }
92
93 /* Rests are simple */
94 if (note.type == NOTETYPE_REST) {
95 AddToBuffer(audiospec, 0, wait);
96 }
97
98 /* Drums */
99 if (note.type == NOTETYPE_DRUM) {
100 /* Okay, these drums sound terrible, but they're better than nothing. */
101 int i;
102
103 /* Loop through each drum cycle */
104 for (i = 0; i < DRUMCYCLES; i++) {
105 AddToBuffer(audiospec, drums[note.index][i], ((float)DRUMBREAK) / 1000);
106 }
107
108 /* Add a break based on the current duration */
109 AddToBuffer(audiospec, 0, wait - ((float)DRUMBREAK) * DRUMCYCLES / 1000);
110 }
111
112 if (spacing != 0.0)
113 AddToBuffer(audiospec, 0, spacing);
114 }
115
AddToBuffer(SDL_AudioSpec spec,float freq,float seconds)116 void AddToBuffer(SDL_AudioSpec spec, float freq, float seconds)
117 {
118 size_t notesize = seconds * spec.freq; /* Bytes of sound */
119 size_t wordsize;
120 size_t i, j;
121
122 int osc = 1;
123
124 Uint16 uon = U16_1, uoff = U16_0;
125 Sint16 son = S16_1, soff = S16_0;
126
127 /* Don't let the callback function access the playbuffer while we're editing it! */
128 SDL_LockAudio();
129
130 if(spec.format == AUDIO_U8 || spec.format == AUDIO_S8)
131 wordsize = 1;
132 else
133 wordsize = 2;
134
135 if(playbuffersize != 0 && playbufferloc != 0) {
136 /* Shift buffer back to zero */
137 memcpy(masterplaybuffer,
138 &masterplaybuffer[playbufferloc],
139 playbuffersize-playbufferloc);
140 playbuffermax -= playbufferloc;
141 playbufferloc = 0;
142 }
143 if(playbuffersize == 0) {
144 /* Create buffer */
145 masterplaybuffer = malloc(notesize*wordsize);
146 playbuffersize = notesize*wordsize;
147 }
148 if((notesize*wordsize) > (playbuffersize-playbuffermax)) {
149 /* Make bigger buffer */
150 masterplaybuffer = realloc(masterplaybuffer,
151 playbuffersize+notesize*wordsize);
152
153 playbuffersize += notesize*wordsize;
154 }
155
156 if(freq == 0) {
157 /* Rest */
158 memset(&masterplaybuffer[playbuffermax],
159 spec.silence,
160 notesize*wordsize);
161 playbuffermax += notesize*wordsize;
162 } else {
163 /* Tone */
164 float hfreq = (spec.freq/freq/2.0);
165 for(i = 0, j = 0; i < notesize; i++, j++) {
166 if(j >= hfreq) {
167 osc ^= 1;
168 j = 0;
169 }
170 if(spec.format == AUDIO_U8) {
171 if(osc)
172 masterplaybuffer[playbuffermax] = U8_1;
173 else
174 masterplaybuffer[playbuffermax] = U8_0;
175 } else if(spec.format == AUDIO_S8) {
176 if(osc)
177 masterplaybuffer[playbuffermax] = S8_1;
178 else
179 masterplaybuffer[playbuffermax] = S8_0;
180 } else if(spec.format == AUDIO_U16) {
181 if(osc)
182 memcpy(&masterplaybuffer[playbuffermax], &uon, 2);
183 else
184 memcpy(&masterplaybuffer[playbuffermax], &uoff, 2);
185 } else if(spec.format == AUDIO_S16) {
186 if(osc)
187 memcpy(&masterplaybuffer[playbuffermax], &son, 2);
188 else
189 memcpy(&masterplaybuffer[playbuffermax], &soff, 2);
190 }
191 playbuffermax += wordsize;
192 }
193 }
194
195 /* Now let AudioCallback do its work */
196 SDL_UnlockAudio();
197 }
198
AudioCallback(SDL_AudioSpec * spec,Uint8 * stream,int len)199 void AudioCallback(SDL_AudioSpec *spec, Uint8 *stream, int len)
200 {
201 int i;
202 for(i = 0; i < len && playbufferloc < playbuffermax; i++) {
203 stream[i] = masterplaybuffer[playbufferloc];
204 playbufferloc++;
205 }
206 for(; i < len; i++)
207 stream[i] = ((SDL_AudioSpec *) spec)->silence;
208 }
209
AudioCleanUp()210 void AudioCleanUp()
211 {
212 if(playbuffersize != 0) {
213 free(masterplaybuffer);
214 masterplaybuffer = NULL;
215 playbuffersize = 0;
216 playbufferloc = 0;
217 playbuffermax = 0;
218 }
219 }
220