sound.c 37.5 KB
Newer Older
1
#include "mus-config.h"
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

#if USE_SND
  #include "snd.h"
#endif

#include <math.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <stdarg.h>

17 18
#ifndef _MSC_VER
  #include <unistd.h>
19
#else
20 21
  #include <io.h>
  #pragma warning(disable: 4244)
22
#endif
23
#include <string.h>
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

#include "_sndlib.h"
#include "sndlib-strings.h"


static mus_error_handler_t *mus_error_handler = NULL;

mus_error_handler_t *mus_error_set_handler(mus_error_handler_t *new_error_handler)
{
  mus_error_handler_t *old_handler;
  old_handler = mus_error_handler;
  mus_error_handler = new_error_handler;
  return(old_handler);
}


static char *mus_error_buffer = NULL;
static int mus_error_buffer_size = 1024;

int mus_error(int error, const char *format, ...)
{
  int bytes_needed = 0;
  va_list ap;

48
  if (!format) 
49 50
    return(MUS_ERROR); /* else bus error in Mac OSX */

51
  if (!mus_error_buffer)
52 53 54
    mus_error_buffer = (char *)calloc(mus_error_buffer_size, sizeof(char));

  va_start(ap, format);
55 56 57 58

  /* can't use vasprintf here or below because the error handler may jump anywhere,
   *   leaving unfreed memory behind.
   */
59 60 61
  bytes_needed = vsnprintf(mus_error_buffer, mus_error_buffer_size, format, ap);
  va_end(ap);

62
  if (bytes_needed >= mus_error_buffer_size)
63 64 65 66
    {
      mus_error_buffer_size = bytes_needed * 2;
      free(mus_error_buffer);
      mus_error_buffer = (char *)calloc(mus_error_buffer_size, sizeof(char));
67

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
      va_start(ap, format);
      vsnprintf(mus_error_buffer, mus_error_buffer_size, format, ap);
      va_end(ap);
    }

  if (mus_error_handler)
    (*mus_error_handler)(error, mus_error_buffer);
  else 
    {
      fprintf(stderr, "%s", mus_error_buffer);
      fputc('\n', stderr);
    }

  return(MUS_ERROR);
}


static mus_print_handler_t *mus_print_handler = NULL;

mus_print_handler_t *mus_print_set_handler(mus_print_handler_t *new_print_handler) 
{
  mus_print_handler_t *old_handler;
  old_handler = mus_print_handler;
  mus_print_handler = new_print_handler;
  return(old_handler);
}


void mus_print(const char *format, ...)
{
  va_list ap;

  if (mus_print_handler)
    {
      int bytes_needed = 0;

104
      if (!mus_error_buffer)
105 106 107 108 109
	mus_error_buffer = (char *)calloc(mus_error_buffer_size, sizeof(char));

      va_start(ap, format);
      bytes_needed = vsnprintf(mus_error_buffer, mus_error_buffer_size, format, ap);
      va_end(ap);
110

111
      if (bytes_needed >= mus_error_buffer_size)
112 113 114 115
	{
	  mus_error_buffer_size = bytes_needed * 2;
	  free(mus_error_buffer);
	  mus_error_buffer = (char *)calloc(mus_error_buffer_size, sizeof(char));
116

117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
	  va_start(ap, format);
	  vsnprintf(mus_error_buffer, mus_error_buffer_size, format, ap);
	  va_end(ap);
	}

      (*mus_print_handler)(mus_error_buffer);
    }
  else
    {
      va_start(ap, format);
      vfprintf(stdout, format, ap);
      va_end(ap);
    }
}


133
static const char *mus_error_names[MUS_NUM_ERRORS] = {
134
  "no error", "no frequency method", "no phase method", "null gen arg to method", "no length method",
135 136
  "no describe method", "no data method", "no scaler method",
  "memory allocation failed", 
137 138
  "can't open file", "no sample input", "no sample output",
  "no such channel", "no file name provided", "no location method", "no channel method",
139 140
  "no such fft window", "unknown sample type", "header read failed",
  "unknown header type", "file descriptors not initialized", "not a sound file", "file closed", "write error",
141 142
  "header write failed", "can't open temp file", "interrupted", "bad envelope",

143
  "audio channels not available", "audio srate not available", "audio sample type not available",
144
  "no audio input available", "audio configuration not available", 
145 146
  "audio write error", "audio size not available", "audio device not available",
  "can't close audio", "can't open audio", "audio read error",
147
  "can't write audio", "can't read audio", "no audio read permission", 
148
  "can't close file", "arg out of range", 
149 150 151 152

  "no channels method", "no hop method", "no width method", "no file-name method", "no ramp method", "no run method",
  "no increment method", "no offset method",
  "no xcoeff method", "no ycoeff method", "no xcoeffs method", "no ycoeffs method", "no reset", "bad size", "can't convert",
153 154 155
  "read error",
  "no feedforward method", "no feedback method", "no interp-type method", "no position method", "no order method", "no copy method",
  "can't translate"
156 157 158 159 160
};


const char *mus_error_type_to_string(int err)
{
161 162 163
  if ((err >= 0) &&
      (err < MUS_NUM_ERRORS))
    return(mus_error_names[err]);
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
  return("unknown mus error");
}


static void default_mus_error(int ignore, char *msg)
{
  /* default error handler simply prints the error message */
  fprintf(stderr, "%s", msg);
}


static time_t local_file_write_date(const char *filename)
{
  struct stat statbuf;
  int err;
  err = stat(filename, &statbuf);
  if (err < 0) return((time_t)0);
  return((time_t)(statbuf.st_mtime));
}



/* -------- sound file table -------- */

188
typedef struct sound_file {
189
  char *file_name;  /* full path -- everything is keyed to this name */
190
  int table_pos, file_name_length, table_index;
191 192 193 194 195 196
  mus_long_t *aux_comment_start, *aux_comment_end;
  int *loop_modes, *loop_starts, *loop_ends;
  int markers, base_detune, base_note;
  int *marker_ids, *marker_positions;
  mus_long_t samples, true_file_length;
  mus_long_t data_location;
197 198 199
  int srate, chans, original_sound_samp_type, datum_size; 
  mus_header_t header_type;
  mus_sample_t sample_type;
200 201 202
  mus_long_t comment_start, comment_end;
  int type_specifier, bits_per_sample, block_align, fact_samples;
  time_t write_date;
203
  mus_float_t *maxamps;
204
  mus_long_t *maxtimes;
205 206 207
  int maxamps_size; /* we can't depend on sf->chans here because the user could set chans to some bogus value */
  mus_float_t **saved_data;
  struct sound_file *next;
208 209
} sound_file;

210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
static int *sound_table_sizes = NULL;
static sound_file ***sound_tables = NULL;
#define NUM_SOUND_TABLES 64

/* it's not enough to hash on the file name length -- they're nearly all the same length!
 *  I think I'll try taking the last few chars instead (the first 15-20 chars are
 *  also always the same: the tmp directory, eg: /home/bil/zap/tmp/snd_16687_632.snd).
 */

static int sound_file_hash_index(const char *name, int len)
{
  unsigned char *s;
  if (!name) return(0);
  if (len < 8) return(len);
  s = (unsigned char *)(name + len - 8);
225
  return((int)(s[0] + s[1] + s[2] + s[3]) % (int)NUM_SOUND_TABLES);
226 227
}

228

229 230 231
void scan_io_fds_for_saved_data(mus_float_t **data);

static sound_file *sf_free_list = NULL;
232 233 234 235 236

static void free_sound_file(sound_file *sf)
{
  if (sf)
    {
237
      sound_tables[sf->table_index][sf->table_pos] = NULL;
238 239 240 241 242 243 244 245 246 247
      if (sf->aux_comment_start) free(sf->aux_comment_start);
      if (sf->aux_comment_end) free(sf->aux_comment_end);
      if (sf->file_name) free(sf->file_name);
      if (sf->loop_modes) free(sf->loop_modes);
      if (sf->loop_starts) free(sf->loop_starts);
      if (sf->loop_ends) free(sf->loop_ends);
      if (sf->marker_ids) free(sf->marker_ids);
      if (sf->marker_positions) free(sf->marker_positions);
      if (sf->maxamps) free(sf->maxamps);
      if (sf->maxtimes) free(sf->maxtimes);
248 249 250 251 252 253 254 255 256 257 258 259 260 261
      sf->maxamps_size = 0;
      if (sf->saved_data)
	{
	  int i;
	  scan_io_fds_for_saved_data(sf->saved_data);
	  for (i = 0; i < sf->chans; i++)
	    if (sf->saved_data[i])
	      free(sf->saved_data[i]);
	  free(sf->saved_data);
	  sf->saved_data = NULL;
	}
      /* free(sf); */
      sf->next = sf_free_list;
      sf_free_list = sf;
262 263 264 265 266
    }
}

static sound_file *add_to_sound_table(const char *name)
{
267 268 269
  int i, len, pos = -1, index, sound_table_size;
  sound_file **sound_table;
  sound_file *sf;
270

271 272 273 274 275
  len = strlen(name);
  index = sound_file_hash_index(name, len);
  
  sound_table = sound_tables[index];
  sound_table_size = sound_table_sizes[index];
276 277

  for (i = 0; i < sound_table_size; i++)
278
    if (!sound_table[i]) 
279 280 281 282 283 284 285 286 287
      {
	pos = i;
	break;
      }

  if (pos == -1)
    {
      pos = sound_table_size;
      sound_table_size += 16;
288
      if (!sound_table)
289 290 291
	{
	  sound_table = (sound_file **)calloc(sound_table_size, sizeof(sound_file *));
	}
292 293 294 295 296
      else 
	{
	  sound_table = (sound_file **)realloc(sound_table, sound_table_size * sizeof(sound_file *));
	  for (i = pos; i < sound_table_size; i++) sound_table[i] = NULL;
	}
297 298
      sound_tables[index] = sound_table;
      sound_table_sizes[index] = sound_table_size;
299 300
    }

301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
  if (sf_free_list)
    {
      sf = sf_free_list;
      sf_free_list = sf->next;
      memset((void *)sf, 0, sizeof(sound_file));
    }
  else sf = (sound_file *)calloc(1, sizeof(sound_file));
  sound_table[pos] = sf;
  sf->table_pos = pos;
  sf->table_index = index;
  sf->file_name = (char *)malloc((len + 1) * sizeof(char));
  strcpy(sf->file_name, name);
  sf->file_name[len] = 0;
  sf->file_name_length = len;
  sf->saved_data = NULL;
  return(sf);
317 318 319 320 321
}


int mus_sound_prune(void)
{
322
  int j, pruned = 0;
323

324 325 326 327
  for (j = 0; j < NUM_SOUND_TABLES; j++)
    {
      int i, sound_table_size;
      sound_file **sound_table;
328

329 330
      sound_table = sound_tables[j];
      sound_table_size = sound_table_sizes[j];
331

332 333 334 335 336 337 338 339 340
      for (i = 0; i < sound_table_size; i++)
	if ((sound_table[i]) && 
	    (!(mus_file_probe(sound_table[i]->file_name))))
	  {
	    free_sound_file(sound_table[i]);
	    sound_table[i] = NULL;
	    pruned++;
	  }
    }
341 342 343 344 345 346 347

  return(pruned);
}


int mus_sound_forget(const char *name)
{
348 349
  /* apparently here we want to forget either name or the expanded or contracted forms of name -- as many as we find! */
  int i, len, len2, short_len = 0;
350 351
  bool free_name = false;
  char *short_name = NULL;
352 353 354 355
  sound_file **sound_table;
  int sound_table_size, index;
  char c;

356
  if (!name) return(MUS_ERROR);
357 358 359 360 361 362
  len = strlen(name);
  if (len > 6)
    len2 = len - 6;
  else len2 = len / 2;
  c = name[len2];

363 364 365 366 367 368 369 370 371 372 373
  if (name[0] == '/')
    {
      for (i = 0; i < len; i++)
	if (name[i] == '/')
	  short_name = (char *)(name + i + 1);
    }
  else
    {
      short_name = mus_expand_filename(name);
      free_name = true;
    }
374 375
  if (short_name) 
    short_len = strlen(short_name);
376

377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
  index = sound_file_hash_index(name, len);
  sound_table = sound_tables[index];
  sound_table_size = sound_table_sizes[index];

  for (i = 0; i < sound_table_size; i++)
    if ((sound_table[i]) &&
	(sound_table[i]->file_name_length == len) &&
	(sound_table[i]->file_name[len2] == c) &&
	(mus_strcmp(name, sound_table[i]->file_name)))
      {
	free_sound_file(sound_table[i]);
	sound_table[i] = NULL;
      }
  
  if (short_name)
392
    {
393 394 395 396 397 398 399 400
      if (short_len > 6)
	len2 = short_len - 6;
      else len2 = short_len / 2;
      c = short_name[len2];

      index = sound_file_hash_index(short_name, short_len);
      sound_table = sound_tables[index];
      sound_table_size = sound_table_sizes[index];
401 402 403

      for (i = 0; i < sound_table_size; i++)
	if ((sound_table[i]) &&
404 405 406
	    (sound_table[i]->file_name_length == short_len) &&
	    (sound_table[i]->file_name[len2] == c) &&
	    (mus_strcmp(short_name, sound_table[i]->file_name)))
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
	  {
	    free_sound_file(sound_table[i]);
	    sound_table[i] = NULL;
	  }
    }
  
  if (free_name) free(short_name);
  return(MUS_NO_ERROR);
}


static sound_file *check_write_date(const char *name, sound_file *sf)
{
  if (sf)
    {
      time_t date;
      date = local_file_write_date(name);
      if (date == sf->write_date)
	return(sf);
426 427

      if ((sf->header_type == MUS_RAW) && (mus_header_no_header(name)))
428
	{
429 430 431 432 433 434 435 436 437 438 439 440 441 442
	  int chan;
	  mus_long_t data_size;
	  /* sound has changed since we last read it, but it has no header, so
	   * the only sensible thing to check is the new length (i.e. caller
	   * has set other fields by hand)
	   */
	  sf->write_date = date;
	  chan = mus_file_open_read(name);
	  data_size = lseek(chan, 0L, SEEK_END);
	  sf->true_file_length = data_size;
	  sf->samples = mus_bytes_to_samples(sf->sample_type, data_size);
	  CLOSE(chan, name);  

	  return(sf);
443
	}
444 445
      /* otherwise our data base is out-of-date, so clear it out */
      free_sound_file(sf);
446 447 448 449 450 451 452
    }
  return(NULL);
}


static sound_file *find_sound_file(const char *name)
{
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
  /* assume name != NULL */
  int i, len, len2;
  sound_file **sound_table;
  int sound_table_size, index;
  char c;

  len = strlen(name);
  if (len > 6)
    len2 = len - 6; /* the names probably all start with '/' and end with ".snd", so try to find a changing character... */
  else len2 = len / 2;
  c = name[len2];
  
  index = sound_file_hash_index(name, len);
  sound_table = sound_tables[index];
  sound_table_size = sound_table_sizes[index];
468

469 470 471 472 473 474 475 476 477 478
  for (i = 0; i < sound_table_size; i++)  
    {
      sound_file *sf;
      sf = sound_table[i];
      if ((sf) &&
	  (sf->file_name_length == len) &&
	  (c == sf->file_name[len2]) &&
	  (strcmp(name, sf->file_name) == 0))
	return(check_write_date(name, sf));
    }
479 480 481 482 483 484
  return(NULL);
}


static void display_sound_file_entry(FILE *fp, const char *name, sound_file *sf)
{
485
  #define TIME_BUFFER_SIZE 64
486
  time_t date;
487
  char timestr[TIME_BUFFER_SIZE];
488 489 490

  date = sf->write_date;
  if (date != 0)
491
    strftime(timestr, TIME_BUFFER_SIZE, "%a %d-%b-%Y %H:%M:%S", localtime(&date));
492
  else snprintf(timestr, TIME_BUFFER_SIZE, "(date cleared)");
493

494
  fprintf(fp, "  %s: %s, chans: %d, srate: %d, header: %s, data: %s, samps: %" print_mus_long,
495 496 497 498 499
	  name,
	  timestr,
	  sf->chans,
	  sf->srate,
	  mus_header_type_name(sf->header_type),
500
	  mus_sample_type_name(sf->sample_type),
501 502 503 504 505 506 507 508 509 510 511 512 513
	  sf->samples);

  if (sf->loop_modes)
    {
      if (sf->loop_modes[0] != 0)
	fprintf(fp, ", loop mode %d: %d to %d", sf->loop_modes[0], sf->loop_starts[0], sf->loop_ends[0]);
      if (sf->loop_modes[1] != 0)
	fprintf(fp, ", loop mode %d: %d to %d, ", sf->loop_modes[1], sf->loop_starts[1], sf->loop_ends[1]);
      fprintf(fp, ", base: %d, detune: %d", sf->base_note, sf->base_detune);
    }

  if (sf->maxamps)
    {
514 515
      int lim;
      lim = sf->maxamps_size;
516 517
      if (lim > 0)
	{
518
	  int i;
519 520
	  if (lim > 64) 
	    lim = 64;
521
	  fprintf(fp, ", maxamp:");
522 523 524 525
	  for (i = 0; i < lim; i++)
	    {
	      if (i > 1) fprintf(fp, ", ");
	      fprintf(fp, " %.3f at %.3f ",
526
		      sf->maxamps[i],
527
		      (sf->srate > 0) ? ((double)(sf->maxtimes[i]) / (double)(sf->srate)) : (double)(sf->maxtimes[i]));
528 529 530 531 532 533
	    }
	}
    }

  if (mus_file_probe(name))
    {
534
      char *comment;
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
      comment = mus_sound_comment(name);
      if (comment)
	{
	  fprintf(fp, "\n      comment: %s", comment);
	  free(comment);
	}
    }
  else fprintf(fp, " [defunct]");
  fprintf(fp, "\n");
}


void mus_sound_report_cache(FILE *fp)
{
  sound_file *sf;
  int entries = 0;
551 552 553 554 555
  int i, j;
  sound_file **sound_table;

  fprintf(fp, "sound table:");
  for (j = 0; j < NUM_SOUND_TABLES; j++)
556
    {
557 558 559 560 561
      int sound_table_size;
      sound_table = sound_tables[j];
      sound_table_size = sound_table_sizes[j];

      for (i = 0; i < sound_table_size; i++)
562
	{
563 564 565 566 567 568 569
	  sf = sound_table[i];
	  if (sf) 
	    {
	      if (entries == 0) fprintf(fp, "\n");
	      display_sound_file_entry(fp, sf->file_name, sf);
	      entries++;
	    }
570 571
	}
    }
572 573 574
  if (entries > 0)
    fprintf(fp, "\nentries: %d\n", entries); 
  else fprintf(fp, " empty");
575 576 577 578 579 580 581 582 583 584
  fflush(fp);
}


static sound_file *fill_sf_record(const char *name, sound_file *sf)
{
  int i;

  sf->data_location = mus_header_data_location();
  sf->samples = mus_header_samples();
585
  sf->sample_type = mus_header_sample_type();
586 587 588 589
  sf->srate = mus_header_srate();
  /* if (sf->srate < 0) sf->srate = 0; */
  sf->chans = mus_header_chans();
  /* if (sf->chans < 0) sf->chans = 0; */
590
  sf->datum_size = mus_bytes_per_sample(sf->sample_type);
591
  sf->header_type = mus_header_type();
592
  sf->original_sound_samp_type = mus_header_original_sample_type();
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
  sf->true_file_length = mus_header_true_length();

  sf->comment_start = mus_header_comment_start();
  sf->comment_end = mus_header_comment_end();
  if (((sf->header_type == MUS_AIFC) || 
       (sf->header_type == MUS_AIFF) || 
       (sf->header_type == MUS_RF64) || 
       (sf->header_type == MUS_RIFF)) &&
      (mus_header_aux_comment_start(0) != 0))

    {
      sf->aux_comment_start = (mus_long_t *)calloc(4, sizeof(mus_long_t));
      sf->aux_comment_end = (mus_long_t *)calloc(4, sizeof(mus_long_t));
      for (i = 0; i < 4; i++)
	{
	  sf->aux_comment_start[i] = mus_header_aux_comment_start(i);
	  sf->aux_comment_end[i] = mus_header_aux_comment_end(i);
	}
    }

  sf->type_specifier = mus_header_type_specifier();
  sf->bits_per_sample = mus_header_bits_per_sample();
  sf->fact_samples = mus_header_fact_samples();
  sf->block_align = mus_header_block_align();
  sf->write_date = local_file_write_date(name);

  if ((sf->header_type == MUS_AIFF) || (sf->header_type == MUS_AIFC))
    {
      int *marker_ids, *marker_positions;
      sf->markers = mus_header_mark_info(&marker_ids, &marker_positions);
      if (sf->markers > 0)
	{
	  sf->marker_ids = (int *)malloc(sf->markers * sizeof(int));
	  sf->marker_positions = (int *)malloc(sf->markers * sizeof(int));
	  memcpy((void *)(sf->marker_ids), (void *)marker_ids, sizeof(int) * sf->markers);
	  memcpy((void *)(sf->marker_positions), (void *)marker_positions, sizeof(int) * sf->markers);
	}
    }

  if (mus_header_loop_mode(0) > 0)
    {
      sf->loop_modes = (int *)calloc(2, sizeof(int));
      sf->loop_starts = (int *)calloc(2, sizeof(int));
      sf->loop_ends = (int *)calloc(2, sizeof(int));
      for (i = 0; i < 2; i++)
	{
	  sf->loop_modes[i] = mus_header_loop_mode(i);
	  if ((sf->header_type == MUS_AIFF) || 
	      (sf->header_type == MUS_AIFC))
	    {
	      sf->loop_starts[i] = mus_header_mark_position(mus_header_loop_start(i)); 
	      sf->loop_ends[i] = mus_header_mark_position(mus_header_loop_end(i));
	    }
	  else
	    {
	      sf->loop_starts[i] = mus_header_loop_start(i); 
	      sf->loop_ends[i] = mus_header_loop_end(i);
	    }
	}
      sf->base_detune = mus_header_base_detune();
      sf->base_note = mus_header_base_note();
    }

  return(sf);
}


static sound_file *read_sound_file_header(const char *name) /* 2 calls on this: mus_sound_open_input and get_sf */
{
  int result;
  mus_sound_initialize();
  result = mus_header_read(name);
  /* this portion won't trigger mus_error */
  if (result != MUS_ERROR)
    return(fill_sf_record(name, add_to_sound_table(name))); /* only call on fill_sf_record and add_to_sound_table */
  return(NULL);
}

static sound_file *get_sf(const char *arg) 
{
  sound_file *sf = NULL;
674
  if (!arg) return(NULL);
675
  sf = find_sound_file(arg);
676
  return((sf) ? sf : read_sound_file_header(arg));
677 678 679 680 681 682 683
}


mus_long_t mus_sound_samples(const char *arg)       
{
  sound_file *sf;
  sf = get_sf(arg);
684
  return((sf) ? sf->samples : (mus_long_t)MUS_ERROR);
685 686 687
}


688
mus_long_t mus_sound_framples(const char *arg)        
689 690 691
{
  sound_file *sf;
  sf = get_sf(arg);
692
  return((sf) ? ((sf->chans > 0) ? (sf->samples / sf->chans) : 0) : (mus_long_t)MUS_ERROR);
693 694 695 696 697 698 699
}


int mus_sound_datum_size(const char *arg)      
{
  sound_file *sf;
  sf = get_sf(arg);
700
  return((sf) ? sf->datum_size : MUS_ERROR);
701 702 703 704 705 706 707
}


mus_long_t mus_sound_data_location(const char *arg) 
{
  sound_file *sf;
  sf = get_sf(arg);
708
  return((sf) ? sf->data_location : MUS_ERROR);
709 710 711 712 713 714 715
}


int mus_sound_chans(const char *arg)           
{
  sound_file *sf;
  sf = get_sf(arg);
716
  return((sf) ? sf->chans : MUS_ERROR);
717 718 719 720 721 722 723
}


int mus_sound_srate(const char *arg)           
{
  sound_file *sf;
  sf = get_sf(arg);
724
  return((sf) ? sf->srate : MUS_ERROR);
725 726 727
}


728
mus_header_t mus_sound_header_type(const char *arg)     
729 730 731
{
  sound_file *sf;
  sf = get_sf(arg);
732
  return((sf) ? sf->header_type : MUS_UNKNOWN_HEADER);
733 734 735
}


736
mus_sample_t mus_sound_sample_type(const char *arg)     
737 738 739
{
  sound_file *sf;
  sf = get_sf(arg);
740
  return((sf) ? sf->sample_type : MUS_UNKNOWN_SAMPLE);
741 742 743
}


744
int mus_sound_original_sample_type(const char *arg) 
745 746 747
{
  sound_file *sf;
  sf = get_sf(arg);
748
  return((sf) ? sf->original_sound_samp_type : MUS_ERROR);
749 750 751 752 753 754 755
}


mus_long_t mus_sound_comment_start(const char *arg) 
{
  sound_file *sf;
  sf = get_sf(arg);
756
  return((sf) ? sf->comment_start : (mus_long_t)MUS_ERROR);
757 758 759 760 761 762 763
}


mus_long_t mus_sound_comment_end(const char *arg)   
{
  sound_file *sf;
  sf = get_sf(arg);
764
  return((sf) ? sf->comment_end : (mus_long_t)MUS_ERROR);
765 766 767 768 769 770 771
}


mus_long_t mus_sound_length(const char *arg)        
{
  sound_file *sf;
  sf = get_sf(arg);
772
  return((sf) ? sf->true_file_length : (mus_long_t)MUS_ERROR);
773 774 775 776 777 778 779
}


int mus_sound_fact_samples(const char *arg)    
{
  sound_file *sf;
  sf = get_sf(arg);
780
  return((sf) ? sf->fact_samples : MUS_ERROR);
781 782 783 784 785 786 787
}


time_t mus_sound_write_date(const char *arg)   
{
  sound_file *sf;
  sf = get_sf(arg);
788
  return((sf) ? sf->write_date : (time_t)MUS_ERROR);
789 790 791 792 793 794 795
}


int mus_sound_type_specifier(const char *arg)  
{
  sound_file *sf;
  sf = get_sf(arg);
796
  return((sf) ? sf->type_specifier : MUS_ERROR);
797 798 799 800 801 802 803
}


int mus_sound_block_align(const char *arg)     
{
  sound_file *sf;
  sf = get_sf(arg);
804
  return((sf) ? sf->block_align : MUS_ERROR);
805 806 807 808 809 810 811
}


int mus_sound_bits_per_sample(const char *arg) 
{
  sound_file *sf;
  sf = get_sf(arg);
812
  return((sf) ? sf->bits_per_sample : MUS_ERROR);
813 814 815 816 817 818 819 820 821 822 823
}


float mus_sound_duration(const char *arg) 
{
  float val = -1.0;
  sound_file *sf; 
  sf = get_sf(arg); 
  if (sf) 
    {
      if ((sf->chans > 0) && (sf->srate > 0))
824
	val = ((double)(sf->samples) / ((double)(sf->chans) * (double)(sf->srate)));
825 826 827 828 829 830
      else val = 0.0;
    }
  return(val);
}


831 832 833 834 835
mus_float_t **mus_sound_saved_data(const char *arg) 
{
  /* slightly tricky -- we don't want to trigger a sound_file table entry here!
   */
  sound_file *sf;
836
  if (!arg) return(NULL);
837 838 839 840 841 842 843 844 845 846 847 848 849 850
  sf = find_sound_file(arg); /* not get_sf which will make an entry in the table */
  return((sf) ? sf->saved_data : NULL);
}


void mus_sound_set_saved_data(const char *arg, mus_float_t **data)
{
  sound_file *sf;
  sf = get_sf(arg);
  if (sf)
    sf->saved_data = data;
}


851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
int *mus_sound_loop_info(const char *arg)
{
  sound_file *sf; 
  sf = get_sf(arg); 
  if ((sf) && (sf->loop_modes))
    {
      int *info;
      info = (int *)calloc(MUS_LOOP_INFO_SIZE, sizeof(int));
      if (sf->loop_modes[0] != 0)
	{
	  info[0] = sf->loop_starts[0];
	  info[1] = sf->loop_ends[0];
	  info[6] = sf->loop_modes[0];
	}
      if (sf->loop_modes[1] != 0)
	{
	  info[2] = sf->loop_starts[1];
	  info[3] = sf->loop_ends[1];
	  info[7] = sf->loop_modes[1];
	}
      info[4] = sf->base_note;
      info[5] = sf->base_detune;
      return(info);
    }
  return(NULL);
}


void mus_sound_set_loop_info(const char *arg, int *loop)
{
  sound_file *sf; 
  sf = get_sf(arg); 
  if (sf)
    {
885
      if (!sf->loop_modes)
886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936
	{
	  sf->loop_modes = (int *)calloc(2, sizeof(int));
	  sf->loop_starts = (int *)calloc(2, sizeof(int));
	  sf->loop_ends = (int *)calloc(2, sizeof(int));
	}
      sf->loop_modes[0] = loop[6]; 
      if (loop[6] != 0)
	{
	  sf->loop_starts[0] = loop[0];
	  sf->loop_ends[0] = loop[1];
	}
      else
	{
	  sf->loop_starts[0] = 0;
	  sf->loop_ends[0] = 0;
	}
      sf->loop_modes[1] = loop[7];
      if (loop[7] != 0)
	{
	  sf->loop_starts[1] = loop[2];
	  sf->loop_ends[1] = loop[3];
	}
      else
	{
	  sf->loop_starts[1] = 0;
	  sf->loop_ends[1] = 0;
	}
      sf->base_note = loop[4];
      sf->base_detune = loop[5];
    }
}


int mus_sound_mark_info(const char *arg, int **mark_ids, int **mark_positions)
{
  sound_file *sf; 
  int result = 0;
  sf = get_sf(arg); 
  if (sf)
    {
      (*mark_ids) = sf->marker_ids;
      (*mark_positions) = sf->marker_positions;
      result = sf->markers;
    }
  return(result);
}


char *mus_sound_comment(const char *name)
{
  char *sc = NULL;
937
  sound_file *sf;
938 939 940
  sf = get_sf(name); 
  if (sf)
    {
941
      mus_long_t start, end;
942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963
      start = sf->comment_start;
      end = sf->comment_end;
      if (end == 0) 
	{
	  if (sf->aux_comment_start)
	    {
	      if ((sf->header_type == MUS_RIFF) ||
		  (sf->header_type == MUS_RF64))
		sc = mus_header_riff_aux_comment(name, 
						 sf->aux_comment_start, 
						 sf->aux_comment_end);
	      if ((sf->header_type == MUS_AIFF) || 
		  (sf->header_type == MUS_AIFC)) 
		sc = mus_header_aiff_aux_comment(name, 
						 sf->aux_comment_start, 
						 sf->aux_comment_end);
	    }
	}
      else
	{
	  if (end <= sf->true_file_length)
	    {
964
	      int len;
965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981
	      len = end - start + 1;
	      if (len > 0)
		{
		  /* open and get the comment */
		  ssize_t bytes;
		  int fd;
		  fd = mus_file_open_read(name);
		  if (fd == -1) return(NULL);
		  lseek(fd, start, SEEK_SET);
		  sc = (char *)calloc(len + 1, sizeof(char));
		  bytes = read(fd, sc, len);
		  CLOSE(fd, name);
		  if (((sf->header_type == MUS_AIFF) || 
		       (sf->header_type == MUS_AIFC)) &&
		      (sf->aux_comment_start) &&
		      (bytes != 0))
		    {
982
		      char *auxcom;
983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
		      auxcom = mus_header_aiff_aux_comment(name, 
							   sf->aux_comment_start, 
							   sf->aux_comment_end);
		      if (auxcom)
			{
			  size_t full_len;
			  full_len = strlen(auxcom) + strlen(sc) + 2;
			  sc = (char *)realloc(sc, full_len * sizeof(char));
			  strcat(sc, "\n");
			  strcat(sc, auxcom);
			}
		    }
		}
	    }
	}
    }
  return(sc);
}


int mus_sound_open_input(const char *arg) 
{
  int fd = -1;
  if (!(mus_file_probe(arg)))
1007
    mus_error(MUS_CANT_OPEN_FILE, "mus_sound_open_input: can't open %s: %s", arg, STRERROR(errno));
1008 1009
  else
    {
1010
      sound_file *sf;
1011 1012 1013 1014 1015
      mus_sound_initialize();
      sf = get_sf(arg);
      if (sf) 
	{
	  fd = mus_file_open_read(arg);
1016 1017 1018 1019 1020 1021 1022 1023 1024
	  if (fd == -1)
	    mus_error(MUS_CANT_OPEN_FILE, "mus_sound_open_input: can't open %s: %s", arg, STRERROR(errno));
	  else
	    {
	      mus_file_open_descriptors(fd, arg, sf->sample_type, sf->datum_size, sf->data_location, sf->chans, sf->header_type);
	      lseek(fd, sf->data_location, SEEK_SET);
	      if (sf->saved_data)
		mus_file_save_data(fd, sf->samples / sf->chans, sf->saved_data);
	    }
1025 1026 1027 1028 1029 1030
	}
    }
  return(fd);
}


1031
int mus_sound_open_output(const char *arg, int srate, int chans, mus_sample_t sample_type, mus_header_t header_type, const char *comment)
1032 1033 1034 1035
{
  int fd = MUS_ERROR, err;
  mus_sound_initialize();
  mus_sound_forget(arg);
1036
  err = mus_write_header(arg, header_type, srate, chans, 0, sample_type, comment);
1037 1038 1039 1040 1041 1042
  if (err != MUS_ERROR)
    {
      fd = mus_file_open_write(arg);
      if (fd != -1)
	mus_file_open_descriptors(fd,
				  arg,
1043 1044
				  sample_type,
				  mus_bytes_per_sample(sample_type),
1045 1046 1047 1048 1049 1050 1051 1052
				  mus_header_data_location(),
				  chans,
				  header_type);
    }
  return(fd);
}


1053
int mus_sound_reopen_output(const char *arg, int chans, mus_sample_t samp_type, mus_header_t type, mus_long_t data_loc)
1054 1055 1056 1057 1058
{
  int fd;
  mus_sound_initialize();
  fd = mus_file_reopen_write(arg);
  if (fd != -1)
1059
    mus_file_open_descriptors(fd, arg, samp_type, mus_bytes_per_sample(samp_type), data_loc, chans, type);
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
  return(fd);
}


int mus_sound_close_input(int fd) 
{
  return(mus_file_close(fd)); /* this closes the clm file descriptors */
}


int mus_sound_close_output(int fd, mus_long_t bytes_of_data) 
{
  char *name;
  name = mus_file_fd_name(fd);
  if (name)
    {
1076 1077
      int err;
      mus_header_t old_type;
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
      char *fname;
      fname = mus_strdup(name); 
      old_type = mus_file_header_type(fd);
      err = mus_file_close(fd);        /* this frees the original fd->name, so we copied above */
      /* fd is NULL now */
      mus_sound_forget(fname);
      mus_header_change_data_size(fname, old_type, bytes_of_data);
      free(fname);
      return(err);
    }
  return(MUS_ERROR);
}


1092
typedef enum {SF_CHANS, SF_SRATE, SF_TYPE, SF_SAMP_TYPE, SF_LOCATION, SF_SIZE} sf_field_t;
1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112

static int mus_sound_set_field(const char *arg, sf_field_t field, int val)
{
  sound_file *sf;
  int result = MUS_NO_ERROR;

  sf = get_sf(arg); 
  if (sf) 
    {
      switch (field)
	{
	case SF_CHANS:    
	  sf->chans = val;       
	  break;

	case SF_SRATE:    
	  sf->srate = val;       
	  break;

	case SF_TYPE:     
1113
	  sf->header_type = (mus_header_t)val; 
1114 1115
	  break;

1116 1117 1118
	case SF_SAMP_TYPE:   
	  sf->sample_type = (mus_sample_t)val; 
	  sf->datum_size = mus_bytes_per_sample(sf->sample_type); 
1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159
	  break;

	default: 
	  result = MUS_ERROR; 
	  break;
	}
    }
  else result = MUS_ERROR;
  return(result);
}


static int mus_sound_set_mus_long_t_field(const char *arg, sf_field_t field, mus_long_t val)
{
  sound_file *sf; 
  int result = MUS_NO_ERROR;

  sf = get_sf(arg); 
  if (sf) 
    {
      switch (field)
	{
	case SF_SIZE:     
	  sf->samples = val; 
	  break;

	case SF_LOCATION: 
	  sf->data_location = val; 
	  break;

	default: 
	  result = MUS_ERROR;
	  break;
	}
    }
  else result = MUS_ERROR;

  return(result);
}


1160 1161 1162 1163
int mus_sound_set_chans(const char *arg, int val)                {return(mus_sound_set_field(arg,            SF_CHANS,    val));}
int mus_sound_set_srate(const char *arg, int val)                {return(mus_sound_set_field(arg,            SF_SRATE,    val));}
mus_header_t mus_sound_set_header_type(const char *arg, mus_header_t val) {return((mus_header_t)mus_sound_set_field(arg, SF_TYPE, val));}
mus_sample_t mus_sound_set_sample_type(const char *arg, mus_sample_t val) {return((mus_sample_t)mus_sound_set_field(arg, SF_SAMP_TYPE, val));}
1164 1165 1166 1167
int mus_sound_set_data_location(const char *arg, mus_long_t val) {return(mus_sound_set_mus_long_t_field(arg, SF_LOCATION, val));}
int mus_sound_set_samples(const char *arg, mus_long_t val)       {return(mus_sound_set_mus_long_t_field(arg, SF_SIZE,     val));}


1168
int mus_sound_override_header(const char *arg, int srate, int chans, mus_sample_t samp_type, mus_header_t type, mus_long_t location, mus_long_t size)
1169 1170 1171 1172 1173 1174 1175 1176 1177 1178
{
  sound_file *sf; 
  int result = MUS_NO_ERROR;
  /* perhaps once a header has been over-ridden, we should not reset the relevant fields upon re-read? */

  sf = get_sf(arg); 
  if (sf)
    {
      if (location != -1) sf->data_location = location;
      if (size != -1) sf->samples = size;
1179
      if (samp_type != MUS_UNKNOWN_SAMPLE) 
1180
	{
1181 1182
	  sf->sample_type = samp_type;
	  sf->datum_size = mus_bytes_per_sample(samp_type);
1183 1184 1185
	}
      if (srate != -1) sf->srate = srate;
      if (chans != -1) sf->chans = chans;
1186
      if (type != MUS_UNKNOWN_HEADER) sf->header_type = (mus_header_t)type;
1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197
    }
  else result = MUS_ERROR;

  return(result);
}


bool mus_sound_maxamp_exists(const char *ifile)
{
  sound_file *sf; 
  sf = get_sf(ifile); 
1198 1199 1200 1201 1202 1203 1204 1205 1206
  if ((sf) && (sf->maxtimes))
    {
      int i;
      for (i = 0; i < sf->maxamps_size; i++)
	if (sf->maxtimes[i] == -1)
	  return(false);
      return(true);
    }
  return(false);
1207 1208 1209
}


1210
mus_long_t mus_sound_maxamps(const char *ifile, int chans, mus_float_t *vals, mus_long_t *times)
1211
{
1212
  mus_long_t framples;
1213
  uint32_t ichans, chn;
1214 1215 1216 1217
  sound_file *sf; 
    
  sf = get_sf(ifile); 
  if (sf->chans <= 0)
1218
    return(MUS_ERROR);
1219 1220 1221
  
  if ((sf) && (sf->maxamps))
    {
1222 1223
      if (chans > sf->maxamps_size) 
	ichans = sf->maxamps_size; 
1224
      else ichans = (uint32_t)chans;
1225 1226 1227 1228 1229
      for (chn = 0; chn < ichans; chn++)
	{
	  times[chn] = sf->maxtimes[chn];
	  vals[chn] = sf->maxamps[chn];
	}
1230 1231
      framples = sf->samples / sf->chans;
      return(framples);
1232 1233 1234 1235
    }

  {
    int j, bufnum, ifd;
1236 1237
    mus_long_t n, curframples;
    mus_float_t *buffer, *samp;
1238
    mus_long_t *time;
1239
    mus_float_t **ibufs;
1240 1241 1242

    ifd = mus_sound_open_input(ifile);
    if (ifd == MUS_ERROR) return(MUS_ERROR);
1243
    ichans = (uint32_t)mus_sound_chans(ifile);
1244
    framples = mus_sound_framples(ifile);
1245
    if ((framples == 0) || (ichans > MUS_MAX_CHANS))
1246 1247 1248 1249 1250
      {
	mus_sound_close_input(ifd);
	return(0);
      }

1251
    mus_file_seek_frample(ifd, 0);
1252

1253
    ibufs = (mus_float_t **)calloc(ichans, sizeof(mus_float_t *));
1254
    bufnum = 8192;
1255 1256
    for (chn = 0; chn < ichans; chn++) 
      ibufs[chn] = (mus_float_t *)calloc(bufnum, sizeof(mus_float_t));
1257 1258

    time = (mus_long_t *)calloc(ichans, sizeof(mus_long_t));
1259
    samp = (mus_float_t *)calloc(ichans, sizeof(mus_float_t));
1260

1261
    for (n = 0; n < framples; n += bufnum)
1262
      {
1263 1264 1265 1266
	if ((n + bufnum) < framples) 
	  curframples = bufnum; 
	else curframples = (framples - n);
	mus_file_read(ifd, n, curframples, ichans, ibufs);
1267 1268
	for (chn = 0; chn < ichans; chn++)
	  {
1269 1270 1271
	    int i;
	    buffer = (mus_float_t *)(ibufs[chn]);
	    for (i = 0; i < curframples; i++) 
1272
	      {
1273 1274
		mus_float_t abs_samp;
		abs_samp = fabs(buffer[i]);
1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
		if (abs_samp > samp[chn])
		  {
		    time[chn] = i + n; 
		    samp[chn] = abs_samp;
		  }
	      }
	  }
      }

    mus_sound_close_input(ifd);
1285
    /* fprintf(stderr, "set in mus_sound_maxamps\n"); */
1286 1287
    mus_sound_set_maxamps(ifile, ichans, samp, time); /* save the complete set */

1288
    if ((int)ichans > chans) ichans = chans;
1289 1290 1291 1292 1293 1294 1295
    for (chn = 0; chn < ichans; chn++)
      {
	times[chn] = time[chn];
	vals[chn] = samp[chn];
      }
    free(time);
    free(samp);
1296
    for (j = 0; j < (int)ichans; j++) free(ibufs[j]);
1297
    free(ibufs);
1298
    return(framples);
1299 1300 1301 1302
  }
}


1303
int mus_sound_set_maxamps(const char *ifile, int chans, mus_float_t *vals, mus_long_t *times)
1304 1305 1306 1307
{
  sound_file *sf; 
  int result = MUS_NO_ERROR;

1308
  /* fprintf(stderr, "set %s maxamps: %d %.3f %" print_mus_long "\n", ifile, chans, vals[0], times[0]); */
1309 1310 1311 1312
  sf = get_sf(ifile); 
  if (sf)
    {
      int i, ichans = 0;
1313

1314 1315
      if (sf->maxamps)
	{
1316 1317
	  if (chans > sf->maxamps_size) 
	    ichans = sf->maxamps_size; 
1318 1319 1320 1321 1322 1323 1324 1325 1326
	  else ichans = chans;
	  for (i = 0; i < ichans; i++)
	    {
	      sf->maxtimes[i] = times[i];
	      sf->maxamps[i] = vals[i];
	    }
	}
      else
	{
1327
	  ichans = sf->chans;
1328
	  if (!sf->maxamps) 
1329 1330 1331 1332 1333 1334 1335
	    {
	      /* here we need to use the max, since the caller may be confused */
	      int max_chans;
	      max_chans = ichans;
	      if (max_chans < chans)
		max_chans = chans;

1336
	      sf->maxamps = (mus_float_t *)calloc(max_chans, sizeof(mus_float_t));
1337
	      sf->maxtimes = (mus_long_t *)calloc(max_chans, sizeof(mus_long_t));
1338
	      sf->maxamps_size = max_chans;
1339
	    }
1340 1341 1342
	  if (ichans > sf->maxamps_size)
	    ichans = sf->maxamps_size;
	  
1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356
	  if (ichans > chans) 
	    ichans = chans;
	  for (i = 0; i < ichans; i++)
	    {
	      sf->maxtimes[i] = times[i];
	      sf->maxamps[i] = vals[i];
	    }
	}
    }
  else result = MUS_ERROR;
  return(result);
}


1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384
bool mus_sound_channel_maxamp_exists(const char *file, int chan)
{
  sound_file *sf; 
  sf = get_sf(file); 
  return((sf) && 
	 (sf->maxtimes) && 
	 (sf->maxamps_size > chan) && 
	 (sf->maxtimes[chan] != -1));
}


mus_float_t mus_sound_channel_maxamp(const char *file, int chan, mus_long_t *pos)
{
  sound_file *sf; 
  sf = get_sf(file); 
  if ((chan < sf->maxamps_size) &&
      (sf->maxtimes))
    {
      (*pos) = sf->maxtimes[chan];
      return(sf->maxamps[chan]);
    }
  return(-1.0);
}


void mus_sound_channel_set_maxamp(const char *file, int chan, mus_float_t mx, mus_long_t pos)
{
  sound_file *sf; 
1385
  /* fprintf(stderr, "set %s maxamp: %.3f %" print_mus_long "\n", file, mx, pos); */
1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408
  sf = get_sf(file); 
  if ((sf) &&
      (sf->chans > chan))
    {
      if (!(sf->maxamps))
	{
	  int i;
	  sf->maxamps = (mus_float_t *)malloc(sf->chans * sizeof(mus_float_t));
	  sf->maxtimes = (mus_long_t *)malloc(sf->chans * sizeof(mus_long_t));
	  sf->maxamps_size = sf->chans;
	  for (i = 0; i < sf->chans; i++)
	    {
	      sf->maxamps[i] = -1.0;
	      sf->maxtimes[i] = -1;
	    }
	}
      sf->maxamps[chan] = mx;
      sf->maxtimes[chan] = pos;
    }
}


mus_long_t mus_file_to_array(const char *filename, int chan, mus_long_t start, mus_long_t samples, mus_float_t *array)
1409 1410 1411
{
  int ifd, chans;
  mus_long_t total_read;
1412
  mus_float_t **bufs;
1413 1414 1415 1416 1417

  ifd = mus_sound_open_input(filename);
  if (ifd == MUS_ERROR) return(MUS_ERROR);

  chans = mus_sound_chans(filename);
1418 1419
  if ((chan >= chans) ||
      (chan < 0))
1420 1421 1422 1423 1424
    {
      mus_sound_close_input(ifd);      
      return(mus_error(MUS_NO_SUCH_CHANNEL, "mus_file_to_array can't read %s channel %d (file has %d chans)", filename, chan, chans));
    }

1425
  bufs = (mus_float_t **)calloc(chans, sizeof(mus_float_t *));
1426 1427
  bufs[chan] = array;

1428 1429
  mus_file_seek_frample(ifd, start);
  total_read = mus_file_read_any(ifd, start, chans, samples, bufs, bufs);
1430 1431 1432 1433 1434 1435 1436 1437

  mus_sound_close_input(ifd);
  free(bufs);

  return(total_read);
}


1438
const char *mus_array_to_file_with_error(const char *filename, mus_float_t *ddata, mus_long_t len, int srate, int channels)
1439 1440 1441
{
  /* put ddata into a sound file, taking byte order into account */
  /* assume ddata is interleaved already if more than one channel */
1442
  int fd, err;
1443 1444 1445
  mus_long_t oloc;
  mus_sound_forget(filename);

1446
  err = mus_write_header(filename, MUS_NEXT, srate, channels, len * channels, MUS_OUT_SAMPLE_TYPE, "array->file");
1447 1448 1449 1450 1451 1452
  if (err != MUS_NO_ERROR)
    return("mus_array_to_file can't create output file");
  oloc = mus_header_data_location();
  fd = mus_file_reopen_write(filename);
  lseek(fd, oloc, SEEK_SET);
  err = mus_file_open_descriptors(fd, filename,
1453 1454
				  MUS_OUT_SAMPLE_TYPE,
				  mus_bytes_per_sample(MUS_OUT_SAMPLE_TYPE),
1455 1456 1457
				  oloc, channels, MUS_NEXT);
  if (err != MUS_ERROR)
    {
1458
      mus_float_t *bufs[1];
1459 1460 1461 1462 1463 1464 1465 1466 1467
      bufs[0] = ddata;
      err = mus_file_write(fd, 0, len - 1, 1, bufs); /* 1 = chans?? */
    }
  mus_file_close(fd);
  if (err == MUS_ERROR)
    return("mus_array_to_file write error");
  return(NULL);
}

1468
int mus_array_to_file(const char *filename, mus_float_t *ddata, mus_long_t len, int srate, int channels)
1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499
{
  const char *errmsg;
  errmsg = mus_array_to_file_with_error(filename, ddata, len, srate, channels);
  if (errmsg)
    return(mus_error(MUS_CANT_OPEN_FILE, "%s", errmsg));
  return(MUS_NO_ERROR);
}


mus_long_t mus_file_to_float_array(const char *filename, int chan, mus_long_t start, mus_long_t samples, mus_float_t *array)
{
  return(mus_file_to_array(filename, chan, start, samples, array));
}


int mus_float_array_to_file(const char *filename, mus_float_t *ddata, mus_long_t len, int srate, int channels)
{
  const char *errmsg;
  errmsg = mus_array_to_file_with_error(filename, ddata, len, srate, channels);
  if (errmsg)
    return(mus_error(MUS_CANT_OPEN_FILE, "%s", errmsg));

  return(MUS_NO_ERROR);
}


int mus_sound_initialize(void)
{
  static bool sndlib_initialized = false;
  if (!sndlib_initialized)
    {
1500
      int err;
1501 1502 1503 1504 1505
      sndlib_initialized = true;
      mus_error_handler = default_mus_error;
      err = mus_header_initialize();
      if (err == MUS_NO_ERROR) 
	err = mus_audio_initialize();
1506 1507
      sound_tables = (sound_file ***)calloc(NUM_SOUND_TABLES, sizeof(sound_file **));
      sound_table_sizes = (int *)calloc(NUM_SOUND_TABLES, sizeof(int));
1508 1509 1510 1511 1512 1513
      return(err);
    }
  return(MUS_NO_ERROR);
}