2
* Copyright (C) 2002 2003 2004 2005, Magnus Hjorth
4
* This file is part of mhWaveEdit.
6
* mhWaveEdit is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* mhWaveEdit is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with mhWaveEdit; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
33
#include "datasource.h"
37
static GtkObjectClass *parent_class;
38
static GList *datasource_list = NULL;
41
guint datasource_count(void)
43
return g_list_length(datasource_list);
46
static void datasource_init(Datasource *obj)
48
/* printf("datasource_init(%p)\n",obj); */
49
datasource_list = g_list_append(datasource_list, obj);
50
obj->type = DATASOURCE_SILENCE;
51
obj->format.type = DATAFORMAT_PCM;
52
obj->format.samplerate=44100;
53
obj->format.samplesize=2;
54
obj->format.channels=2;
56
obj->format.bigendian = IS_BIGENDIAN;
57
obj->format.samplebytes=4;
64
static void datasource_clear(Datasource *ds)
66
g_assert(ds->opencount == 0);
70
case DATASOURCE_TEMPFILE:
71
if (ds->data.virtual.filename)
72
xunlink(ds->data.virtual.filename);
75
case DATASOURCE_VIRTUAL:
76
if (ds->data.virtual.filename) {
77
g_free(ds->data.virtual.filename);
78
ds->data.virtual.filename = NULL;
84
g_free(ds->data.real);
89
case DATASOURCE_SNDFILE_TEMPORARY:
90
if (ds->data.sndfile.filename)
91
xunlink(ds->data.sndfile.filename);
94
case DATASOURCE_SNDFILE:
95
if (ds->data.sndfile.filename) {
96
g_free(ds->data.sndfile.filename);
97
ds->data.sndfile.filename = NULL;
102
case DATASOURCE_CLONE:
103
case DATASOURCE_BYTESWAP:
104
case DATASOURCE_CONVERT:
106
gtk_object_unref(GTK_OBJECT(ds->data.clone));
107
ds->data.clone = NULL;
110
ds->type = DATASOURCE_SILENCE;
113
static void datasource_destroy(GtkObject *obj)
115
Datasource *ds = DATASOURCE(obj);
116
/* printf("datasource_destroy(%p)\n",obj); */
117
datasource_clear(ds);
118
datasource_list = g_list_remove(datasource_list, obj);
119
parent_class->destroy(obj);
122
static void datasource_class_init(GtkObjectClass *klass)
124
parent_class = gtk_type_class(gtk_object_get_type());
125
klass->destroy = datasource_destroy;
128
GtkType datasource_get_type(void)
130
static GtkType id = 0;
135
sizeof(DatasourceClass),
136
(GtkClassInitFunc) datasource_class_init,
137
(GtkObjectInitFunc) datasource_init
139
id=gtk_type_unique(gtk_object_get_type(),&info);
144
/* Creates a copy of the original datasource
145
* If it refers to a file, create a DATASOURCE_REF instead.
148
static Datasource *datasource_copy(Datasource *orig)
152
ds = gtk_type_new(datasource_get_type());
153
ds->type = orig->type;
154
memcpy(&(ds->format),&(orig->format),sizeof(Dataformat));
155
ds->length = orig->length;
156
ds->bytes = orig->bytes;
158
switch (orig->type) {
159
case DATASOURCE_VIRTUAL:
160
case DATASOURCE_TEMPFILE:
161
case DATASOURCE_SNDFILE:
162
case DATASOURCE_SNDFILE_TEMPORARY:
163
ds->type = DATASOURCE_REF;
164
ds->data.clone = orig;
165
gtk_object_ref(GTK_OBJECT(orig));
166
gtk_object_sink(GTK_OBJECT(orig));
168
case DATASOURCE_REAL:
169
ds->data.real = g_malloc(orig->bytes);
170
memcpy(ds->data.real, orig->data.real, orig->bytes);
172
case DATASOURCE_CLONE:
173
case DATASOURCE_BYTESWAP:
175
case DATASOURCE_CONVERT:
176
ds->data.clone = orig->data.clone;
177
gtk_object_ref(GTK_OBJECT(ds->data.clone));
178
gtk_object_sink(GTK_OBJECT(ds->data.clone));
183
Datasource *datasource_clone_df(Datasource *source, Dataformat *format)
187
g_assert(source->format.type == DATAFORMAT_PCM);
189
if (source->type == DATASOURCE_REAL ||
190
source->type == DATASOURCE_SILENCE ||
191
source->type == DATASOURCE_CLONE) {
192
df = datasource_copy(source);
193
g_assert(df->type == source->type);
194
memcpy(&(df->format),format,sizeof(Dataformat));
195
df->length = df->bytes / format->samplebytes;
199
df = gtk_type_new(datasource_get_type());
200
df->type = DATASOURCE_CLONE;
201
memcpy(&(df->format),format,sizeof(Dataformat));
202
df->bytes = source->bytes;
203
df->length = source->bytes / format->samplebytes;
204
df->data.clone = source;
205
gtk_object_ref(GTK_OBJECT(source));
209
Datasource *datasource_byteswap(Datasource *source)
212
if (source == NULL) return NULL;
213
ds = gtk_type_new(datasource_get_type());
214
ds->type = DATASOURCE_BYTESWAP;
215
memcpy(&(ds->format),&(source->format),sizeof(Dataformat));
216
ds->format.bigendian = !source->format.bigendian;
217
ds->length = source->length;
218
ds->bytes = source->bytes;
219
ds->data.clone = source;
220
gtk_object_ref(GTK_OBJECT(source));
221
gtk_object_sink(GTK_OBJECT(source));
225
Datasource *datasource_new_silent(Dataformat *format, off_t samples)
228
ds = gtk_type_new(datasource_get_type());
229
memcpy(&(ds->format), format, sizeof(Dataformat));
230
ds->type = DATASOURCE_SILENCE;
231
ds->length = samples;
232
ds->bytes = samples * ds->format.samplebytes;
236
Datasource *datasource_new_from_data(void *data, Dataformat *format,
240
ds = (Datasource *)gtk_type_new(datasource_get_type());
241
ds->type = DATASOURCE_REAL;
242
memcpy(&(ds->format),format,sizeof(Dataformat));
243
ds->length = size / format->samplebytes;
245
ds->data.real = data;
249
#define DUMP_BUFSIZE 65536
251
gboolean datasource_dump(Datasource *ds, off_t position,
252
off_t length, EFILE *file, int dither_mode,
258
if (datasource_open(ds)) return TRUE;
259
buf = g_malloc(DUMP_BUFSIZE);
261
i = MIN(length*ds->format.samplebytes,DUMP_BUFSIZE);
262
u = datasource_read_array(ds,position,i,buf,dither_mode);
263
if (u==0 || e_fwrite(buf,u,file) || status_bar_progress(bar,u)) {
264
datasource_close(ds);
268
length -= u/ds->format.samplebytes;
269
position += u/ds->format.samplebytes;
271
datasource_close(ds);
276
gboolean datasource_realize(Datasource *ds, int dither_mode)
279
guint32 sz=ds->bytes;
281
if (datasource_open(ds)) return TRUE;
284
if (datasource_read_array(ds,0,sz,c, dither_mode)) {
286
datasource_close(ds);
289
datasource_close(ds);
291
datasource_clear(ds);
292
ds->type = DATASOURCE_REAL;
298
static gboolean datasource_open_sndfile(Datasource *ds)
300
#if defined(HAVE_LIBSNDFILE)
305
fd = xopen(ds->data.sndfile.filename,O_RDONLY,0);
306
if (fd == -1) return TRUE;
307
s = sf_open_fd(fd,SFM_READ,&i,TRUE);
309
d = g_strdup_printf(_("Couldn't open %s"),
310
ds->data.sndfile.filename);
317
ds->data.sndfile.handle = s;
318
ds->data.sndfile.pos = 0;
320
sf_command ( s, SFC_SET_NORM_FLOAT, NULL, SF_TRUE );
321
sf_command ( s, SFC_SET_NORM_DOUBLE, NULL, SF_TRUE );
324
g_assert_not_reached();
329
static void datasource_close_sndfile(Datasource *ds)
331
#if defined(HAVE_LIBSNDFILE)
332
sf_close(ds->data.sndfile.handle);
334
g_assert_not_reached();
338
gboolean datasource_open(Datasource *ds)
340
if (ds->opencount == 0)
342
case DATASOURCE_VIRTUAL:
343
case DATASOURCE_TEMPFILE:
344
ds->data.virtual.handle = e_fopen(ds->data.virtual.filename,
346
if (ds->data.virtual.handle == NULL) return TRUE;
347
ds->data.virtual.pos = 0;
349
case DATASOURCE_SNDFILE:
350
case DATASOURCE_SNDFILE_TEMPORARY:
351
if (datasource_open_sndfile(ds)) return TRUE;
354
case DATASOURCE_CLONE:
355
case DATASOURCE_BYTESWAP:
356
case DATASOURCE_CONVERT:
357
if (datasource_open(ds->data.clone)) return TRUE;
363
void datasource_close(Datasource *ds)
365
g_assert(ds->opencount != 0);
367
if (ds->opencount > 0) return;
369
case DATASOURCE_VIRTUAL:
370
case DATASOURCE_TEMPFILE:
371
e_fclose(ds->data.virtual.handle);
373
case DATASOURCE_SNDFILE:
374
case DATASOURCE_SNDFILE_TEMPORARY:
375
datasource_close_sndfile(ds);
378
case DATASOURCE_CLONE:
379
case DATASOURCE_BYTESWAP:
380
case DATASOURCE_CONVERT:
381
datasource_close(ds->data.clone);
386
static guint datasource_clone_read_array(Datasource *source, off_t sampleno,
387
guint size, gpointer buffer,
390
/* This is not optimized but it's only used in rare cases so the important
391
* thing is that it works.*/
398
/* Offset to be sent to the original chunk */
399
orig_offset = sampleno * source->format.samplebytes;
400
/* orig_offset converted to sample number */
401
orig_sampleno = orig_offset / source->data.clone->format.samplebytes;
402
/* How bany bytes too early will we read? */
403
orig_adjust = orig_offset % source->data.clone->format.samplebytes;
404
/* How much data should we read (should be able to fill buffer) */
405
orig_size = size + orig_adjust + source->data.clone->format.samplebytes - 1;
406
p = g_malloc(orig_size);
407
x = datasource_read_array(source->data.clone, orig_sampleno, orig_size, p,
410
g_assert(x-orig_adjust >= size);
411
memcpy(buffer, p+orig_adjust, size);
418
#if defined(HAVE_LIBSNDFILE)
419
static void sndfile_read_error(sf_count_t x, Datasource *source)
422
if (x<0) sf_error_str(source->data.sndfile.handle,c,sizeof(c));
423
else strcpy(c,_("Unexpected end of file"));
424
d = g_strdup_printf(_("Error reading %s: %s"),
425
source->data.sndfile.filename, c);
431
static guint datasource_sndfile_read_array_pcm(Datasource *source,
432
off_t sampleno, guint size,
435
#if defined(HAVE_LIBSNDFILE)
438
g_assert(source->format.type == DATAFORMAT_PCM);
439
if (source->data.sndfile.pos != sampleno) {
440
if (sf_seek(source->data.sndfile.handle,sampleno,SEEK_SET) == -1) {
441
sf_error_str(source->data.sndfile.handle,c,sizeof(c));
442
d = g_strdup_printf(_("Error seeking in %s: %s"),
443
source->data.sndfile.filename, c);
448
source->data.sndfile.pos = sampleno;
450
if (source->data.sndfile.raw_readable) {
452
x = sf_read_raw(source->data.sndfile.handle,buffer,size);
453
if (x>0) source->data.sndfile.pos += x/source->format.samplebytes;
455
sndfile_read_error(x,source);
459
} else if (source->format.samplesize==2 && source->format.sign) {
461
x = sf_readf_short(source->data.sndfile.handle,buffer,
462
size / source->format.samplebytes);
463
if (x>0) source->data.sndfile.pos += x;
464
if (x < size / source->format.samplebytes) {
465
sndfile_read_error(x,source);
468
return x * source->format.samplebytes;
470
g_assert_not_reached();
474
g_assert_not_reached();
479
static guint datasource_sndfile_read_array_fp(Datasource *source,
480
off_t sampleno, guint samples,
483
#if defined(HAVE_LIBSNDFILE)
487
if (source->data.sndfile.pos != sampleno) {
488
if (sf_seek(source->data.sndfile.handle,sampleno,SEEK_SET) == -1) {
489
sf_error_str(source->data.sndfile.handle,c,sizeof(c));
490
d = g_strdup_printf(_("Error seeking in %s: %s"),
491
source->data.sndfile.filename, c);
496
source->data.sndfile.pos = sampleno;
499
x = sf_readf_sample_t(source->data.sndfile.handle,buffer,samples);
500
if (x>0) source->data.sndfile.pos += x;
502
sndfile_read_error(x,source);
508
g_assert_not_reached();
513
static guint datasource_read_array_main(Datasource *source,
514
off_t sampleno, guint size,
515
gpointer buffer, int dither_mode)
520
switch (source->type) {
521
case DATASOURCE_REAL:
522
memcpy(buffer,source->data.real+sampleno*source->format.samplebytes,
525
case DATASOURCE_VIRTUAL:
526
case DATASOURCE_TEMPFILE:
527
/* Calculate offset in file */
528
x = source->data.virtual.offset +
529
sampleno*source->format.samplebytes;
530
if (x != source->data.virtual.pos &&
531
e_fseek(source->data.virtual.handle, x, SEEK_SET))
533
source->data.virtual.pos = x;
534
if (e_fread(buffer,size,source->data.virtual.handle)) return 0;
535
source->data.virtual.pos += size;
537
case DATASOURCE_SILENCE:
538
memset(buffer,0,size);
540
case DATASOURCE_SNDFILE:
541
case DATASOURCE_SNDFILE_TEMPORARY:
542
if (source->format.type == DATAFORMAT_PCM)
543
return datasource_sndfile_read_array_pcm(source,sampleno,size,
546
return datasource_sndfile_read_array_fp
547
(source,sampleno,size/source->format.samplebytes,buffer);
549
return datasource_read_array_main(source->data.clone,sampleno,size,
551
case DATASOURCE_CLONE:
552
return datasource_clone_read_array(source,sampleno,size,buffer,
554
case DATASOURCE_BYTESWAP:
555
u = datasource_read_array_main(source->data.clone,sampleno,size,
557
if (u>0) byteswap(buffer,source->format.samplesize,u);
559
case DATASOURCE_CONVERT:
560
u = size / source->format.samplebytes;
561
if (source->format.type == DATAFORMAT_FLOAT &&
562
source->format.samplesize == sizeof(sample_t))
563
return datasource_read_array_fp(source->data.clone,sampleno,u,
564
buffer,dither_mode) *
565
source->format.samplebytes ;
566
c = g_malloc(u*sizeof(sample_t)*source->format.channels);
567
u = datasource_read_array_fp(source->data.clone, sampleno, u,
568
(gpointer)c,dither_mode);
570
convert_array(c,&dataformat_sample_t,buffer,&(source->format),
571
u*source->format.channels,dither_mode);
574
return u * source->format.samplebytes;
576
g_assert_not_reached();
581
guint datasource_read_array(Datasource *source, off_t sampleno, guint size,
582
gpointer buffer, int dither_mode)
585
g_assert(source->opencount > 0);
587
g_assert(sampleno <= source->length);
589
/* Round down to even samples */
590
size = size - size % source->format.samplebytes;
591
o = (source->length - sampleno) * (off_t)(source->format.samplebytes);
592
if (size > o) size = (guint)o; /* Round down to available data */
593
if (size == 0) return 0;
595
return datasource_read_array_main(source,sampleno,size,buffer,
599
gboolean datasource_read(Datasource *source, off_t sampleno, gpointer buffer,
602
return (datasource_read_array(source,sampleno,source->format.samplebytes,
604
!= source->format.samplebytes);
608
guint datasource_read_array_fp(Datasource *source, off_t sampleno,
609
guint samples, sample_t *buffer,
615
g_assert(sampleno <= source->length);
616
if (samples > source->length-sampleno)
617
samples = (guint)(source->length-sampleno);
618
if (samples == 0) return 0;
621
switch (source->type) {
622
case DATASOURCE_SILENCE:
623
memset(buffer,0,samples*source->format.channels*sizeof(sample_t));
625
case DATASOURCE_SNDFILE:
626
case DATASOURCE_SNDFILE_TEMPORARY:
627
return datasource_sndfile_read_array_fp(source,sampleno,samples,
630
case DATASOURCE_CONVERT:
631
return datasource_read_array_fp(source->data.clone,sampleno,
632
samples,buffer,dither_mode);
634
if (source->format.type == DATAFORMAT_FLOAT &&
635
source->format.samplesize == sizeof(sample_t))
637
return datasource_read_array(source,sampleno,
638
samples*source->format.samplebytes,
640
/ source->format.samplebytes;
642
s = samples * source->format.samplebytes;
644
x = datasource_read_array(source,sampleno,s,p,dither_mode);
645
g_assert(x==s || x==0);
647
convert_array(p,&(source->format),buffer,&dataformat_sample_t,
648
samples*source->format.channels,dither_mode);
657
gboolean datasource_read_fp(Datasource *ds, off_t sampleno, sample_t *buffer,
660
return (datasource_read_array_fp(ds,sampleno,1,buffer,dither_mode)!=1);
663
static gboolean datasource_uses_file(Datasource *ds, gchar *filename)
665
return ((ds->type==DATASOURCE_VIRTUAL &&
666
is_same_file(ds->data.virtual.filename,filename)) ||
667
((ds->type==DATASOURCE_SNDFILE &&
668
is_same_file(ds->data.sndfile.filename,filename))));
671
gboolean datasource_backup_unlink(gchar *filename)
675
Datasource *backup=NULL;
676
gchar *lastname=filename,*t;
680
/* Find out which Datasources actually use the file. */
681
for (l=datasource_list; l!=NULL; l=l->next) {
682
ds = (Datasource *)l->data;
683
if (datasource_uses_file(ds,filename))
684
q = g_list_append(q,ds);
687
/* Now iterate through those files. */
688
for (l=q; l!=NULL; l=l->next) {
689
ds = (Datasource *)l->data;
690
if (!datasource_uses_file(ds,filename)) continue;
692
/* FIXME: When multiple tempdir support has been added, try moving to
693
* all different partitions before copying */
694
if (backup == NULL) {
697
t = get_temp_filename(dirnum);
700
t = get_temp_filename(0);
701
i = xrename(lastname,t,TRUE);
704
i = xrename(lastname,t,FALSE);
714
/* Special case: the same file has been opened many times. */
715
t = get_temp_filename(0);
716
if (errdlg_copyfile(lastname,t)) {
723
case DATASOURCE_VIRTUAL:
724
g_free(ds->data.virtual.filename);
725
ds->data.virtual.filename = t;
726
ds->type = DATASOURCE_TEMPFILE;
728
case DATASOURCE_SNDFILE:
729
g_free(ds->data.sndfile.filename);
730
ds->data.sndfile.filename = t;
731
ds->type = DATASOURCE_SNDFILE_TEMPORARY;
734
g_assert_not_reached();
742
return xunlink(filename);
745
Datasource *datasource_convert(Datasource *source, Dataformat *new_format)
748
if (source == NULL) return NULL;
749
g_assert(!dataformat_equal(new_format,&(source->format)));
750
g_assert(new_format->channels == source->format.channels &&
751
new_format->samplerate == source->format.samplerate);
753
if (source->format.type == DATAFORMAT_PCM &&
754
new_format->type == DATAFORMAT_PCM &&
755
source->format.samplesize == new_format->samplesize &&
756
source->format.sign == new_format->sign) {
757
return datasource_byteswap(source);
760
ds = gtk_type_new(datasource_get_type());
761
ds->type = DATASOURCE_CONVERT;
762
memcpy(&(ds->format),new_format,sizeof(Dataformat));
763
ds->length = source->length;
764
ds->bytes = ds->length * new_format->samplebytes;
765
ds->data.clone = source;
766
gtk_object_ref(GTK_OBJECT(source));
767
gtk_object_sink(GTK_OBJECT(source));