missing commit in generator.h
[galan.git] / plugins / libwin_output.c
blobfa4023b933052bbbde9c03724245ba2c4dff7ec0
1 /* gAlan - Graphical Audio Language
2 * Copyright (C) 1999 Tony Garnock-Jones
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include <stdlib.h>
20 #include <string.h>
21 #include <stdio.h>
23 #include <sys/types.h>
24 #include <fcntl.h>
25 #include <unistd.h>
27 #include <windows.h>
28 #include <mmsystem.h>
30 #include <gdk/gdk.h>
31 #include <gtk/gtk.h>
33 #include "global.h"
34 #include "generator.h"
35 #include "comp.h"
36 #include "control.h"
37 #include "gencomp.h"
38 #include "msgbox.h"
40 #define SIG_LEFT_CHANNEL 0
41 #define SIG_RIGHT_CHANNEL 1
43 typedef signed short OUTPUTSAMPLE;
45 typedef struct Data {
46 HWAVEOUT hwo;
47 gboolean open;
48 guint idle_tag;
49 AClock *clock;
50 } Data;
52 #define MIN_BLOCKS 8 /* Minimum usable number of WAVEHDRs */
53 #define DEFAULT_NUM_BLOCKS 16 /* Default number of WAVEHDRs used */
54 #define MAX_BLOCKS 32 /* Maximum number of available WAVEHDRs */
56 PRIVATE int num_blocks = DEFAULT_NUM_BLOCKS; /* Number of WAVEHDRs to actually use */
58 PRIVATE int instance_count = 0; /* Only one instance allowed. */
60 PRIVATE int next = 0; /* Index of next available WAVEHDR */
61 PRIVATE int pending = 0; /* Number of WAVEHDRs in system queue */
62 PRIVATE WAVEHDR hdr[MAX_BLOCKS]; /* WAVEHDRs == system audio buffers */
64 PRIVATE void audio_play_fragment(HWAVEOUT hwo, SAMPLE *left, SAMPLE *right, int length) {
65 OUTPUTSAMPLE *outbuf;
66 int i;
68 if (length <= 0)
69 return;
71 outbuf = (OUTPUTSAMPLE *) hdr[next].lpData; /* Copy data directly into WAVEHDR */
72 for (i = 0; i < length; i++) {
73 outbuf[i<<1] = (OUTPUTSAMPLE) MIN(MAX(left[i] * 32767, -32768), 32767);
74 outbuf[(i<<1) + 1] = (OUTPUTSAMPLE) MIN(MAX(right[i] * 32767, -32768), 32767);
77 hdr[next].dwBufferLength = length * sizeof(OUTPUTSAMPLE) * 2;
78 waveOutWrite(hwo, &hdr[next], sizeof(hdr[next]));
79 pending++; /* Mark it as 'in system queue' */
80 next = (next + 1) % num_blocks;
83 PRIVATE void CALLBACK win_callback(HWAVEOUT hwo, UINT uMsg,
84 DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) {
85 if (uMsg == WOM_DONE) {
86 pending--; /* Notify rest of app that it's time to calculate another buffer's worth */
90 PRIVATE gboolean open_audiofd(LPHWAVEOUT phwo) {
91 WAVEFORMATEX format;
92 int i;
94 format.wFormatTag = WAVE_FORMAT_PCM;
95 format.nChannels = 2;
96 format.nSamplesPerSec = SAMPLE_RATE;
97 format.nAvgBytesPerSec = SAMPLE_RATE * 2 * sizeof(OUTPUTSAMPLE);
98 format.nBlockAlign = sizeof(OUTPUTSAMPLE) * 2;
99 format.wBitsPerSample = sizeof(OUTPUTSAMPLE) * 8;
100 format.cbSize = 0;
102 RETURN_VAL_UNLESS(waveOutOpen(phwo, WAVE_MAPPER, &format,
103 (DWORD) win_callback, 0, CALLBACK_FUNCTION)
104 == MMSYSERR_NOERROR,
105 FALSE);
107 for (i = 0; i < num_blocks; i++) {
108 hdr[i].lpData = calloc(MAXIMUM_REALTIME_STEP * 2, sizeof(OUTPUTSAMPLE));
109 hdr[i].dwBufferLength = MAXIMUM_REALTIME_STEP * sizeof(OUTPUTSAMPLE) * 2;
110 hdr[i].dwBytesRecorded = 0;
111 hdr[i].dwUser = 0;
112 hdr[i].dwFlags = 0;
113 hdr[i].dwLoops = 0;
115 /* Two MS-reserved fields */
116 hdr[i].lpNext = NULL;
117 hdr[i].reserved = 0;
119 RETURN_VAL_UNLESS(waveOutPrepareHeader(*phwo, &hdr[i], sizeof(hdr[i]))
120 == MMSYSERR_NOERROR,
121 FALSE);
124 return TRUE;
127 PRIVATE gboolean close_audiofd(HWAVEOUT hwo) {
128 int i;
130 RETURN_VAL_UNLESS(waveOutReset(hwo) == MMSYSERR_NOERROR, FALSE);
132 for (i = 0; i < num_blocks; i++) {
133 LPSTR buf = hdr[i].lpData;
134 if (waveOutUnprepareHeader(hwo, &hdr[i], sizeof(hdr[i])) != MMSYSERR_NOERROR)
135 g_warning("waveOutUnprepareHeader of hdr[%d] failed", i);
136 free(buf);
139 RETURN_VAL_UNLESS(waveOutClose(hwo) == MMSYSERR_NOERROR, FALSE);
141 return TRUE;
144 /* This function acts as the master clock on Windows. */
145 PRIVATE gint idle_handler(gpointer data) {
146 while (pending < num_blocks)
147 gen_clock_mainloop();
149 return TRUE;
152 PRIVATE void clock_handler(AClock *clock, AClockReason reason) {
153 Data *data = clock->gen->data;
155 switch (reason) {
156 case CLOCK_DISABLE:
157 if (data->open) {
158 gtk_idle_remove(data->idle_tag);
159 close_audiofd(data->hwo);
160 data->open = FALSE;
162 break;
164 case CLOCK_ENABLE:
165 if (!data->open) {
166 data->open = open_audiofd(&data->hwo);
167 if (data->open)
168 data->idle_tag = gtk_idle_add(idle_handler, NULL);
170 break;
172 default:
173 g_message("Unreachable code reached (win_output)... reason = %d", reason);
174 break;
178 PRIVATE void realtime_handler(Generator *g, AEvent *event) {
179 Data *data = g->data;
181 switch (event->kind) {
182 case AE_REALTIME: {
183 SAMPLE *l_buf, *r_buf;
184 int bufbytes = event->d.integer * sizeof(SAMPLE);
186 l_buf = safe_malloc(bufbytes);
187 r_buf = safe_malloc(bufbytes);
189 if (!gen_read_realtime_input(g, SIG_LEFT_CHANNEL, -1, l_buf, event->d.integer))
190 memset(l_buf, 0, bufbytes);
192 if (!gen_read_realtime_input(g, SIG_RIGHT_CHANNEL, -1, r_buf, event->d.integer))
193 memset(r_buf, 0, bufbytes);
195 if (data->open)
196 audio_play_fragment(data->hwo, l_buf, r_buf, event->d.integer);
197 free(l_buf);
198 free(r_buf);
200 break;
203 default:
204 g_warning("win_output module doesn't care for events of kind %d.", event->kind);
205 break;
209 PRIVATE gboolean init_instance(Generator *g) {
210 Data *data;
212 instance_count++;
213 if (instance_count > 1)
214 /* Not allowed more than one of these things. */
215 return 0;
217 data = safe_malloc(sizeof(Data));
219 data->hwo = 0;
220 data->open = FALSE;
221 data->clock = gen_register_clock(g, "Win32 Output Clock", clock_handler);
223 g->data = data;
225 gen_register_realtime_fn(g, realtime_handler);
226 gen_select_clock(data->clock); /* a not unreasonable assumption? */
228 return TRUE;
231 PRIVATE void destroy_instance(Generator *g) {
232 Data *data = g->data;
234 gen_deregister_realtime_fn(g, realtime_handler);
236 if (data != NULL) {
237 gen_deregister_clock(data->clock);
238 if (data->open)
239 close_audiofd(data->hwo);
240 free(data);
243 instance_count--;
246 PRIVATE InputSignalDescriptor input_sigs[] = {
247 { "Left Channel", SIG_FLAG_REALTIME },
248 { "Right Channel", SIG_FLAG_REALTIME },
249 { NULL, }
252 PRIVATE void setup_class(void) {
253 GeneratorClass *k;
256 char *value = prefs_get_item("output_win_num_blocks");
258 if (value == NULL || sscanf(value, "%d", &num_blocks) != 1) {
259 num_blocks = DEFAULT_NUM_BLOCKS;
262 num_blocks = MAX(MIN_BLOCKS, MIN(MAX_BLOCKS, num_blocks));
266 int i;
267 char tmp[20];
269 for (i = MIN_BLOCKS; i <= MAX_BLOCKS; i++) {
270 sprintf(tmp, "%d", i);
271 prefs_register_option("output_win_num_blocks", tmp);
275 k = gen_new_generatorclass("audio_out", FALSE, 0, 0,
276 input_sigs, NULL, NULL,
277 init_instance, destroy_instance,
278 (AGenerator_pickle_t) init_instance, NULL);
280 gencomp_register_generatorclass(k, FALSE, "Outputs/Win32 Output",
281 PIXMAPDIRIFY("oss_output.xpm"),
282 NULL);
285 PUBLIC void init_plugin(void) {
286 setup_class();