make midimap obey channels
[galan.git] / plugins / libpcm_out.c
blob2b99fd4d6e24c41f614b30cf49b53e0d508d0ead
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>
22 #include <stddef.h>
24 #include <gdk/gdk.h>
25 #include <gtk/gtk.h>
26 #include <gmodule.h>
28 #include "global.h"
29 #include "generator.h"
30 #include "comp.h"
31 #include "control.h"
32 #include "gencomp.h"
33 #include "msgbox.h"
35 #include <unistd.h>
37 #ifndef HAVE_AUDIOFILE_H
38 #error Library 'audiofile' required to build pcm_out.c!
39 #endif
40 #include <audiofile.h>
42 #define GENERATOR_CLASS_NAME "pcm_out"
43 #define GENERATOR_CLASS_PATH "Outputs/WAV Recorder (PCM, Stereo)"
45 #define SIG_LEFT 0
46 #define SIG_RIGHT 1
48 #define EVT_RECORD 0
50 typedef struct Data {
51 char *filename;
52 AFfilehandle output;
53 AFfilesetup setup;
54 guint frames_recorded;
55 } Data;
57 typedef gint16 OUTPUTSAMPLE;
59 PRIVATE int output_fragment(AFfilehandle f, SAMPLE *l_buf, SAMPLE *r_buf, int length) {
60 OUTPUTSAMPLE *outbuf;
61 int buflen = length * sizeof(OUTPUTSAMPLE) * 2;
62 int i;
64 if (length <= 0)
65 return 0;
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);
75 return i;
78 PRIVATE void realtime_handler(Generator *g, AEvent *event) {
79 Data *data = g->data;
81 switch (event->kind) {
82 case AE_REALTIME: {
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);
98 if (written > 0)
99 data->frames_recorded += written;
102 break;
105 default:
106 g_warning("pcm_out module doesn't care for events of kind %d.", event->kind);
107 break;
111 PRIVATE int init_instance(Generator *g) {
112 Data *data = safe_malloc(sizeof(Data));
113 g->data = data;
115 data->filename = NULL;
116 data->output = 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);
126 return 1;
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);
142 free(g->data);
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));
149 FILE *f;
151 f = fopen(filename, "rb");
153 if (f != NULL) {
154 fclose(f);
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) {
158 return;
162 if (data->filename != NULL)
163 free(data->filename);
164 data->filename = safe_string_dup(filename);
166 #ifdef NATIVE_WIN32
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);
170 #else
171 data->output = afOpenFile(filename, "w", data->setup);
172 #endif
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.",
179 filename);
180 return;
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;
190 if (start) {
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);
203 gtk_widget_show(fs);
204 } else {
205 if (data->output != NULL) {
206 afCloseFile(data->output);
207 data->output = NULL;
210 popup_msgbox("Recording Complete", MSGBOX_OK, 0, MSGBOX_OK,
211 "Recorded %g seconds (%d frames) of data to file\n"
212 "%s",
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 },
221 { NULL, }
224 PRIVATE OutputSignalDescriptor output_sigs[] = {
225 { NULL, }
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) {
247 setup_class();