3
* $Id: file_io.c,v 1.75 2001/12/07 22:28:01 mag Exp $
5
* Copyright (C) 1999, 2000 Alexander Ehlert, Richard Guenther, Daniel Kobras
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.
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.
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
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
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
47
#include <sys/types.h>
58
#include "glame_types.h"
59
#include "glame_byteorder.h"
60
#include "glame_audiofile.h"
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);
69
PLUGIN_SET(file_io, "read_file write_file")
73
struct glame_list_head list;
74
int (*prepare)(filter_t *, const char *);
75
int (*connect)(filter_t *, filter_pipe_t *);
77
void (*cleanup)(filter_t *);
92
/* put your shared state stuff here */
95
AFframecount frameCount;
97
int sampleFormat,sampleWidth;
98
int channelCount,frameSize;
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)
109
/* the readers & the writers list */
110
static struct glame_list_head readers;
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 *),
122
if (!(rw = ALLOC(rw_t)))
124
GLAME_INIT_LIST_HEAD(&rw->list);
125
rw->prepare = prepare;
126
rw->connect = connect;
128
rw->cleanup = cleanup;
130
rw->regexp = strdup(regexp);
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 *))
141
if (!(rw = add_rw(prepare, connect, f, cleanup, NULL)))
143
glame_list_add(&rw->list, &readers);
147
/* generic read&write methods */
148
static void rw_file_cleanup(glsig_handler_t *h, long sig, va_list va)
152
GLSIGH_GETARGS1(va, n);
158
&& RWPRIV(n)->rw->cleanup
159
&& RWPRIV(n)->initted)
160
RWPRIV(n)->rw->cleanup(n);
164
static int rw_file_init(filter_t *n)
168
if (!(p = ALLOC(rw_private_t)))
171
glsig_add_handler(&n->emitter, GLSIG_FILTER_DELETED,
172
rw_file_cleanup, NULL);
178
static int read_file_f(filter_t *n)
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);
188
static int read_file_connect_out(filter_port_t *port, filter_pipe_t *p)
190
filter_t *n = filterport_filter(port);
192
/* no reader -> no filename -> some "defaults".
193
* only allow 2 connections. */
194
if (!RWPRIV(n)->rw) {
195
if (filterport_nrpipes(port) > 1)
197
filterpipe_settype_sample(p, GLAME_DEFAULT_SAMPLERATE,
198
FILTER_PIPEPOS_DEFAULT);
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);
207
static void read_file_fixup_pipe(glsig_handler_t *h, long sig, va_list va) {
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);
217
static int read_file_setup_param(filter_param_t *param, const void *val)
219
filter_t *n = filterparam_filter(param);
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);
233
/* filename change! */
235
filename = *((char**)val);
239
DPRINTF("filename change to %s\n", filename);
240
/* check actual reader */
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 */
251
RWPRIV(n)->rw = NULL;
252
RWPRIV(n)->initted = 0;
254
/* search for applicable reader */
255
glame_list_foreach(&readers, rw_t, list, r) {
256
if (r->prepare(n, filename) != -1) {
258
RWPRIV(n)->initted = 1;
263
/* no reader found */
268
/* re-connect all pipes */
269
filterportdb_foreach_port(filter_portdb(n), port) {
270
if (filterport_is_input(port))
272
filterport_foreach_pipe(port, p) {
273
if (RWPRIV(n)->rw->connect(n, p) == -1) {
274
filterpipe_delete(p);
277
glsig_emit(&p->emitter, GLSIG_PIPE_CHANGED, p);
284
static int write_file_connect_in(filter_port_t *port, filter_pipe_t *p)
286
/* So why is there no write_file_connect_in?? Do we really
287
* support any number of inputs? Seems all is _f() time... */
291
int read_file_register(plugin_t *pl)
295
filter_param_t *param;
297
if (!(f = filter_creat(NULL)))
300
p = filterportdb_add_port(filter_portdb(f), PORTNAME_OUT,
301
FILTER_PORTTYPE_SAMPLE,
302
FILTER_PORTFLAG_OUTPUT,
303
FILTERPORT_DESCRIPTION, "audio stream",
305
p->connect = read_file_connect_out;
306
param = filterparamdb_add_param_float(filterport_paramdb(p), "position",
307
FILTER_PARAMTYPE_POSITION, FILTER_PIPEPOS_DEFAULT,
309
param->set = read_file_setup_param;
310
param = filterparamdb_add_param_string(filter_paramdb(f), "filename",
311
FILTER_PARAMTYPE_FILENAME, NULL,
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));
318
f->init = rw_file_init;
320
glsig_add_handler(&f->emitter, GLSIG_PIPE_DELETED,
321
read_file_fixup_pipe, NULL);
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");
328
filter_register(f, pl);
335
int file_io_register(plugin_t *p)
337
GLAME_INIT_LIST_HEAD(&readers);
339
add_reader(af_read_prepare, af_read_connect,
340
af_read_f, af_read_cleanup);
347
/* The actual readers and writers.
350
int af_read_prepare(filter_t *n, const char *filename)
352
filter_param_t *fparam;
356
fparam = filterparamdb_get_param(filter_paramdb(n), "filename");
358
DPRINTF("Using audiofile library\n");
359
if ((RWA(n).file=afOpenFile(filename,"r",NULL))==NULL){
360
DPRINTF("File not found!\n");
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);
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);
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);
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);
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);
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);
391
if (!(RWA(n).track=ALLOCN(RWA(n).channelCount,track_t))){
392
DPRINTF("Couldn't allocate track buffer\n");
396
DPRINTF("File %s: %d channel(s) %d bit %s at %d Hz, "
397
"framecount %d, framesize %d.\n",
399
RWA(n).channelCount, RWA(n).sampleWidth,
400
RWA(n).sampleFormat == AF_SAMPFMT_TWOSCOMP ?
402
RWA(n).sampleFormat == AF_SAMPFMT_UNSIGNED ?
403
"unsigned" : "unknown",
404
RWA(n).sampleRate, (int)RWA(n).frameCount,
409
int af_read_connect(filter_t *n, filter_pipe_t *p)
415
outp = filterportdb_get_port(filter_portdb(n), PORTNAME_OUT);
416
filterport_foreach_pipe(outp, out) {
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;
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
441
for(i=0;i<RWA(n).channelCount;i++)
442
if ((RWA(n).track[i].mapped) && RWA(n).track[i].p==p){
447
/* Moah! what is this? does libaudiofile not provide
448
* some "direct" information on position??
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);
454
filterpipe_settype_sample(p,RWA(n).sampleRate,FILTER_PIPEPOS_CENTRE);
456
RWA(n).track[i].mapped=1;
461
int af_read_f(filter_t *n)
464
filter_pipe_t *p_out;
469
filter_param_t *pos_param;
472
/* seek to start of audiofile */
473
afSeekFrame(RWA(n).file, AF_DEFAULT_TRACK, 0);
474
fcnt = RWA(n).frameCount;
476
if (afSetVirtualSampleFormat(RWA(n).file, AF_DEFAULT_TRACK, AF_SAMPFMT_FLOAT, 32)==-1)
477
FILTER_ERROR_RETURN("virtual method failed, get newer libaudiofile!");
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!");
482
buffer=ALLOCN(GLAME_WBUFSIZE*RWA(n).channelCount, SAMPLE);
485
pos_param = filterparamdb_get_param(filter_paramdb(n), FILTERPARAM_LABEL_POS);
486
filterparam_val_set_pos(pos_param, 0);
491
if (!(frames=afReadFrames(RWA(n).file, AF_DEFAULT_TRACK,
493
MIN(GLAME_WBUFSIZE, fcnt))))
496
filterparam_val_set_pos(pos_param, pos);
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;
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++] =
509
for (i=0; i < RWA(n).channelCount; i++)
510
sbuf_queue(RWA(n).track[i].p, RWA(n).track[i].buf);
513
filterportdb_foreach_port(filter_portdb(n), port) {
514
if (filterport_is_input(port))
516
filterport_foreach_pipe(port, p_out)
517
sbuf_queue(p_out, NULL);
520
FILTER_BEFORE_STOPCLEANUP;
521
FILTER_BEFORE_CLEANUP;
529
void af_read_cleanup(filter_t *n)
532
afCloseFile(RWA(n).file);
533
memset(&(RWPRIV(n)->u), 0, sizeof(RWPRIV(n)->u));
537
static int af_typecnt, *af_indices;
539
int write_file_f(filter_t *n)
544
int res=-1, failed=0;
548
char *errstring = "write file failed";
550
filter_param_t *pos_param;
552
/* audiofile stuff */
555
int sampleFormat,sampleWidth;
556
int channelCount, compression;
560
int buffer_size, written, frames;
563
channelCount = filterport_nrpipes(filterportdb_get_port(filter_portdb(n), PORTNAME_IN));
565
filename = filterparam_val_string(filterparamdb_get_param(filter_paramdb(n), "filename"));
568
FILTER_ERROR_RETURN("no filename");
571
filetype = filterparam_val_int(filterparamdb_get_param(filter_paramdb(n), "filetype"));
573
filetype = glame_get_filetype_by_name(filename);
576
if (filetype>=af_typecnt)
579
filetype=af_indices[filetype];
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"));
587
FILTER_ERROR_RETURN("Filetype not recognized"
588
" or not supported by libaudiofile");
591
FILTER_ERROR_RETURN("no inputs");
593
if (!(track=ALLOCN(channelCount,track_t)))
594
FILTER_ERROR_RETURN("no memory");
597
filterportdb_foreach_port(filter_portdb(n), port) {
598
if (filterport_is_output(port))
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--)
606
sampleRate=filterpipe_sample_rate(in);
608
if (filterpipe_sample_rate(in)!=sampleRate)
609
FILTER_ERROR_RETURN("inconsistent samplerates");
614
fsetup=afNewFileSetup();
615
afInitFileFormat(fsetup, filetype);
616
afInitChannels(fsetup, AF_DEFAULT_TRACK, channelCount);
617
afInitSampleFormat(fsetup, AF_DEFAULT_TRACK, sampleFormat, sampleWidth);
619
afInitRate(fsetup, AF_DEFAULT_TRACK,sampleRate);
620
afInitCompression(fsetup, AF_DEFAULT_TRACK, compression);
622
file=afOpenFile(filename, "w", fsetup);
624
if (file==AF_NULL_FILEHANDLE) {
625
errstring = "couldn't open file";
629
if (afSetVirtualSampleFormat(file, AF_DEFAULT_TRACK, AF_SAMPFMT_FLOAT, 32)==-1) {
630
errstring = "virtual method failed, get newer libaudiofile!";
634
if (afSetVirtualPCMMapping(file, AF_DEFAULT_TRACK, 1.0, 0.0, -1.0, 1.0)==-1) {
635
errstring = "virtual method failed, get newer libaudiofile!";
639
buffer_size = 2048*channelCount;
641
buffer = ALLOCN(buffer_size, SAMPLE);
647
pos_param = filterparamdb_get_param(filter_paramdb(n), FILTERPARAM_LABEL_POS);
648
filterparam_val_set_pos(pos_param, 0);
653
for(i=0;i<channelCount;i++) {
654
if (!(track[i].buf=sbuf_get(track[i].p))) eofs--;
662
/* write one interleaved frame to buffer */
663
for(i=0;i<channelCount;i++)
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--;
674
/* if one track stops before another we have to fill up
678
} while ((wbpos<buffer_size) && (eofs));
679
frames = wbpos/channelCount;
681
written = afWriteFrames(file, AF_DEFAULT_TRACK, buffer, frames);
682
if (written!=frames) {
684
errstring="couldn't write all frames(disk full?)";
688
filterparam_val_set_pos(pos_param, pos);
692
FILTER_BEFORE_STOPCLEANUP;
693
FILTER_BEFORE_CLEANUP;
698
if(fsetup) afFreeFileSetup(fsetup);
700
if (res==-1) FILTER_ERROR_RETURN(errstring);
704
int write_file_register(plugin_t *pl)
708
filter_param_t *param;
712
if (!(f = filter_creat(NULL)))
715
in = filterportdb_add_port(filter_portdb(f), PORTNAME_IN,
716
FILTER_PORTTYPE_SAMPLE,
717
FILTER_PORTFLAG_INPUT,
718
FILTERPORT_DESCRIPTION, "audio stream",
720
in->connect = write_file_connect_in;
721
param = filterparamdb_add_param_string(filter_paramdb(f), "filename",
722
FILTER_PARAMTYPE_FILENAME, NULL,
725
/* construct xmlstring for filetype parameter */
727
af_typecnt = afQueryLong(AF_QUERYTYPE_FILEFMT, AF_QUERY_ID_COUNT,0 ,0 ,0);
731
xmlparam = ALLOCN(256, char);
733
"<?xml version=\"1.0\"?><GTK-Interface>"
734
"<widget><class>GtkOptionMenu</class>"
735
"<name>widget</name><can_focus>True</can_focus><items>auto\n");
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");
744
strcat(xmlparam, "</items><initial_choice>0</initial_choice></widget></GTK-Interface>");
746
filterparamdb_add_param_int(filter_paramdb(f),"filetype",
747
FILTER_PARAMTYPE_INT, 0,
748
FILTERPARAM_DESCRIPTION,
750
FILTERPARAM_GLADEXML, xmlparam,
754
filterparamdb_add_param_int(filter_paramdb(f), "sampleformat",
755
FILTER_PARAMTYPE_INT, AF_SAMPFMT_TWOSCOMP,
756
FILTERPARAM_HIDDEN, "FIXME",
759
filterparamdb_add_param_int(filter_paramdb(f), "samplewidth",
760
FILTER_PARAMTYPE_INT, 16,
761
FILTERPARAM_HIDDEN, "FIXME",
764
filterparamdb_add_param_int(filter_paramdb(f), "compression",
765
FILTER_PARAMTYPE_INT, AF_COMPRESSION_NONE,
766
FILTERPARAM_HIDDEN, "FIXME",
769
filterparamdb_add_param_pos(filter_paramdb(f));
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");
778
filter_register(f, pl);