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
29 #include "generator.h"
37 #ifndef HAVE_AUDIOFILE_H
38 #error Library 'audiofile' required to build pcm_out.c!
40 #include <audiofile.h>
42 #define GENERATOR_CLASS_NAME "pcm_out"
43 #define GENERATOR_CLASS_PATH "Outputs/WAV Recorder (PCM, Stereo)"
54 guint frames_recorded
;
57 typedef gint16 OUTPUTSAMPLE
;
59 PRIVATE
int output_fragment(AFfilehandle f
, SAMPLE
*l_buf
, SAMPLE
*r_buf
, int length
) {
61 int buflen
= length
* sizeof(OUTPUTSAMPLE
) * 2;
67 outbuf
= g_alloca(buflen
);
69 for (i
= 0; i
< length
; i
++) {
70 outbuf
[i
<<1] = (OUTPUTSAMPLE
) MIN(MAX(l_buf
[i
] * 32767, -32768), 32767);
71 outbuf
[(i
<<1) + 1] = (OUTPUTSAMPLE
) MIN(MAX(r_buf
[i
] * 32767, -32768), 32767);
74 i
= afWriteFrames(f
, AF_DEFAULT_TRACK
, outbuf
, length
);
78 PRIVATE
void realtime_handler(Generator
*g
, AEvent
*event
) {
81 switch (event
->kind
) {
83 SAMPLE
*l_buf
, *r_buf
;
84 int bufbytes
= event
->d
.integer
* sizeof(SAMPLE
);
87 l_buf
= g_alloca(bufbytes
);
88 r_buf
= g_alloca(bufbytes
);
90 if (!gen_read_realtime_input(g
, SIG_LEFT
, -1, l_buf
, event
->d
.integer
))
91 memset(l_buf
, 0, bufbytes
);
93 if (!gen_read_realtime_input(g
, SIG_RIGHT
, -1, r_buf
, event
->d
.integer
))
94 memset(r_buf
, 0, bufbytes
);
96 if (data
->output
!= NULL
) {
97 int written
= output_fragment(data
->output
, l_buf
, r_buf
, event
->d
.integer
);
99 data
->frames_recorded
+= written
;
106 g_warning("pcm_out module doesn't care for events of kind %d.", event
->kind
);
111 PRIVATE
int init_instance(Generator
*g
) {
112 Data
*data
= safe_malloc(sizeof(Data
));
115 data
->filename
= NULL
;
117 data
->setup
= afNewFileSetup();
118 data
->frames_recorded
= 0;
120 afInitFileFormat(data
->setup
, AF_FILE_WAVE
);
121 afInitChannels(data
->setup
, AF_DEFAULT_TRACK
, 2);
122 afInitSampleFormat(data
->setup
, AF_DEFAULT_TRACK
, AF_SAMPFMT_TWOSCOMP
, 16);
124 gen_register_realtime_fn(g
, realtime_handler
);
129 PRIVATE
void destroy_instance(Generator
*g
) {
130 Data
*data
= g
->data
;
132 gen_deregister_realtime_fn(g
, realtime_handler
);
134 if (data
->filename
!= NULL
)
135 free(data
->filename
);
137 if (data
->output
!= NULL
)
138 afCloseFile(data
->output
);
140 afFreeFileSetup(data
->setup
);
145 PRIVATE
void access_output_file(GtkWidget
*widget
, GtkWidget
*fs
) {
146 Generator
*g
= gtk_object_get_data(GTK_OBJECT(fs
), "Generator");
147 Data
*data
= g
->data
;
148 const char *filename
= gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs
));
151 f
= fopen(filename
, "rb");
155 if (popup_msgbox("Confirm Overwrite", MSGBOX_YES
| MSGBOX_NO
, 0, MSGBOX_NO
,
156 "The file named %s exists.\nDo you want to overwrite it?",
157 filename
) != MSGBOX_YES
) {
162 if (data
->filename
!= NULL
)
163 free(data
->filename
);
164 data
->filename
= safe_string_dup(filename
);
167 /* Note that libaudiofile needs a small patch to get this to work right
168 on Win32. Details in README.w32. */
169 data
->output
= afOpenFile(filename
, "wb", data
->setup
);
171 data
->output
= afOpenFile(filename
, "w", data
->setup
);
173 data
->frames_recorded
= 0;
175 if (data
->output
== NULL
) {
176 popup_msgbox("Could Not Create File", MSGBOX_OK
, 0, MSGBOX_OK
,
177 "Could not create output file %s.\n"
178 "Recording cancelled.",
183 gtk_widget_destroy(fs
); /* %%% should this be gtk_widget_hide? uber-paranoia */
186 PRIVATE
void evt_record_handler(Generator
*g
, AEvent
*event
) {
187 gboolean start
= (event
->d
.number
> 0.5);
188 Data
*data
= g
->data
;
191 GtkWidget
*fs
= gtk_file_selection_new("Select Output WAV File");
193 gtk_object_set_data(GTK_OBJECT(fs
), "Generator", g
);
194 gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs
)->ok_button
), "clicked",
195 GTK_SIGNAL_FUNC(access_output_file
), fs
);
196 gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(fs
)->cancel_button
), "clicked",
197 GTK_SIGNAL_FUNC(gtk_widget_destroy
), GTK_OBJECT(fs
));
199 if (data
->filename
!= NULL
)
200 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs
), data
->filename
);
202 gtk_window_set_modal(GTK_WINDOW(fs
), TRUE
);
205 if (data
->output
!= NULL
) {
206 afCloseFile(data
->output
);
210 popup_msgbox("Recording Complete", MSGBOX_OK
, 0, MSGBOX_OK
,
211 "Recorded %g seconds (%d frames) of data to file\n"
213 (float) data
->frames_recorded
/ (SAMPLE_RATE
), data
->frames_recorded
,
214 data
->filename
? data
->filename
: "<anonymous>");
218 PRIVATE InputSignalDescriptor input_sigs
[] = {
219 { "Left", SIG_FLAG_REALTIME
},
220 { "Right", SIG_FLAG_REALTIME
},
224 PRIVATE OutputSignalDescriptor output_sigs
[] = {
228 PRIVATE ControlDescriptor controls
[] = {
229 /* { kind, name, min,max,step,page, size,editable, is_dst,queue_number,
230 init,destroy,refresh,refresh_data }, */
231 { CONTROL_KIND_TOGGLE
, "record", 0,0,0,0, 0,FALSE
, TRUE
,EVT_RECORD
, NULL
,NULL
,NULL
},
232 { CONTROL_KIND_NONE
, }
235 PRIVATE
void setup_class(void) {
236 GeneratorClass
*k
= gen_new_generatorclass(GENERATOR_CLASS_NAME
, FALSE
, 1, 0,
237 input_sigs
, output_sigs
, controls
,
238 init_instance
, destroy_instance
,
239 (AGenerator_pickle_t
) init_instance
, NULL
);
241 gen_configure_event_input(k
, EVT_RECORD
, "Record", evt_record_handler
);
243 gencomp_register_generatorclass(k
, FALSE
, GENERATOR_CLASS_PATH
, NULL
, NULL
);
246 PUBLIC
void init_plugin(void) {