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