~ubuntu-branches/ubuntu/utopic/glame/utopic

« back to all changes in this revision

Viewing changes to src/plugins/file_io.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Kobras
  • Date: 2002-04-09 17:14:12 UTC
  • Revision ID: james.westby@ubuntu.com-20020409171412-jzpnov7mbz2w6zsr
Tags: upstream-0.6.2
ImportĀ upstreamĀ versionĀ 0.6.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * file_io.c
 
3
 * $Id: file_io.c,v 1.75 2001/12/07 22:28:01 mag Exp $
 
4
 *
 
5
 * Copyright (C) 1999, 2000 Alexander Ehlert, Richard Guenther, Daniel Kobras
 
6
 *
 
7
 * This program is free software; you can redistribute it and/or modify
 
8
 * it under the terms of the GNU General Public License as published by
 
9
 * the Free Software Foundation; either version 2 of the License, or
 
10
 * (at your option) any later version.
 
11
 *
 
12
 * This program is distributed in the hope that it will be useful,
 
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
 * GNU General Public License for more details.
 
16
 *
 
17
 * You should have received a copy of the GNU General Public License
 
18
 * along with this program; if not, write to the Free Software
 
19
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
20
 *
 
21
 *
 
22
 * Generic audiofile reader filter. Every generic reader should honour
 
23
 * the per-pipe parameter "position" by just selecting the "best matching"
 
24
 * channel of the file for the pipe. Remixing is not required, neither is
 
25
 * duplicate channel output. The real position of the stream should be set
 
26
 * exact though.
 
27
 * Every generic reader should have a
 
28
 * - prepare method which does audiofile header reading and checking if
 
29
 *   it can handle the file. Fixup of the output pipes type is required, too.
 
30
 *   prepare is a unification of the connect_out & fixup_param method.
 
31
 * - f method which does the actual reading
 
32
 * - cleanup method to cleanup the private shared state
 
33
 * Every generic writer should just have a
 
34
 * - f method which does all setup and the actual writing, just terminate
 
35
 *   with an error if something is wrong
 
36
 * A writer should register itself with a regular expression of filenames
 
37
 * it wants to handle.
 
38
 */
 
39
 
 
40
#ifdef HAVE_CONFIG_H
 
41
#include <config.h>
 
42
#endif
 
43
 
 
44
#include <sys/stat.h>
 
45
#include <sys/mman.h>
 
46
#include <sys/time.h>
 
47
#include <sys/types.h>
 
48
#include <signal.h>
 
49
#include <regex.h>
 
50
#include <unistd.h>
 
51
#include <stdlib.h>
 
52
#include <fcntl.h> 
 
53
#include <string.h>
 
54
#include <math.h>
 
55
#include "filter.h"
 
56
#include "util.h"
 
57
#include "glplugin.h"
 
58
#include "glame_types.h"
 
59
#include "glame_byteorder.h"
 
60
#include "glame_audiofile.h"
 
61
 
 
62
 
 
63
int af_read_prepare(filter_t *n, const char *filename);
 
64
int af_read_connect(filter_t *n, filter_pipe_t *p);
 
65
int af_read_f(filter_t *n);
 
66
void af_read_cleanup(filter_t *n);
 
67
int af_write_f(filter_t *n);
 
68
 
 
69
PLUGIN_SET(file_io, "read_file write_file")
 
70
 
 
71
 
 
72
typedef struct {
 
73
        struct glame_list_head list;
 
74
        int (*prepare)(filter_t *, const char *);
 
75
        int (*connect)(filter_t *, filter_pipe_t *);
 
76
        int (*f)(filter_t *);
 
77
        void (*cleanup)(filter_t *);
 
78
        const char *regexp;
 
79
} rw_t;
 
80
 
 
81
typedef struct {
 
82
        filter_pipe_t   *p;
 
83
        filter_buffer_t *buf;
 
84
        int             pos;
 
85
        int             mapped;
 
86
} track_t;
 
87
 
 
88
typedef struct {
 
89
        rw_t *rw;
 
90
        int initted;
 
91
        union {
 
92
                /* put your shared state stuff here */
 
93
                struct {
 
94
                        AFfilehandle    file;
 
95
                        AFframecount    frameCount;
 
96
                        AFfilesetup     fsetup;
 
97
                        int             sampleFormat,sampleWidth;
 
98
                        int             channelCount,frameSize;
 
99
                        int             sampleRate;
 
100
                        int             format;
 
101
                        track_t         *track;
 
102
                } audiofile;
 
103
        } u;
 
104
} rw_private_t;
 
105
#define RWPRIV(node) ((rw_private_t *)((node)->priv))
 
106
#define RWA(node) (RWPRIV(node)->u.audiofile)
 
107
#define RWM(node) (RWPRIV(node)->u.lame)
 
108
 
 
109
/* the readers & the writers list */
 
110
static struct glame_list_head readers;
 
111
 
 
112
static rw_t *add_rw(int (*prepare)(filter_t *, const char *),
 
113
                    int (*connect)(filter_t *, filter_pipe_t *),
 
114
                    int (*f)(filter_t *),
 
115
                    void (*cleanup)(filter_t *),
 
116
                    const char *regexp)
 
117
{
 
118
        rw_t *rw;
 
119
 
 
120
        if (!prepare && !f)
 
121
                return NULL;
 
122
        if (!(rw = ALLOC(rw_t)))
 
123
                return NULL;
 
124
        GLAME_INIT_LIST_HEAD(&rw->list);
 
125
        rw->prepare = prepare;
 
126
        rw->connect = connect;
 
127
        rw->f = f;
 
128
        rw->cleanup = cleanup;
 
129
        if (regexp)
 
130
                rw->regexp = strdup(regexp);
 
131
 
 
132
        return rw;
 
133
}
 
134
static int add_reader(int (*prepare)(filter_t *, const char *),
 
135
                      int (*connect)(filter_t *, filter_pipe_t *),
 
136
                      int (*f)(filter_t *),
 
137
                      void (*cleanup)(filter_t *))
 
138
{
 
139
        rw_t *rw;
 
140
 
 
141
        if (!(rw = add_rw(prepare, connect, f, cleanup, NULL)))
 
142
                return -1;
 
143
        glame_list_add(&rw->list, &readers);
 
144
        return 0;
 
145
}
 
146
 
 
147
/* generic read&write methods */
 
148
static void rw_file_cleanup(glsig_handler_t *h, long sig, va_list va)
 
149
{
 
150
        filter_t *n;
 
151
 
 
152
        GLSIGH_GETARGS1(va, n);
 
153
        if (!RWPRIV(n)) {
 
154
                DPRINTF("ficken\n");
 
155
                return;
 
156
        }
 
157
        if (RWPRIV(n)->rw
 
158
            && RWPRIV(n)->rw->cleanup 
 
159
            && RWPRIV(n)->initted)
 
160
                RWPRIV(n)->rw->cleanup(n);
 
161
        free(RWPRIV(n));
 
162
        n->priv = NULL;
 
163
}
 
164
static int rw_file_init(filter_t *n)
 
165
{
 
166
        rw_private_t *p;
 
167
 
 
168
        if (!(p = ALLOC(rw_private_t)))
 
169
                return -1;
 
170
        n->priv = p;
 
171
        glsig_add_handler(&n->emitter, GLSIG_FILTER_DELETED,
 
172
                          rw_file_cleanup, NULL);
 
173
 
 
174
        return 0;
 
175
}
 
176
 
 
177
/* read methods */
 
178
static int read_file_f(filter_t *n)
 
179
{
 
180
        /* require set filename (a selected reader) and
 
181
         * at least one connected output. */
 
182
        if (!RWPRIV(n)->initted)
 
183
                FILTER_ERROR_RETURN("invalid file");
 
184
        if (!filterport_get_pipe(filterportdb_get_port(filter_portdb(n), PORTNAME_OUT)))
 
185
                FILTER_ERROR_RETURN("no outputs");
 
186
        return RWPRIV(n)->rw->f(n);
 
187
}
 
188
static int read_file_connect_out(filter_port_t *port, filter_pipe_t *p)
 
189
{
 
190
        filter_t *n = filterport_filter(port);
 
191
 
 
192
        /* no reader -> no filename -> some "defaults".
 
193
         * only allow 2 connections. */
 
194
        if (!RWPRIV(n)->rw) {
 
195
                if (filterport_nrpipes(port) > 1)
 
196
                        return -1;
 
197
                filterpipe_settype_sample(p, GLAME_DEFAULT_SAMPLERATE,
 
198
                                          FILTER_PIPEPOS_DEFAULT);
 
199
                return 0;
 
200
        }
 
201
 
 
202
        /* pass request to readers prepare, it can reject the
 
203
         * connection, but not the file here. */
 
204
        return RWPRIV(n)->rw->connect(n, p);
 
205
}
 
206
 
 
207
static void read_file_fixup_pipe(glsig_handler_t *h, long sig, va_list va) {
 
208
        filter_t        *n;
 
209
        filter_pipe_t   *pipe;
 
210
        
 
211
        GLSIGH_GETARGS1(va, pipe);
 
212
        n = filterport_filter(filterpipe_source(pipe));
 
213
        if (RWPRIV(n) && RWPRIV(n)->rw) 
 
214
                RWPRIV(n)->rw->connect(n, pipe);
 
215
}
 
216
 
 
217
static int read_file_setup_param(filter_param_t *param, const void *val) 
 
218
{
 
219
        filter_t *n = filterparam_filter(param);
 
220
        filter_pipe_t *p;
 
221
        filter_port_t *port;
 
222
        rw_t *r;
 
223
        char *filename;
 
224
 
 
225
        /* only position param change? - we only care for initted rw. */
 
226
        if (strcmp("position", filterparam_label(param)) == 0) {
 
227
                p = filterparam_get_sourcepipe(param);
 
228
                if (RWPRIV(n)->rw && RWPRIV(n)->rw->connect(n, p) == -1)
 
229
                        PANIC("Uh? Reject pipe that previously was ok?");
 
230
                glsig_emit(&p->emitter, GLSIG_PIPE_CHANGED, p);
 
231
                return 0;
 
232
        
 
233
        /* filename change! */
 
234
        } else {
 
235
                filename = *((char**)val);
 
236
                if (!filename)
 
237
                        return -1;
 
238
 
 
239
                DPRINTF("filename change to %s\n", filename);
 
240
                /* check actual reader */
 
241
                if (RWPRIV(n)->rw) {
 
242
                        /* cleanup previous stuff */
 
243
                        if (RWPRIV(n)->initted
 
244
                            && RWPRIV(n)->rw->cleanup)
 
245
                                RWPRIV(n)->rw->cleanup(n);
 
246
                        RWPRIV(n)->initted = 0;
 
247
                        /* deleted reuse here, because libmp3lame
 
248
                         * detects wav as mp3 on change */
 
249
                }
 
250
 
 
251
                RWPRIV(n)->rw = NULL;
 
252
                RWPRIV(n)->initted = 0;
 
253
 
 
254
                /* search for applicable reader */
 
255
                glame_list_foreach(&readers, rw_t, list, r) {
 
256
                        if (r->prepare(n, filename) != -1) {
 
257
                                RWPRIV(n)->rw = r;
 
258
                                RWPRIV(n)->initted = 1;
 
259
                                goto reconnect;
 
260
                        }
 
261
                }
 
262
 
 
263
                /* no reader found */
 
264
                return -1;
 
265
        }
 
266
 
 
267
 reconnect:
 
268
        /* re-connect all pipes */
 
269
        filterportdb_foreach_port(filter_portdb(n), port) {
 
270
                if (filterport_is_input(port))
 
271
                        continue;
 
272
                filterport_foreach_pipe(port, p) {
 
273
                        if (RWPRIV(n)->rw->connect(n, p) == -1) {
 
274
                                filterpipe_delete(p);
 
275
                                goto reconnect;
 
276
                        }
 
277
                        glsig_emit(&p->emitter, GLSIG_PIPE_CHANGED, p);
 
278
                }
 
279
        }
 
280
        return 0;
 
281
}
 
282
 
 
283
 
 
284
static int write_file_connect_in(filter_port_t *port, filter_pipe_t *p)
 
285
{
 
286
        /* So why is there no write_file_connect_in?? Do we really
 
287
         * support any number of inputs? Seems all is _f() time... */
 
288
        return 0;
 
289
}
 
290
 
 
291
int read_file_register(plugin_t *pl)
 
292
{
 
293
        filter_t *f;
 
294
        filter_port_t *p;
 
295
        filter_param_t *param;
 
296
 
 
297
        if (!(f = filter_creat(NULL)))
 
298
                return -1;
 
299
 
 
300
        p = filterportdb_add_port(filter_portdb(f), PORTNAME_OUT,
 
301
                                  FILTER_PORTTYPE_SAMPLE,
 
302
                                  FILTER_PORTFLAG_OUTPUT,
 
303
                                  FILTERPORT_DESCRIPTION, "audio stream",
 
304
                                  FILTERPORT_END);
 
305
        p->connect = read_file_connect_out;
 
306
        param = filterparamdb_add_param_float(filterport_paramdb(p), "position", 
 
307
                                  FILTER_PARAMTYPE_POSITION, FILTER_PIPEPOS_DEFAULT,
 
308
                                  FILTERPARAM_END);
 
309
        param->set = read_file_setup_param;
 
310
        param = filterparamdb_add_param_string(filter_paramdb(f), "filename",
 
311
                                   FILTER_PARAMTYPE_FILENAME, NULL,
 
312
                                   FILTERPARAM_END);
 
313
        param->set = read_file_setup_param;
 
314
        filterparam_set_property(param,FILTER_PARAM_PROPERTY_FILE_FILTER,"*.wav");
 
315
        filterparamdb_add_param_pos(filter_paramdb(f));
 
316
 
 
317
        f->f = read_file_f;
 
318
        f->init = rw_file_init;
 
319
 
 
320
        glsig_add_handler(&f->emitter, GLSIG_PIPE_DELETED,
 
321
                          read_file_fixup_pipe, NULL);
 
322
 
 
323
        plugin_set(pl, PLUGIN_DESCRIPTION, "read a file");
 
324
        plugin_set(pl, PLUGIN_PIXMAP, "input.png");
 
325
        plugin_set(pl, PLUGIN_CATEGORY, "Input");
 
326
        plugin_set(pl, PLUGIN_GUI_HELP_PATH, "File_I_O");
 
327
        
 
328
        filter_register(f, pl);
 
329
 
 
330
        return 0;
 
331
}
 
332
 
 
333
 
 
334
 
 
335
int file_io_register(plugin_t *p)
 
336
{
 
337
        GLAME_INIT_LIST_HEAD(&readers);
 
338
 
 
339
        add_reader(af_read_prepare, af_read_connect,
 
340
                   af_read_f, af_read_cleanup);
 
341
 
 
342
        return 0;
 
343
}
 
344
 
 
345
 
 
346
 
 
347
/* The actual readers and writers.
 
348
 */
 
349
 
 
350
int af_read_prepare(filter_t *n, const char *filename)
 
351
{
 
352
        filter_param_t *fparam;
 
353
        char info[255];
 
354
        int ftype, version;
 
355
 
 
356
        fparam = filterparamdb_get_param(filter_paramdb(n), "filename");
 
357
 
 
358
        DPRINTF("Using audiofile library\n");
 
359
        if ((RWA(n).file=afOpenFile(filename,"r",NULL))==NULL){ 
 
360
                DPRINTF("File not found!\n"); 
 
361
                return -1; 
 
362
        }
 
363
 
 
364
 
 
365
        RWA(n).frameCount=afGetFrameCount(RWA(n).file, AF_DEFAULT_TRACK);
 
366
        sprintf(info, "%d", (int)RWA(n).frameCount); 
 
367
        filterparam_set_property(fparam,"#framecount", info);
 
368
 
 
369
        ftype = afGetFileFormat(RWA(n).file, &version);
 
370
        DPRINTF("ftype = %d, version = %d\n", ftype, version);
 
371
        sprintf(info, "%d %d", ftype, version);
 
372
        filterparam_set_property(fparam,"#format", info);
 
373
 
 
374
        RWA(n).channelCount = afGetChannels(RWA(n).file, AF_DEFAULT_TRACK);
 
375
        sprintf(info, "%d", RWA(n).channelCount);
 
376
        filterparam_set_property(fparam,"#channels", info);
 
377
 
 
378
        afGetSampleFormat(RWA(n).file, AF_DEFAULT_TRACK, &(RWA(n).sampleFormat), &(RWA(n).sampleWidth));
 
379
        sprintf(info, "%d bit", RWA(n).sampleWidth);
 
380
        filterparam_set_property(fparam,"#quality", info);
 
381
 
 
382
        RWA(n).frameSize = (int) afGetVirtualFrameSize(RWA(n).file,
 
383
                                                 AF_DEFAULT_TRACK, 1);
 
384
        sprintf(info, "%d", RWA(n).frameSize);
 
385
        filterparam_set_property(fparam,"#framesize", info);
 
386
        
 
387
        RWA(n).sampleRate = (int)afGetRate(RWA(n).file, AF_DEFAULT_TRACK);
 
388
        sprintf(info, "%d Hz", RWA(n).sampleRate);
 
389
        filterparam_set_property(fparam,"#samplerate", info);
 
390
                
 
391
        if (!(RWA(n).track=ALLOCN(RWA(n).channelCount,track_t))){
 
392
                DPRINTF("Couldn't allocate track buffer\n");
 
393
                return -1;
 
394
        }
 
395
 
 
396
        DPRINTF("File %s: %d channel(s) %d bit %s at %d Hz, "
 
397
                "framecount %d, framesize %d.\n",
 
398
                        filename,
 
399
                        RWA(n).channelCount, RWA(n).sampleWidth, 
 
400
                        RWA(n).sampleFormat == AF_SAMPFMT_TWOSCOMP ?
 
401
                        "signed" : 
 
402
                        RWA(n).sampleFormat == AF_SAMPFMT_UNSIGNED ? 
 
403
                        "unsigned" : "unknown",
 
404
                        RWA(n).sampleRate, (int)RWA(n).frameCount, 
 
405
                        RWA(n).frameSize);
 
406
        return 0;
 
407
}
 
408
 
 
409
int af_read_connect(filter_t *n, filter_pipe_t *p)
 
410
{
 
411
        int i, deleted=1;
 
412
        filter_port_t   *outp;
 
413
        filter_pipe_t   *out;
 
414
        
 
415
        outp = filterportdb_get_port(filter_portdb(n), PORTNAME_OUT);
 
416
        filterport_foreach_pipe(outp, out) {
 
417
                if (p == out)
 
418
                        deleted = 0;
 
419
        }
 
420
        
 
421
        if (deleted == 1) {
 
422
                for(i=0; i<RWA(n).channelCount; i++)
 
423
                        if (RWA(n).track[i].p == p) {
 
424
                                DPRINTF("unmapped channel %d\n", i);
 
425
                                RWA(n).track[i].mapped = 0;
 
426
                                RWA(n).track[i].p = NULL;
 
427
                                return 0;
 
428
                        }
 
429
        }
 
430
        
 
431
        for(i=0;(i<RWA(n).channelCount) && (RWA(n).track[i].mapped);i++);
 
432
        if (i>=RWA(n).channelCount){
 
433
                /* Check if track is already mapped ?!
 
434
                 * Would be strange, but ... 
 
435
                 * - nope, not strange, connect gets called for each
 
436
                 *   already connected pipes at parameter change, too!
 
437
                 *   (as for just pipe parameter change)
 
438
                 * - you should fixup, i.e. re-route perhaps, reject, whatever
 
439
                 *   in this case
 
440
                 */
 
441
                for(i=0;i<RWA(n).channelCount;i++)
 
442
                        if ((RWA(n).track[i].mapped) && RWA(n).track[i].p==p){
 
443
                                return 0; 
 
444
                        }
 
445
                return -1;
 
446
        } else {
 
447
                /* Moah! what is this? does libaudiofile not provide
 
448
                 * some "direct" information on position??
 
449
                 */
 
450
                if (RWA(n).channelCount!=1)
 
451
                        filterpipe_settype_sample(p,RWA(n).sampleRate,
 
452
                                (M_PI/(RWA(n).channelCount-1))*i+FILTER_PIPEPOS_LEFT);
 
453
                else
 
454
                        filterpipe_settype_sample(p,RWA(n).sampleRate,FILTER_PIPEPOS_CENTRE);
 
455
                RWA(n).track[i].p=p;
 
456
                RWA(n).track[i].mapped=1;
 
457
        }       
 
458
        return 0;       
 
459
}
 
460
 
 
461
int af_read_f(filter_t *n)
 
462
{
 
463
        int frames,i,j;
 
464
        filter_pipe_t *p_out;
 
465
        filter_port_t *port;
 
466
        SAMPLE *s0, *s1;
 
467
        int fcnt, cnt;
 
468
        long pos;
 
469
        filter_param_t *pos_param;
 
470
        SAMPLE          *buffer;
 
471
 
 
472
        /* seek to start of audiofile */
 
473
        afSeekFrame(RWA(n).file, AF_DEFAULT_TRACK, 0);
 
474
        fcnt = RWA(n).frameCount;
 
475
        
 
476
        if (afSetVirtualSampleFormat(RWA(n).file, AF_DEFAULT_TRACK, AF_SAMPFMT_FLOAT, 32)==-1)
 
477
                FILTER_ERROR_RETURN("virtual method failed, get newer libaudiofile!");
 
478
                
 
479
        if (afSetVirtualPCMMapping(RWA(n).file, AF_DEFAULT_TRACK, 1.0, 0.0, -1.0, 1.0)==-1)
 
480
                FILTER_ERROR_RETURN("virtual method failed, get newer libaudiofile!");
 
481
 
 
482
        buffer=ALLOCN(GLAME_WBUFSIZE*RWA(n).channelCount, SAMPLE);
 
483
 
 
484
        FILTER_AFTER_INIT;
 
485
        pos_param = filterparamdb_get_param(filter_paramdb(n), FILTERPARAM_LABEL_POS);
 
486
        filterparam_val_set_pos(pos_param, 0);
 
487
        pos = 0;
 
488
 
 
489
        while(fcnt){
 
490
                FILTER_CHECK_STOP;
 
491
                if (!(frames=afReadFrames(RWA(n).file, AF_DEFAULT_TRACK, 
 
492
                                          buffer,
 
493
                                          MIN(GLAME_WBUFSIZE, fcnt))))
 
494
                        break;
 
495
                pos += frames;
 
496
                filterparam_val_set_pos(pos_param, pos);
 
497
                fcnt-=frames;
 
498
                for (i=0; i < RWA(n).channelCount; i++){
 
499
                        RWA(n).track[i].buf =
 
500
                                sbuf_make_private(sbuf_alloc(frames,n));
 
501
                        RWA(n).track[i].pos = 0;
 
502
                }
 
503
                i=0;
 
504
                while (i < frames*RWA(n).channelCount)
 
505
                        for (j=0; j < RWA(n).channelCount; j++)
 
506
                                sbuf_buf(RWA(n).track[j].buf)[RWA(n).track[j].pos++] =
 
507
                                        buffer[i++] ;
 
508
 
 
509
                for (i=0; i < RWA(n).channelCount; i++)
 
510
                        sbuf_queue(RWA(n).track[i].p, RWA(n).track[i].buf);
 
511
        }
 
512
 
 
513
        filterportdb_foreach_port(filter_portdb(n), port) {
 
514
                if (filterport_is_input(port))
 
515
                        continue;
 
516
                filterport_foreach_pipe(port, p_out)
 
517
                        sbuf_queue(p_out, NULL);
 
518
        }
 
519
 
 
520
        FILTER_BEFORE_STOPCLEANUP;
 
521
        FILTER_BEFORE_CLEANUP;
 
522
 
 
523
        if(buffer!=NULL)
 
524
                free(buffer);
 
525
 
 
526
        return 0;
 
527
}
 
528
 
 
529
void af_read_cleanup(filter_t *n)
 
530
{
 
531
        free(RWA(n).track);
 
532
        afCloseFile(RWA(n).file);
 
533
        memset(&(RWPRIV(n)->u), 0, sizeof(RWPRIV(n)->u));
 
534
}
 
535
 
 
536
 
 
537
static int af_typecnt, *af_indices;
 
538
 
 
539
int write_file_f(filter_t *n)
 
540
{
 
541
        filter_pipe_t *in;
 
542
        filter_port_t *port;
 
543
        char *filename;
 
544
        int res=-1, failed=0;
 
545
        int eofs,wbpos;
 
546
        int i,iat,iass;
 
547
        int filetype;
 
548
        char *errstring = "write file failed";
 
549
        long pos;
 
550
        filter_param_t *pos_param;
 
551
 
 
552
        /* audiofile stuff */
 
553
        AFfilehandle    file;
 
554
        AFfilesetup     fsetup;
 
555
        int             sampleFormat,sampleWidth;
 
556
        int             channelCount, compression;
 
557
        int             sampleRate;
 
558
        track_t         *track;
 
559
        SAMPLE          *buffer;
 
560
        int             buffer_size, written, frames;
 
561
        
 
562
 
 
563
        channelCount = filterport_nrpipes(filterportdb_get_port(filter_portdb(n), PORTNAME_IN));
 
564
 
 
565
        filename = filterparam_val_string(filterparamdb_get_param(filter_paramdb(n), "filename"));
 
566
 
 
567
        if (!filename)
 
568
                FILTER_ERROR_RETURN("no filename");
 
569
 
 
570
 
 
571
        filetype = filterparam_val_int(filterparamdb_get_param(filter_paramdb(n), "filetype"));
 
572
        if (filetype==0 )
 
573
                filetype = glame_get_filetype_by_name(filename);
 
574
        else {
 
575
                filetype--;
 
576
                if (filetype>=af_typecnt)
 
577
                        filetype=-1;
 
578
                else
 
579
                        filetype=af_indices[filetype];
 
580
        }
 
581
 
 
582
        sampleFormat=filterparam_val_int(filterparamdb_get_param(filter_paramdb(n), "sampleformat"));
 
583
        sampleWidth=filterparam_val_int(filterparamdb_get_param(filter_paramdb(n), "samplewidth"));
 
584
        compression=filterparam_val_int(filterparamdb_get_param(filter_paramdb(n), "compression"));
 
585
 
 
586
        if (filetype==-1)
 
587
                FILTER_ERROR_RETURN("Filetype not recognized" 
 
588
                                    " or not supported by libaudiofile");
 
589
 
 
590
        if (channelCount==0)
 
591
                FILTER_ERROR_RETURN("no inputs");
 
592
 
 
593
        if (!(track=ALLOCN(channelCount,track_t)))
 
594
                FILTER_ERROR_RETURN("no memory");
 
595
 
 
596
        iass=0;
 
597
        filterportdb_foreach_port(filter_portdb(n), port) {
 
598
                if (filterport_is_output(port))
 
599
                        continue;
 
600
                filterport_foreach_pipe(port, in) {
 
601
                        for(iat=0;iat<iass && FILTER_SAMPLEPIPE_MORE_LEFT(track[iat].p,in);iat++);
 
602
                        for(i=iass;i>iat;i--)
 
603
                                track[i]=track[i-1];
 
604
                        track[iat].p=in;
 
605
                        if(iass==0)
 
606
                                sampleRate=filterpipe_sample_rate(in);
 
607
                        else 
 
608
                                if (filterpipe_sample_rate(in)!=sampleRate)
 
609
                                        FILTER_ERROR_RETURN("inconsistent samplerates");
 
610
                        iass++;
 
611
                }
 
612
        }
 
613
        
 
614
        fsetup=afNewFileSetup();
 
615
        afInitFileFormat(fsetup, filetype);
 
616
        afInitChannels(fsetup, AF_DEFAULT_TRACK, channelCount);
 
617
        afInitSampleFormat(fsetup, AF_DEFAULT_TRACK, sampleFormat, sampleWidth);
 
618
 
 
619
        afInitRate(fsetup, AF_DEFAULT_TRACK,sampleRate);
 
620
        afInitCompression(fsetup, AF_DEFAULT_TRACK, compression);
 
621
 
 
622
        file=afOpenFile(filename, "w", fsetup);
 
623
        
 
624
        if (file==AF_NULL_FILEHANDLE) {
 
625
                errstring = "couldn't open file";
 
626
                goto _bailout;
 
627
        }
 
628
 
 
629
        if (afSetVirtualSampleFormat(file, AF_DEFAULT_TRACK, AF_SAMPFMT_FLOAT, 32)==-1) {
 
630
                errstring = "virtual method failed, get newer libaudiofile!";
 
631
                goto _bailout;
 
632
        }
 
633
                
 
634
        if (afSetVirtualPCMMapping(file, AF_DEFAULT_TRACK, 1.0, 0.0, -1.0, 1.0)==-1) {
 
635
                errstring = "virtual method failed, get newer libaudiofile!";
 
636
                goto _bailout; 
 
637
        }
 
638
        
 
639
        buffer_size = 2048*channelCount;
 
640
 
 
641
        buffer = ALLOCN(buffer_size, SAMPLE);
 
642
        if(buffer==NULL)
 
643
                goto _bailout;
 
644
 
 
645
        FILTER_AFTER_INIT;
 
646
        /* guihack */
 
647
        pos_param = filterparamdb_get_param(filter_paramdb(n), FILTERPARAM_LABEL_POS);
 
648
        filterparam_val_set_pos(pos_param, 0);
 
649
        pos = 0;
 
650
 
 
651
        eofs=channelCount;
 
652
        
 
653
        for(i=0;i<channelCount;i++) {
 
654
                if (!(track[i].buf=sbuf_get(track[i].p))) eofs--;
 
655
                track[i].pos=0;
 
656
        }
 
657
 
 
658
        while(eofs){
 
659
                FILTER_CHECK_STOP;
 
660
                wbpos=0;
 
661
                do{
 
662
                        /* write one interleaved frame to buffer */
 
663
                        for(i=0;i<channelCount;i++)
 
664
                                if (track[i].buf){
 
665
                                        buffer[wbpos++]=sbuf_buf(track[i].buf)[track[i].pos++];
 
666
                                        /* Check for end of buffer */
 
667
                                        if(track[i].pos==sbuf_size(track[i].buf)){
 
668
                                                sbuf_unref(track[i].buf);
 
669
                                                if (!(track[i].buf=sbuf_get(track[i].p))) eofs--;
 
670
                                                track[i].pos=0;
 
671
                                        }
 
672
                                }
 
673
                                else
 
674
                                        /* if one track stops before another we have to fill up
 
675
                                         * with zeroes
 
676
                                         */
 
677
                                        buffer[wbpos++]=0.0;
 
678
                } while ((wbpos<buffer_size) && (eofs));
 
679
                frames = wbpos/channelCount;
 
680
                if (frames>0) {
 
681
                        written = afWriteFrames(file, AF_DEFAULT_TRACK, buffer, frames);
 
682
                        if (written!=frames) {
 
683
                                failed=1;
 
684
                                errstring="couldn't write all frames(disk full?)";
 
685
                                break;
 
686
                        }
 
687
                        pos += frames;
 
688
                        filterparam_val_set_pos(pos_param, pos);
 
689
                }               
 
690
        }
 
691
 
 
692
        FILTER_BEFORE_STOPCLEANUP;
 
693
        FILTER_BEFORE_CLEANUP;
 
694
        if (failed==0)
 
695
                res=0;
 
696
_bailout:
 
697
        afCloseFile(file);
 
698
        if(fsetup) afFreeFileSetup(fsetup);
 
699
        free(buffer); 
 
700
        if (res==-1) FILTER_ERROR_RETURN(errstring); 
 
701
        return res;
 
702
}
 
703
 
 
704
int write_file_register(plugin_t *pl)
 
705
{
 
706
        filter_t *f;
 
707
        filter_port_t *in;
 
708
        filter_param_t *param;
 
709
        char *xmlparam;
 
710
        int i;
 
711
 
 
712
        if (!(f = filter_creat(NULL)))
 
713
                return -1;
 
714
 
 
715
        in = filterportdb_add_port(filter_portdb(f), PORTNAME_IN,
 
716
                                   FILTER_PORTTYPE_SAMPLE,
 
717
                                   FILTER_PORTFLAG_INPUT,
 
718
                                   FILTERPORT_DESCRIPTION, "audio stream",
 
719
                                   FILTERPORT_END);
 
720
        in->connect = write_file_connect_in;
 
721
        param = filterparamdb_add_param_string(filter_paramdb(f), "filename",
 
722
                                               FILTER_PARAMTYPE_FILENAME, NULL,
 
723
                                               FILTERPARAM_END);
 
724
 
 
725
        /* construct xmlstring for filetype parameter */
 
726
 
 
727
        af_typecnt = afQueryLong(AF_QUERYTYPE_FILEFMT, AF_QUERY_ID_COUNT,0 ,0 ,0);
 
728
        af_indices = NULL;
 
729
 
 
730
        if (af_typecnt>0) {
 
731
                xmlparam = ALLOCN(256, char);
 
732
                strcat(xmlparam, 
 
733
                       "<?xml version=\"1.0\"?><GTK-Interface>"
 
734
                       "<widget><class>GtkOptionMenu</class>"
 
735
                       "<name>widget</name><can_focus>True</can_focus><items>auto\n");
 
736
                
 
737
                
 
738
                af_indices = afQueryPointer(AF_QUERYTYPE_FILEFMT, AF_QUERY_IDS, 0 ,0, 0);       
 
739
                for(i=0; i<af_typecnt; i++) {
 
740
                        strcat(xmlparam, (char*)afQueryPointer(AF_QUERYTYPE_FILEFMT, AF_QUERY_LABEL, af_indices[i] ,0 ,0));
 
741
                        strcat(xmlparam,"\n");
 
742
                }
 
743
 
 
744
                strcat(xmlparam, "</items><initial_choice>0</initial_choice></widget></GTK-Interface>");
 
745
        
 
746
                filterparamdb_add_param_int(filter_paramdb(f),"filetype", 
 
747
                                            FILTER_PARAMTYPE_INT, 0, 
 
748
                                            FILTERPARAM_DESCRIPTION,
 
749
                                            "filetype", 
 
750
                                            FILTERPARAM_GLADEXML, xmlparam,
 
751
                                            FILTERPARAM_END);
 
752
        }
 
753
 
 
754
        filterparamdb_add_param_int(filter_paramdb(f), "sampleformat",
 
755
                                    FILTER_PARAMTYPE_INT, AF_SAMPFMT_TWOSCOMP,
 
756
                                    FILTERPARAM_HIDDEN, "FIXME",
 
757
                                    FILTERPARAM_END);
 
758
 
 
759
        filterparamdb_add_param_int(filter_paramdb(f), "samplewidth",
 
760
                                    FILTER_PARAMTYPE_INT, 16,
 
761
                                    FILTERPARAM_HIDDEN, "FIXME",
 
762
                                    FILTERPARAM_END);
 
763
        
 
764
        filterparamdb_add_param_int(filter_paramdb(f), "compression",
 
765
                                    FILTER_PARAMTYPE_INT, AF_COMPRESSION_NONE,
 
766
                                    FILTERPARAM_HIDDEN, "FIXME",
 
767
                                    FILTERPARAM_END);
 
768
 
 
769
        filterparamdb_add_param_pos(filter_paramdb(f));
 
770
 
 
771
        f->f = write_file_f;
 
772
 
 
773
        plugin_set(pl, PLUGIN_DESCRIPTION, "write a file");
 
774
        plugin_set(pl, PLUGIN_PIXMAP, "output.png");
 
775
        plugin_set(pl, PLUGIN_CATEGORY, "Output");
 
776
        plugin_set(pl, PLUGIN_GUI_HELP_PATH, "File_I_O");
 
777
        
 
778
        filter_register(f, pl);
 
779
 
 
780
        return 0;
 
781
}