~ubuntu-branches/ubuntu/lucid/mhwaveedit/lucid

« back to all changes in this revision

Viewing changes to src/datasource.c

  • Committer: Bazaar Package Importer
  • Author(s): Free Ekanayaka
  • Date: 2005-09-29 09:27:02 UTC
  • Revision ID: james.westby@ubuntu.com-20050929092702-7jqrmvibtyuo01be
Tags: upstream-1.4.5b
ImportĀ upstreamĀ versionĀ 1.4.5b

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2002 2003 2004 2005, Magnus Hjorth
 
3
 *
 
4
 * This file is part of mhWaveEdit.
 
5
 *
 
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.
 
10
 *
 
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.
 
15
 *
 
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
 
19
 */
 
20
 
 
21
 
 
22
#include <config.h>
 
23
 
 
24
#include <sys/stat.h>
 
25
#include <unistd.h>
 
26
#include <fcntl.h>
 
27
#include "gtkfiles.h"
 
28
#include "ringbuf.h"
 
29
#include "um.h"
 
30
#include "main.h"
 
31
#include "inifile.h"
 
32
 
 
33
#include "datasource.h"
 
34
#include "tempfile.h"
 
35
#include "gettext.h"
 
36
 
 
37
static GtkObjectClass *parent_class;
 
38
static GList *datasource_list = NULL;
 
39
 
 
40
 
 
41
guint datasource_count(void)
 
42
{
 
43
     return g_list_length(datasource_list);
 
44
}
 
45
 
 
46
static void datasource_init(Datasource *obj)
 
47
{
 
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;
 
55
     obj->format.sign=1;
 
56
     obj->format.bigendian = IS_BIGENDIAN;
 
57
     obj->format.samplebytes=4;
 
58
     obj->length = 0;
 
59
     obj->bytes = 0;
 
60
     obj->opencount = 0;
 
61
     obj->tag = 0;
 
62
}
 
63
 
 
64
static void datasource_clear(Datasource *ds)
 
65
{
 
66
     g_assert(ds->opencount == 0);
 
67
 
 
68
     switch (ds->type) {
 
69
 
 
70
     case DATASOURCE_TEMPFILE:
 
71
          if (ds->data.virtual.filename)
 
72
               xunlink(ds->data.virtual.filename);        
 
73
          /* Fall through */
 
74
 
 
75
     case DATASOURCE_VIRTUAL:
 
76
          if (ds->data.virtual.filename) {
 
77
               g_free(ds->data.virtual.filename);
 
78
               ds->data.virtual.filename = NULL;
 
79
          }
 
80
          break;
 
81
          
 
82
     case DATASOURCE_REAL:
 
83
          if (ds->data.real) {
 
84
               g_free(ds->data.real);
 
85
               ds->data.real = NULL;
 
86
          }
 
87
          break;
 
88
 
 
89
     case DATASOURCE_SNDFILE_TEMPORARY:
 
90
          if (ds->data.sndfile.filename)
 
91
               xunlink(ds->data.sndfile.filename);
 
92
          /* Fall through */
 
93
 
 
94
     case DATASOURCE_SNDFILE:
 
95
          if (ds->data.sndfile.filename) {
 
96
               g_free(ds->data.sndfile.filename);
 
97
               ds->data.sndfile.filename = NULL;
 
98
          }
 
99
          break;
 
100
          
 
101
     case DATASOURCE_REF:
 
102
     case DATASOURCE_CLONE:
 
103
     case DATASOURCE_BYTESWAP:
 
104
     case DATASOURCE_CONVERT:
 
105
          if (ds->data.clone) 
 
106
               gtk_object_unref(GTK_OBJECT(ds->data.clone));
 
107
          ds->data.clone = NULL;
 
108
     }
 
109
     
 
110
     ds->type = DATASOURCE_SILENCE;
 
111
}
 
112
 
 
113
static void datasource_destroy(GtkObject *obj)
 
114
{     
 
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);
 
120
}
 
121
 
 
122
static void datasource_class_init(GtkObjectClass *klass)
 
123
{
 
124
     parent_class = gtk_type_class(gtk_object_get_type());
 
125
     klass->destroy = datasource_destroy;
 
126
}
 
127
 
 
128
GtkType datasource_get_type(void)
 
129
{
 
130
     static GtkType id = 0;
 
131
     if (!id) {
 
132
                GtkTypeInfo info = {
 
133
                     "Datasource",
 
134
                     sizeof(Datasource),
 
135
                     sizeof(DatasourceClass),
 
136
                     (GtkClassInitFunc) datasource_class_init,
 
137
                     (GtkObjectInitFunc) datasource_init 
 
138
                };
 
139
                id=gtk_type_unique(gtk_object_get_type(),&info);          
 
140
     }
 
141
     return id;
 
142
}
 
143
 
 
144
/* Creates a copy of the original datasource
 
145
 * If it refers to a file, create a DATASOURCE_REF instead.  
 
146
 */
 
147
 
 
148
static Datasource *datasource_copy(Datasource *orig)
 
149
{
 
150
     Datasource *ds;
 
151
 
 
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;
 
157
 
 
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));
 
167
          break;
 
168
     case DATASOURCE_REAL:
 
169
          ds->data.real = g_malloc(orig->bytes);
 
170
          memcpy(ds->data.real, orig->data.real, orig->bytes);
 
171
          break;
 
172
     case DATASOURCE_CLONE:
 
173
     case DATASOURCE_BYTESWAP:
 
174
     case DATASOURCE_REF:
 
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));
 
179
     }
 
180
     return ds;
 
181
}
 
182
 
 
183
Datasource *datasource_clone_df(Datasource *source, Dataformat *format)
 
184
{
 
185
     Datasource *df;
 
186
 
 
187
     g_assert(source->format.type == DATAFORMAT_PCM);
 
188
 
 
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;
 
196
          return df;
 
197
     }
 
198
 
 
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));
 
206
     return df;
 
207
}
 
208
 
 
209
Datasource *datasource_byteswap(Datasource *source)
 
210
{
 
211
     Datasource *ds;
 
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));
 
222
     return ds;
 
223
}
 
224
 
 
225
Datasource *datasource_new_silent(Dataformat *format, off_t samples)
 
226
{
 
227
     Datasource *ds;
 
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;
 
233
     return ds;
 
234
}
 
235
 
 
236
Datasource *datasource_new_from_data(void *data, Dataformat *format, 
 
237
                                     guint32 size)
 
238
{
 
239
     Datasource *ds;
 
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;
 
244
     ds->bytes = size;
 
245
     ds->data.real = data;
 
246
     return ds;
 
247
}
 
248
 
 
249
#define DUMP_BUFSIZE 65536
 
250
 
 
251
gboolean datasource_dump(Datasource *ds, off_t position, 
 
252
                         off_t length, EFILE *file, int dither_mode, 
 
253
                         StatusBar *bar)
 
254
{
 
255
     gchar *buf;
 
256
     off_t i;
 
257
     guint u;
 
258
     if (datasource_open(ds)) return TRUE;
 
259
     buf = g_malloc(DUMP_BUFSIZE);
 
260
     while (length > 0) {
 
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); 
 
265
               g_free(buf);
 
266
               return TRUE; 
 
267
          }
 
268
          length -= u/ds->format.samplebytes;
 
269
          position += u/ds->format.samplebytes;
 
270
     }
 
271
     datasource_close(ds);
 
272
     g_free(buf);
 
273
     return FALSE;
 
274
}
 
275
 
 
276
gboolean datasource_realize(Datasource *ds, int dither_mode)
 
277
{
 
278
     gchar *c;
 
279
     guint32 sz=ds->bytes;
 
280
 
 
281
     if (datasource_open(ds)) return TRUE;
 
282
 
 
283
     c = g_malloc(sz);
 
284
     if (datasource_read_array(ds,0,sz,c, dither_mode)) {
 
285
          g_free(c);
 
286
          datasource_close(ds);
 
287
          return TRUE;
 
288
     }
 
289
     datasource_close(ds);
 
290
     
 
291
     datasource_clear(ds);
 
292
     ds->type = DATASOURCE_REAL;
 
293
     ds->data.real = c;     
 
294
 
 
295
     return FALSE;
 
296
}
 
297
 
 
298
static gboolean datasource_open_sndfile(Datasource *ds)
 
299
{
 
300
#if defined(HAVE_LIBSNDFILE)
 
301
     SF_INFO i;
 
302
     SNDFILE *s;
 
303
     char *d;
 
304
     int fd;
 
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);
 
308
     if (s == NULL) {     
 
309
          d = g_strdup_printf(_("Couldn't open %s"),
 
310
                              ds->data.sndfile.filename);
 
311
          user_error(d);
 
312
          g_free(d);
 
313
          close(fd);
 
314
          return TRUE;
 
315
     }
 
316
 
 
317
     ds->data.sndfile.handle = s;
 
318
     ds->data.sndfile.pos = 0;
 
319
 
 
320
     sf_command ( s, SFC_SET_NORM_FLOAT, NULL, SF_TRUE );
 
321
     sf_command ( s, SFC_SET_NORM_DOUBLE, NULL, SF_TRUE );
 
322
     return FALSE;
 
323
#else
 
324
     g_assert_not_reached();
 
325
     return TRUE;
 
326
#endif     
 
327
}
 
328
 
 
329
static void datasource_close_sndfile(Datasource *ds)
 
330
{
 
331
#if defined(HAVE_LIBSNDFILE)
 
332
     sf_close(ds->data.sndfile.handle);
 
333
#else
 
334
     g_assert_not_reached();
 
335
#endif
 
336
}
 
337
 
 
338
gboolean datasource_open(Datasource *ds)
 
339
{
 
340
     if (ds->opencount == 0) 
 
341
          switch (ds->type) {
 
342
          case DATASOURCE_VIRTUAL:
 
343
          case DATASOURCE_TEMPFILE:
 
344
               ds->data.virtual.handle = e_fopen(ds->data.virtual.filename,
 
345
                                                 EFILE_READ);
 
346
               if (ds->data.virtual.handle == NULL) return TRUE;
 
347
               ds->data.virtual.pos = 0;
 
348
               break;
 
349
          case DATASOURCE_SNDFILE:
 
350
          case DATASOURCE_SNDFILE_TEMPORARY:
 
351
               if (datasource_open_sndfile(ds)) return TRUE;
 
352
               break;
 
353
          case DATASOURCE_REF:
 
354
          case DATASOURCE_CLONE:
 
355
          case DATASOURCE_BYTESWAP:            
 
356
          case DATASOURCE_CONVERT:
 
357
               if (datasource_open(ds->data.clone)) return TRUE;
 
358
          }     
 
359
     ds->opencount ++;
 
360
     return FALSE;
 
361
}
 
362
 
 
363
void datasource_close(Datasource *ds)
 
364
{
 
365
     g_assert(ds->opencount != 0);
 
366
     ds->opencount --;
 
367
     if (ds->opencount > 0) return;
 
368
     switch (ds->type) {
 
369
     case DATASOURCE_VIRTUAL:
 
370
     case DATASOURCE_TEMPFILE:
 
371
          e_fclose(ds->data.virtual.handle);
 
372
          break;
 
373
     case DATASOURCE_SNDFILE:
 
374
     case DATASOURCE_SNDFILE_TEMPORARY:
 
375
          datasource_close_sndfile(ds);
 
376
          break;
 
377
     case DATASOURCE_REF:
 
378
     case DATASOURCE_CLONE:
 
379
     case DATASOURCE_BYTESWAP:
 
380
     case DATASOURCE_CONVERT:
 
381
          datasource_close(ds->data.clone);
 
382
          break;
 
383
     }     
 
384
}
 
385
 
 
386
static guint datasource_clone_read_array(Datasource *source, off_t sampleno,
 
387
                                         guint size, gpointer buffer, 
 
388
                                         int dither_mode)
 
389
{
 
390
     /* This is not optimized but it's only used in rare cases so the important
 
391
      * thing is that it works.*/
 
392
     off_t orig_offset;
 
393
     off_t orig_sampleno;
 
394
     off_t orig_size;
 
395
     off_t orig_adjust;
 
396
     guint x;
 
397
     gchar *p;
 
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,
 
408
                               dither_mode);
 
409
     if (x != 0) {
 
410
          g_assert(x-orig_adjust >= size);
 
411
          memcpy(buffer, p+orig_adjust, size);
 
412
          x = size;
 
413
     }
 
414
     g_free(p);
 
415
     return x;                         
 
416
}
 
417
 
 
418
#if defined(HAVE_LIBSNDFILE)
 
419
static void sndfile_read_error(sf_count_t x, Datasource *source)
 
420
{
 
421
     gchar c[256],*d;
 
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);
 
426
     user_error(d);
 
427
     g_free(d);
 
428
}
 
429
#endif
 
430
 
 
431
static guint datasource_sndfile_read_array_pcm(Datasource *source, 
 
432
                                               off_t sampleno, guint size,
 
433
                                               gpointer buffer)
 
434
{
 
435
#if defined(HAVE_LIBSNDFILE)
 
436
     gchar c[256],*d;
 
437
     sf_count_t x;
 
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);
 
444
               user_error(d);
 
445
               g_free(d);
 
446
               return 0;
 
447
          }
 
448
          source->data.sndfile.pos = sampleno;
 
449
     }
 
450
     if (source->data.sndfile.raw_readable) {
 
451
          /* Read raw */
 
452
          x = sf_read_raw(source->data.sndfile.handle,buffer,size);       
 
453
          if (x>0) source->data.sndfile.pos += x/source->format.samplebytes;
 
454
          if (x<size) {
 
455
               sndfile_read_error(x,source);
 
456
               return 0;
 
457
          }
 
458
          return x;
 
459
     } else if (source->format.samplesize==2 && source->format.sign) {
 
460
          /* Read as short */
 
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);
 
466
               return 0;
 
467
          }
 
468
          return x * source->format.samplebytes;
 
469
     } else {     
 
470
          g_assert_not_reached();
 
471
          return 0;
 
472
     } 
 
473
#else
 
474
     g_assert_not_reached();
 
475
     return 0;
 
476
#endif
 
477
}
 
478
 
 
479
static guint datasource_sndfile_read_array_fp(Datasource *source, 
 
480
                                              off_t sampleno, guint samples,
 
481
                                              sample_t *buffer)
 
482
{
 
483
#if defined(HAVE_LIBSNDFILE)
 
484
     gchar c[256],*d;
 
485
     sf_count_t x;
 
486
 
 
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);
 
492
               user_error(d);
 
493
               g_free(d);
 
494
               return 0;
 
495
          }
 
496
          source->data.sndfile.pos = sampleno;
 
497
     }
 
498
 
 
499
     x = sf_readf_sample_t(source->data.sndfile.handle,buffer,samples);
 
500
     if (x>0) source->data.sndfile.pos += x;
 
501
     if (x<samples) {
 
502
          sndfile_read_error(x,source);
 
503
          return 0;                 
 
504
     }
 
505
     return x;
 
506
 
 
507
#else
 
508
     g_assert_not_reached();
 
509
     return 0;
 
510
#endif
 
511
}
 
512
 
 
513
static guint datasource_read_array_main(Datasource *source, 
 
514
                                        off_t sampleno, guint size, 
 
515
                                        gpointer buffer, int dither_mode)
 
516
{
 
517
     off_t x;
 
518
     guint u;
 
519
     gchar *c;
 
520
     switch (source->type) {
 
521
     case DATASOURCE_REAL:
 
522
          memcpy(buffer,source->data.real+sampleno*source->format.samplebytes,
 
523
                 size);
 
524
          return size;
 
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))
 
532
               return 0;
 
533
          source->data.virtual.pos = x;
 
534
          if (e_fread(buffer,size,source->data.virtual.handle)) return 0;
 
535
          source->data.virtual.pos += size;
 
536
          return size;
 
537
     case DATASOURCE_SILENCE:
 
538
          memset(buffer,0,size);
 
539
          return 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,
 
544
                                                        buffer);
 
545
          else
 
546
               return datasource_sndfile_read_array_fp
 
547
                    (source,sampleno,size/source->format.samplebytes,buffer);
 
548
     case DATASOURCE_REF:
 
549
          return datasource_read_array_main(source->data.clone,sampleno,size,
 
550
                                            buffer,dither_mode);
 
551
     case DATASOURCE_CLONE:
 
552
          return datasource_clone_read_array(source,sampleno,size,buffer,
 
553
                                             dither_mode);
 
554
     case DATASOURCE_BYTESWAP:
 
555
          u = datasource_read_array_main(source->data.clone,sampleno,size,
 
556
                                         buffer,dither_mode);
 
557
          if (u>0) byteswap(buffer,source->format.samplesize,u);
 
558
          return 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);
 
569
          if (u > 0) {
 
570
               convert_array(c,&dataformat_sample_t,buffer,&(source->format),
 
571
                             u*source->format.channels,dither_mode);
 
572
          }
 
573
          g_free(c);
 
574
          return u * source->format.samplebytes;
 
575
     default:
 
576
          g_assert_not_reached();
 
577
          return 0;
 
578
     }
 
579
}
 
580
 
 
581
guint datasource_read_array(Datasource *source, off_t sampleno, guint size,
 
582
                            gpointer buffer, int dither_mode)
 
583
{
 
584
     off_t o;
 
585
     g_assert(source->opencount > 0);
 
586
     /* Check sampleno */
 
587
     g_assert(sampleno <= source->length);     
 
588
     /* Check size */
 
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;
 
594
     /* Do it */
 
595
     return datasource_read_array_main(source,sampleno,size,buffer,
 
596
                                       dither_mode);
 
597
}
 
598
 
 
599
gboolean datasource_read(Datasource *source, off_t sampleno, gpointer buffer, 
 
600
                         int dither_mode)
 
601
{
 
602
     return (datasource_read_array(source,sampleno,source->format.samplebytes,
 
603
                                   buffer,dither_mode) 
 
604
             != source->format.samplebytes);
 
605
}
 
606
 
 
607
 
 
608
guint datasource_read_array_fp(Datasource *source, off_t sampleno,
 
609
                               guint samples, sample_t *buffer, 
 
610
                               int dither_mode)
 
611
{
 
612
     gchar *p;
 
613
     guint x,s;
 
614
     
 
615
     g_assert(sampleno <= source->length);
 
616
     if (samples > source->length-sampleno) 
 
617
          samples = (guint)(source->length-sampleno);
 
618
     if (samples == 0) return 0;
 
619
 
 
620
 
 
621
     switch (source->type) {
 
622
     case DATASOURCE_SILENCE:
 
623
          memset(buffer,0,samples*source->format.channels*sizeof(sample_t));
 
624
          return samples;
 
625
     case DATASOURCE_SNDFILE:
 
626
     case DATASOURCE_SNDFILE_TEMPORARY:
 
627
       return datasource_sndfile_read_array_fp(source,sampleno,samples,
 
628
                                               buffer);
 
629
     case DATASOURCE_REF:
 
630
     case DATASOURCE_CONVERT:
 
631
          return datasource_read_array_fp(source->data.clone,sampleno,
 
632
                                          samples,buffer,dither_mode);
 
633
     default:
 
634
          if (source->format.type == DATAFORMAT_FLOAT &&
 
635
              source->format.samplesize == sizeof(sample_t))
 
636
               
 
637
               return datasource_read_array(source,sampleno,
 
638
                                            samples*source->format.samplebytes,
 
639
                                            buffer, dither_mode) 
 
640
                    / source->format.samplebytes;
 
641
               
 
642
          s = samples * source->format.samplebytes;
 
643
          p = g_malloc(s);
 
644
          x = datasource_read_array(source,sampleno,s,p,dither_mode);
 
645
          g_assert(x==s || x==0);
 
646
          if (x==s) {
 
647
               convert_array(p,&(source->format),buffer,&dataformat_sample_t,
 
648
                             samples*source->format.channels,dither_mode);
 
649
               g_free(p);
 
650
               return samples;
 
651
          }
 
652
          g_free(p);
 
653
          return 0;
 
654
     }
 
655
}
 
656
 
 
657
gboolean datasource_read_fp(Datasource *ds, off_t sampleno, sample_t *buffer,
 
658
                            int dither_mode)
 
659
{
 
660
     return (datasource_read_array_fp(ds,sampleno,1,buffer,dither_mode)!=1);
 
661
}
 
662
 
 
663
static gboolean datasource_uses_file(Datasource *ds, gchar *filename)
 
664
{
 
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))));
 
669
}
 
670
 
 
671
gboolean datasource_backup_unlink(gchar *filename)
 
672
{
 
673
     GList *l,*q=NULL;
 
674
     Datasource *ds;
 
675
     Datasource *backup=NULL;
 
676
     gchar *lastname=filename,*t;
 
677
     guint dirnum=0;
 
678
     gint i;
 
679
 
 
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);
 
685
     }
 
686
     
 
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;
 
691
 
 
692
          /* FIXME: When multiple tempdir support has been added, try moving to
 
693
           * all different partitions before copying */
 
694
          if (backup == NULL) {
 
695
               dirnum = 0;
 
696
               while (1) {
 
697
                    t = get_temp_filename(dirnum);
 
698
                    if (t == NULL) {
 
699
                         dirnum = 0;
 
700
                         t = get_temp_filename(0);
 
701
                         i = xrename(lastname,t,TRUE);
 
702
                         break;
 
703
                    }
 
704
                    i = xrename(lastname,t,FALSE);
 
705
                    if (i!=2) break;
 
706
                    dirnum ++;
 
707
                    g_free(t);
 
708
               } 
 
709
               if (i) {
 
710
                    g_free(t);
 
711
                    return TRUE;
 
712
               }                     
 
713
          } else {
 
714
               /* Special case: the same file has been opened many times. */
 
715
               t = get_temp_filename(0);
 
716
               if (errdlg_copyfile(lastname,t)) {
 
717
                    g_free(t);
 
718
                    return TRUE;
 
719
               }
 
720
          }
 
721
                    
 
722
          switch (ds->type) {
 
723
          case DATASOURCE_VIRTUAL:
 
724
               g_free(ds->data.virtual.filename);
 
725
               ds->data.virtual.filename = t;
 
726
               ds->type = DATASOURCE_TEMPFILE;
 
727
               break;
 
728
          case DATASOURCE_SNDFILE:
 
729
               g_free(ds->data.sndfile.filename);
 
730
               ds->data.sndfile.filename = t;
 
731
               ds->type = DATASOURCE_SNDFILE_TEMPORARY;
 
732
               break;
 
733
          default: 
 
734
               g_assert_not_reached(); 
 
735
               break;
 
736
          }       
 
737
          backup = ds;
 
738
          lastname = t;
 
739
     }
 
740
 
 
741
     g_list_free(q);
 
742
     return xunlink(filename);
 
743
}
 
744
 
 
745
Datasource *datasource_convert(Datasource *source, Dataformat *new_format)
 
746
{    
 
747
     Datasource *ds;
 
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);
 
752
 
 
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);
 
758
     }
 
759
 
 
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));
 
768
     return ds;     
 
769
}