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
23 #include <sys/types.h>
34 #include "generator.h"
40 #define SIG_LEFT_CHANNEL 0
41 #define SIG_RIGHT_CHANNEL 1
43 typedef signed short OUTPUTSAMPLE
;
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
) {
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
) {
94 format
.wFormatTag
= WAVE_FORMAT_PCM
;
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;
102 RETURN_VAL_UNLESS(waveOutOpen(phwo
, WAVE_MAPPER
, &format
,
103 (DWORD
) win_callback
, 0, CALLBACK_FUNCTION
)
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;
115 /* Two MS-reserved fields */
116 hdr
[i
].lpNext
= NULL
;
119 RETURN_VAL_UNLESS(waveOutPrepareHeader(*phwo
, &hdr
[i
], sizeof(hdr
[i
]))
127 PRIVATE gboolean
close_audiofd(HWAVEOUT hwo
) {
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
);
139 RETURN_VAL_UNLESS(waveOutClose(hwo
) == MMSYSERR_NOERROR
, FALSE
);
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();
152 PRIVATE
void clock_handler(AClock
*clock
, AClockReason reason
) {
153 Data
*data
= clock
->gen
->data
;
158 gtk_idle_remove(data
->idle_tag
);
159 close_audiofd(data
->hwo
);
166 data
->open
= open_audiofd(&data
->hwo
);
168 data
->idle_tag
= gtk_idle_add(idle_handler
, NULL
);
173 g_message("Unreachable code reached (win_output)... reason = %d", reason
);
178 PRIVATE
void realtime_handler(Generator
*g
, AEvent
*event
) {
179 Data
*data
= g
->data
;
181 switch (event
->kind
) {
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
);
196 audio_play_fragment(data
->hwo
, l_buf
, r_buf
, event
->d
.integer
);
204 g_warning("win_output module doesn't care for events of kind %d.", event
->kind
);
209 PRIVATE gboolean
init_instance(Generator
*g
) {
213 if (instance_count
> 1)
214 /* Not allowed more than one of these things. */
217 data
= safe_malloc(sizeof(Data
));
221 data
->clock
= gen_register_clock(g
, "Win32 Output Clock", clock_handler
);
225 gen_register_realtime_fn(g
, realtime_handler
);
226 gen_select_clock(data
->clock
); /* a not unreasonable assumption? */
231 PRIVATE
void destroy_instance(Generator
*g
) {
232 Data
*data
= g
->data
;
234 gen_deregister_realtime_fn(g
, realtime_handler
);
237 gen_deregister_clock(data
->clock
);
239 close_audiofd(data
->hwo
);
246 PRIVATE InputSignalDescriptor input_sigs
[] = {
247 { "Left Channel", SIG_FLAG_REALTIME
},
248 { "Right Channel", SIG_FLAG_REALTIME
},
252 PRIVATE
void setup_class(void) {
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
));
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"),
285 PUBLIC
void init_plugin(void) {