/* Tie sndlib into Xen */

#include "mus-config.h"

#if USE_SND
  #include "snd.h"
#else
  #if HAVE_RUBY
    #define PROC_FALSE "false"
    #define PROC_TRUE "true"
  #endif
  #if HAVE_SCHEME || HAVE_FORTH
    #define PROC_FALSE "#f"
    #define PROC_TRUE "#t"
  #endif
#endif

#include <stddef.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>


#ifdef _MSC_VER
  #pragma warning(disable: 4244)
#endif

#include "_sndlib.h"
#include "sndlib-strings.h"
#include "vct.h"
#include "clm.h"
#include "sndlib2xen.h"
#include "clm2xen.h"

#ifndef S_set
  #if HAVE_RUBY
    #define S_set "set_"
  #endif
  #if HAVE_SCHEME
    #define S_set "set! "
  #endif
  #if HAVE_FORTH
    #define S_set "set-"
  #endif
#endif



/* originally I tried to simplify C GC by using global static strings that were 
 *   freed whenever the associated function was called again, on the assumption
 *   that the preceding value was now unused.  In a multithread context, that
 *   assumption is false, so I didn't use code like this:
 *
 *  static char *tmpstr = NULL;
 *
 *  static char *local_mus_expand_filename(char *name)
 *  {
 *    if (tmpstr) {free(tmpstr); tmpstr = NULL;}
 *       tmpstr = mus_expand_filename(name);
 *    return(tmpstr);
 *  }
 */

static Xen g_mus_sound_loop_info(Xen gfilename)
{
  #define H_mus_sound_loop_info "(" S_mus_sound_loop_info " filename): synth loop info for sound as a list: (start1 \
end1 start2 end2 base-note base-detune mode1 mode2)"
  int *res;
  Xen sres = Xen_empty_list;
  char *str = NULL;

  Xen_check_type(Xen_is_string(gfilename), gfilename, 1, S_mus_sound_loop_info, "a string"); 

  res = mus_sound_loop_info(str = mus_expand_filename(Xen_string_to_C_string(gfilename)));
  if (str) free(str);
  if (res)
    {
      sres = Xen_list_8(C_int_to_Xen_integer(res[0]), C_int_to_Xen_integer(res[1]), C_int_to_Xen_integer(res[2]),
			C_int_to_Xen_integer(res[3]), C_int_to_Xen_integer(res[4]), C_int_to_Xen_integer(res[5]),
			C_int_to_Xen_integer(res[6]), C_int_to_Xen_integer(res[7]));
      free(res);
    }
  return(sres);
}


static Xen g_mus_sound_mark_info(Xen gfilename)
{
  #define H_mus_sound_mark_info "(" S_mus_sound_mark_info " filename): aifc header mark info as a list of lists: ((id pos)...)"
  int *mark_ids, *mark_positions;
  int marks = 0;
  Xen sres = Xen_empty_list;
  char *str = NULL;

  Xen_check_type(Xen_is_string(gfilename), gfilename, 1, S_mus_sound_mark_info, "a string"); 

  marks = mus_sound_mark_info(str = mus_expand_filename(Xen_string_to_C_string(gfilename)), &mark_ids, &mark_positions);
  if (str) free(str);
  if (marks > 0)
    {
      int i;
      for (i = 0; i < marks; i++)
	sres = Xen_cons(Xen_list_2(C_int_to_Xen_integer(mark_ids[i]),
				   C_int_to_Xen_integer(mark_positions[i])),
			sres);
    }
  return(sres);
}


static Xen gmus_sound(const char *caller, int (*func)(const char *file), Xen gfilename)
{
  char *str = NULL;
  Xen result;

  Xen_check_type(Xen_is_string(gfilename), gfilename, 1, caller, "a string"); 
  str = mus_expand_filename(Xen_string_to_C_string(gfilename));
  result = C_int_to_Xen_integer((*func)(str));
  if (str) free(str);
  return(result);
}


static Xen gmus_sound_set(const char *caller, int (*func)(const char *file, int newval), Xen gfilename, Xen val)
{
  char *str = NULL;
  Xen result;

  Xen_check_type(Xen_is_string(gfilename), gfilename, 1, caller, "a string"); 
  Xen_check_type(Xen_is_integer(val), val, 2, caller, "an integer");
  str = mus_expand_filename(Xen_string_to_C_string(gfilename));
  result = C_int_to_Xen_integer((*func)(str, Xen_integer_to_C_int(val)));
  if (str) free(str);
  return(result);
}


static Xen glmus_sound(const char *caller, mus_long_t (*func)(const char *file), Xen gfilename)
{
  char *str = NULL;
  Xen result;

  Xen_check_type(Xen_is_string(gfilename), gfilename, 1, caller, "a string"); 
  str = mus_expand_filename(Xen_string_to_C_string(gfilename));
  result = C_llong_to_Xen_llong((*func)(str));
  if (str) free(str);
  return(result);
}


static Xen glmus_sound_set(const char *caller, int (*func)(const char *file, mus_long_t newval), Xen gfilename, Xen val)
{
  char *str = NULL;
  Xen result;

  Xen_check_type(Xen_is_string(gfilename), gfilename, 1, caller, "a string"); 
  Xen_check_type(Xen_is_number(val), val, 2, caller, "a number");
  str = mus_expand_filename(Xen_string_to_C_string(gfilename));
  result = C_llong_to_Xen_llong((*func)(str, Xen_llong_to_C_llong(val)));
  if (str) free(str);
  return(result);
}


static Xen g_mus_sound_samples(Xen filename) 
{
  #define H_mus_sound_samples "(" S_mus_sound_samples " filename): samples (framples * channels) in sound file"
  return(glmus_sound(S_mus_sound_samples, mus_sound_samples, filename));
}


static Xen g_mus_sound_set_samples(Xen filename, Xen val) 
{
  return(glmus_sound_set(S_set S_mus_sound_samples, mus_sound_set_samples, filename, val));
}


Xen g_mus_sound_framples(Xen filename) 
{
  #define H_mus_sound_framples "(" S_mus_sound_framples " filename): framples (samples / channel) in sound file"
  return(glmus_sound(S_mus_sound_framples, mus_sound_framples, filename));
}


static Xen g_mus_sound_datum_size(Xen filename) 
{
  #define H_mus_sound_datum_size "(" S_mus_sound_datum_size " filename): bytes per sample used by the data in sound file (sample type dependent)"
  return(gmus_sound(S_mus_sound_datum_size, mus_sound_datum_size, filename));
}


static Xen g_mus_sound_data_location(Xen filename) 
{
  #define H_mus_sound_data_location "(" S_mus_sound_data_location " filename): location (in bytes) of first sample of sound data"
  return(glmus_sound(S_mus_sound_data_location, mus_sound_data_location, filename));
}


static Xen g_mus_sound_set_data_location(Xen filename, Xen val) 
{
  return(glmus_sound_set(S_set S_mus_sound_data_location, mus_sound_set_data_location, filename, val));
}


Xen g_mus_sound_chans(Xen filename) 
{
  #define H_mus_sound_chans "(" S_mus_sound_chans " filename): channels of data in sound file"
  return(gmus_sound(S_mus_sound_chans, mus_sound_chans, filename));
}


static Xen g_mus_sound_set_chans(Xen filename, Xen val) 
{
  return(gmus_sound_set(S_set S_mus_sound_chans, mus_sound_set_chans, filename, val));
}


Xen g_mus_sound_srate(Xen filename) 
{
  #define H_mus_sound_srate "(" S_mus_sound_srate " filename): sampling rate of sound file"
  return(gmus_sound(S_mus_sound_srate, mus_sound_srate, filename));
}


static Xen g_mus_sound_set_srate(Xen filename, Xen val) 
{
  return(gmus_sound_set(S_set S_mus_sound_srate, mus_sound_set_srate, filename, val));
}


static Xen g_mus_sound_header_type(Xen filename) 
{
  #define H_mus_sound_header_type "(" S_mus_sound_header_type " filename): header type (e.g. " S_mus_aifc ") of sound file"

  char *str = NULL;
  Xen result;

  Xen_check_type(Xen_is_string(filename), filename, 1, S_mus_sound_header_type, "a string"); 
  str = mus_expand_filename(Xen_string_to_C_string(filename));
  result = C_int_to_Xen_integer((int)mus_sound_header_type(str));
  if (str) free(str);
  return(result);
}


static Xen g_mus_sound_set_header_type(Xen filename, Xen val) 
{
  char *str = NULL;
  Xen result;

  Xen_check_type(Xen_is_string(filename), filename, 1, S_set S_mus_sound_header_type, "a string"); 
  Xen_check_type(Xen_is_integer(val), val, 2, S_set S_mus_sound_header_type, "an integer");
  str = mus_expand_filename(Xen_string_to_C_string(filename));
  result = C_int_to_Xen_integer((int)mus_sound_set_header_type(str, (mus_header_t)Xen_integer_to_C_int(val)));
  if (str) free(str);
  return(result);
}


static Xen g_mus_sound_sample_type(Xen filename) 
{
  #define H_mus_sound_sample_type "(" S_mus_sound_sample_type " filename): sample type (e.g. " S_mus_bshort ") of data in sound file"
  char *str = NULL;
  Xen result;

  Xen_check_type(Xen_is_string(filename), filename, 1, S_mus_sound_sample_type, "a string"); 
  str = mus_expand_filename(Xen_string_to_C_string(filename));
  result = C_int_to_Xen_integer((int)mus_sound_sample_type(str));
  if (str) free(str);
  return(result);
}


static Xen g_mus_sound_set_sample_type(Xen filename, Xen val) 
{
  char *str = NULL;
  Xen result;

  Xen_check_type(Xen_is_string(filename), filename, 1, S_set S_mus_sound_sample_type, "a string"); 
  Xen_check_type(Xen_is_integer(val), val, 2, S_set S_mus_sound_sample_type, "an integer");
  str = mus_expand_filename(Xen_string_to_C_string(filename));
  result = C_int_to_Xen_integer((int)mus_sound_set_sample_type(str, (mus_sample_t)Xen_integer_to_C_int(val)));
  if (str) free(str);
  return(result);
}


static Xen g_mus_sound_length(Xen filename) 
{
  #define H_mus_sound_length "(" S_mus_sound_length " filename): sound file length in bytes"
  return(glmus_sound(S_mus_sound_length, mus_sound_length, filename));
}


static Xen g_mus_sound_type_specifier(Xen filename) 
{
  #define H_mus_sound_type_specifier "(" S_mus_sound_type_specifier " filename): original sound file header type identifier (e.g. 0x2e736e64)"
  return(gmus_sound(S_mus_sound_type_specifier, mus_sound_type_specifier, filename));
}


static Xen g_mus_sound_forget(Xen filename) 
{
  #define H_mus_sound_forget "(" S_mus_sound_forget " filename): remove 'filename' from sound cache.  If you create, then later \
delete a sound file, " S_mus_sound_forget " can be used to clear it from sndlib's cache of sound files"
  return(gmus_sound(S_mus_sound_forget, mus_sound_forget, filename));
}


static Xen g_mus_sound_prune(void) 
{
  #define H_mus_sound_prune "(" S_mus_sound_prune "): remove all defunct entries from sndlib's sound file cache."
  return(C_int_to_Xen_integer(mus_sound_prune()));
}


static Xen g_mus_sound_comment(Xen gfilename) 
{
  #define H_mus_sound_comment "(" S_mus_sound_comment " filename): comment (a string) found in sound file's header"
  char *res = NULL, *str = NULL; 
  Xen newstr;

  Xen_check_type(Xen_is_string(gfilename), gfilename, 1, S_mus_sound_comment, "a string"); 

  res = mus_sound_comment(str = mus_expand_filename(Xen_string_to_C_string(gfilename)));
  if (str) free(str);
  newstr = C_string_to_Xen_string(res);
  if (res) free(res);
  return(newstr);
}


static Xen g_mus_sound_write_date(Xen filename) 
{
  char *str = NULL;
  Xen result;

  #define H_mus_sound_write_date "(" S_mus_sound_write_date " filename): write date of sound file"
  Xen_check_type(Xen_is_string(filename), filename, 1, S_mus_sound_write_date, "a string"); 
  str = mus_expand_filename(Xen_string_to_C_string(filename));
  result = C_ulong_to_Xen_ulong((unsigned long)mus_sound_write_date(str)); /* actually time_t */
  if (str) free(str);
  return(result);
}

static Xen g_mus_header_writable(Xen head, Xen data)
{
  #define H_mus_header_writable "(" S_mus_header_writable " header-type sample-type) returns " PROC_TRUE " if the header can handle the sample type"
  Xen_check_type(Xen_is_integer(head), head, 1, S_mus_header_writable, "a header type");
  Xen_check_type(Xen_is_integer(data), data, 2, S_mus_header_writable, "a sample type");
  return(C_bool_to_Xen_boolean(mus_header_writable((mus_header_t)Xen_integer_to_C_int(head), (mus_sample_t)Xen_integer_to_C_int(data))));
}

static Xen g_mus_header_raw_defaults(void)
{
  #define H_mus_header_raw_defaults "(" S_mus_header_raw_defaults "): returns list '(srate chans sample-type) of current raw sound default attributes"
  int srate, chans;
  mus_sample_t sample_type;
  mus_header_raw_defaults(&srate, &chans, &sample_type);
  return(Xen_list_3(C_int_to_Xen_integer(srate),
		    C_int_to_Xen_integer(chans),
		    C_int_to_Xen_integer((int)sample_type)));
}


static Xen g_mus_header_set_raw_defaults(Xen lst)
{
  Xen_check_type((Xen_is_list(lst)) && (Xen_list_length(lst) == 3), lst, 1, S_mus_header_raw_defaults, "a list: '(srate chans sample-type)");
  Xen_check_type(Xen_is_integer(Xen_car(lst)), Xen_car(lst), 1, S_mus_header_raw_defaults, "an integer = srate");
  Xen_check_type(Xen_is_integer(Xen_cadr(lst)), Xen_cadr(lst), 2, S_mus_header_raw_defaults, "an integer = chans");
  Xen_check_type(Xen_is_integer(Xen_caddr(lst)), Xen_caddr(lst), 3, S_mus_header_raw_defaults, "an integer = sample-type");
  mus_header_set_raw_defaults(Xen_integer_to_C_int(Xen_car(lst)),
			      Xen_integer_to_C_int(Xen_cadr(lst)),
			      (mus_sample_t)Xen_integer_to_C_int(Xen_caddr(lst)));
  return(lst);
}


static Xen g_mus_header_type_name(Xen type) 
{
  #define H_mus_header_type_name "(" S_mus_header_type_name " type): header type (e.g. " S_mus_aiff ") as a string"
  Xen_check_type(Xen_is_integer(type), type, 1, S_mus_header_type_name, "an integer (header-type id)"); 
  return(C_string_to_Xen_string(mus_header_type_name((mus_header_t)Xen_integer_to_C_int(type))));
}


static Xen g_mus_header_type_to_string(Xen type) 
{
  #define H_mus_header_type_to_string "(" S_mus_header_type_to_string " type): header type (e.g. " S_mus_aiff ") as a string"
  Xen_check_type(Xen_is_integer(type), type, 1, S_mus_header_type_to_string, "an integer (header-type id)"); 
  return(C_string_to_Xen_string(mus_header_type_to_string((mus_header_t)Xen_integer_to_C_int(type))));
}


static Xen g_mus_sample_type_name(Xen samp_type) 
{
  #define H_mus_sample_type_name "(" S_mus_sample_type_name " samp_type): sample type (e.g. " S_mus_bshort ") as a string"
  Xen_check_type(Xen_is_integer(samp_type), samp_type, 1, S_mus_sample_type_name, "an integer (sample-type id)"); 
  return(C_string_to_Xen_string(mus_sample_type_name((mus_sample_t)Xen_integer_to_C_int(samp_type))));
}


static Xen g_mus_sample_type_to_string(Xen samp_type) 
{
  #define H_mus_sample_type_to_string "(" S_mus_sample_type_to_string " samp_type): sample type (e.g. " S_mus_bshort ") as a string"
  Xen_check_type(Xen_is_integer(samp_type), samp_type, 1, S_mus_sample_type_to_string, "an integer (sample-type id)"); 
  return(C_string_to_Xen_string(mus_sample_type_to_string((mus_sample_t)Xen_integer_to_C_int(samp_type))));
}


static Xen g_mus_bytes_per_sample(Xen samp_type) 
{
  #define H_mus_bytes_per_sample "(" S_mus_bytes_per_sample " sample-type): number of bytes per sample in \
sample-type (e.g. " S_mus_bshort " = 2)"
  Xen_check_type(Xen_is_integer(samp_type), samp_type, 1, S_mus_bytes_per_sample, "an integer (sample-type id)"); 
  return(C_int_to_Xen_integer(mus_bytes_per_sample((mus_sample_t)Xen_integer_to_C_int(samp_type))));
}


static Xen g_mus_sound_duration(Xen gfilename) 
{
  #define H_mus_sound_duration "(" S_mus_sound_duration " filename): duration (in seconds) of sound file"
  float res;
  char *str = NULL;

  Xen_check_type(Xen_is_string(gfilename), gfilename, 1, S_mus_sound_duration, "a string"); 
  res = mus_sound_duration(str = mus_expand_filename(Xen_string_to_C_string(gfilename)));
  if (str) free(str);
  return(C_double_to_Xen_real(res));

}


static Xen g_mus_oss_set_buffers(Xen num, Xen size)
{
  #define H_mus_oss_set_buffers "(" S_mus_oss_set_buffers " num size): set Linux OSS 'fragment' number and size. \
If Snd's controls seem sluggish, try (" S_mus_oss_set_buffers " 4 12) or even (" S_mus_oss_set_buffers " 2 12). \
This reduces the on-card buffering, but may introduce clicks."

#if (HAVE_OSS || HAVE_ALSA)
  Xen_check_type(Xen_is_integer(num), num, 1, S_mus_oss_set_buffers, "an integer");
  Xen_check_type(Xen_is_integer(size), size, 2, S_mus_oss_set_buffers, "an integer");
  mus_oss_set_buffers(Xen_integer_to_C_int(num),
			    Xen_integer_to_C_int(size));
#endif
  return(Xen_false);
}


static Xen g_mus_alsa_buffers(void)
{
  #define H_mus_alsa_buffers "(" S_mus_alsa_buffers "): current number of ALSA periods."
#if HAVE_ALSA
  return(C_int_to_Xen_integer(mus_alsa_buffers()));
#endif
  return(Xen_false);
}


static Xen g_mus_alsa_set_buffers(Xen val)
{
  Xen_check_type(Xen_is_integer(val), val, 1, S_set S_mus_alsa_buffers, "an integer");
#if HAVE_ALSA
  return(C_int_to_Xen_integer(mus_alsa_set_buffers(Xen_integer_to_C_int(val))));
#endif
  return(Xen_false);
}


static Xen g_mus_alsa_buffer_size(void)
{
  #define H_mus_alsa_buffer_size "(" S_mus_alsa_buffer_size "): current size of ALSA buffers."
#if HAVE_ALSA
  return(C_int_to_Xen_integer(mus_alsa_buffer_size()));
#endif
  return(Xen_false);
}


static Xen g_mus_alsa_set_buffer_size(Xen val)
{
  Xen_check_type(Xen_is_integer(val), val, 1, S_set S_mus_alsa_buffer_size, "an integer");
#if HAVE_ALSA
  return(C_int_to_Xen_integer(mus_alsa_set_buffer_size(Xen_integer_to_C_int(val))));
#endif
  return(Xen_false);
}


static Xen g_mus_alsa_device(void)
{
  #define H_mus_alsa_device "(" S_mus_alsa_device "): current ALSA device."
#if HAVE_ALSA
  return(C_string_to_Xen_string(mus_alsa_device()));
#endif
  return(Xen_false);
}


static Xen g_mus_alsa_set_device(Xen val)
{
  Xen_check_type(Xen_is_string(val), val, 1, S_set S_mus_alsa_device, "a string (ALSA device name)");
#if HAVE_ALSA
  return(C_string_to_Xen_string(mus_alsa_set_device(Xen_string_to_C_string(val))));
#endif
  return(Xen_false);
}


static Xen g_mus_alsa_playback_device(void)
{
  #define H_mus_alsa_playback_device "(" S_mus_alsa_playback_device "): current ALSA playback device."
#if HAVE_ALSA
  return(C_string_to_Xen_string(mus_alsa_playback_device()));
#endif
  return(Xen_false);
}


static Xen g_mus_alsa_set_playback_device(Xen val)
{
  Xen_check_type(Xen_is_string(val), val, 1, S_set S_mus_alsa_playback_device, "a string (ALSA device name)");
#if HAVE_ALSA
  return(C_string_to_Xen_string(mus_alsa_set_playback_device(Xen_string_to_C_string(val))));
#endif
  return(Xen_false);
}


static Xen g_mus_alsa_capture_device(void)
{
  #define H_mus_alsa_capture_device "(" S_mus_alsa_capture_device "): current ALSA capture device."
#if HAVE_ALSA
  return(C_string_to_Xen_string(mus_alsa_capture_device()));
#endif
  return(Xen_false);
}


static Xen g_mus_alsa_set_capture_device(Xen val)
{
  Xen_check_type(Xen_is_string(val), val, 1, S_set S_mus_alsa_capture_device, "a string (ALSA device name)");
#if HAVE_ALSA
  return(C_string_to_Xen_string(mus_alsa_set_capture_device(Xen_string_to_C_string(val))));
#endif
  return(Xen_false);
}


static Xen g_mus_alsa_squelch_warning(void)
{
  #define H_mus_alsa_squelch_warning "(" S_mus_alsa_squelch_warning "): whether to squelch ALSA srate mismatch warnings."
#if HAVE_ALSA
  return(C_bool_to_Xen_boolean(mus_alsa_squelch_warning()));
#endif
  return(Xen_false);
}


static Xen g_mus_alsa_set_squelch_warning(Xen val)
{
  Xen_check_type(Xen_is_boolean(val), val, 1, S_set S_mus_alsa_squelch_warning, "a boolean");
#if HAVE_ALSA
  return(C_bool_to_Xen_boolean(mus_alsa_set_squelch_warning(Xen_boolean_to_C_bool(val))));
#endif
  return(Xen_false);
}


static Xen g_mus_sound_maxamp_exists(Xen file)
{
  #define H_mus_sound_maxamp_exists "(" S_mus_sound_maxamp_exists " filename): " PROC_TRUE " if sound's maxamp data is available \
in the sound cache; if it isn't, a call on " S_mus_sound_maxamp " has to open and read the data to get the maxamp."
  bool val;
  char *str = NULL;

  Xen_check_type(Xen_is_string(file), file, 1, S_mus_sound_maxamp_exists, "a string");
  val = mus_sound_maxamp_exists(str = mus_expand_filename(Xen_string_to_C_string(file)));
  if (str) free(str);
  return(C_bool_to_Xen_boolean(val));
}


Xen g_mus_sound_maxamp(Xen file)
{
  #define H_mus_sound_maxamp "(" S_mus_sound_maxamp " filename): maxamps in sound (a list of paired amps (floats) and locations (samples))"
  int chans;
  char *filename;
  Xen res = Xen_empty_list;

  Xen_check_type(Xen_is_string(file), file, 1, S_mus_sound_maxamp, "a string");

  filename = mus_expand_filename(Xen_string_to_C_string(file));
  chans = mus_sound_chans(filename);
  if (chans > 0)
    {
      mus_long_t rtn;
      mus_float_t *vals;
      mus_long_t *times;

      vals = (mus_float_t *)calloc(chans, sizeof(mus_float_t));
      times = (mus_long_t *)calloc(chans, sizeof(mus_long_t));

      rtn = mus_sound_maxamps(filename, chans, vals, times);
      if (rtn != MUS_ERROR)
	{
	  int i;
	  for (i = chans - 1; i >= 0; i--)
	    res = Xen_cons(C_llong_to_Xen_llong(times[i]), Xen_cons(C_double_to_Xen_real(vals[i]), res));
	}
      free(vals);
      free(times);
      if (filename) free(filename);
    }
  else 
    {
      if (filename) free(filename);
      Xen_error(BAD_HEADER,
		Xen_list_1(C_string_to_Xen_string(S_mus_sound_maxamp ": chans <= 0")));
    }
  return(res);
}


static Xen g_mus_sound_set_maxamp(Xen file, Xen vals)
{
  int chans;
  char *filename;

  Xen_check_type(Xen_is_string(file), file, 1, S_set S_mus_sound_maxamp, "a string");
  Xen_check_type(Xen_is_list(vals), vals, 2, S_set S_mus_sound_maxamp, "a list");

  filename = mus_expand_filename(Xen_string_to_C_string(file));
  chans = mus_sound_chans(filename);

  /* presumably any actual error here will be trapped via mus-error (raised in mus_header_read via read_sound_file_header),
   *   so chans <= 0 is actually in the header?
   */
  if (chans > 0)
    {
      Xen lst;
      int i, j, len;
      mus_float_t *mvals;
      mus_long_t *times;

      len = Xen_list_length(vals);
      if (len < (chans * 2))
	Xen_wrong_type_arg_error(S_set S_mus_sound_maxamp, 2, vals, "max amp list length must = 2 * chans");
      if (len > chans * 2) len = chans * 2;

      mvals = (mus_float_t *)calloc(chans, sizeof(mus_float_t));
      times = (mus_long_t *)calloc(chans, sizeof(mus_long_t));

      for (i = 0, j = 0, lst = Xen_copy_arg(vals); i < len; i += 2, j++, lst = Xen_cddr(lst))
	{
	  times[j] = Xen_llong_to_C_llong(Xen_car(lst));
	  mvals[j] = Xen_real_to_C_double(Xen_cadr(lst));
	}

      fprintf(stderr, "set in g_mus_sound_set_maxamp\n");
      mus_sound_set_maxamps(filename, chans, mvals, times);
      free(mvals);
      free(times);
      if (filename) free(filename);
    }
  else 
    {
      if (filename) free(filename);
      Xen_error(BAD_HEADER,
		Xen_list_1(C_string_to_Xen_string(S_set S_mus_sound_maxamp ": chans <= 0")));
    }
  return(vals);
}



#define S_mus_sound_preload "mus-sound-preload"
static Xen g_mus_sound_preload(Xen file)
{
  #define H_mus_sound_preload "(" S_mus_sound_preload " filename): save filename's data in memory (faster opens and so on)."
  char *str;

  Xen_check_type(Xen_is_string(file), file, 1, S_mus_sound_preload, "a string");
  str = mus_expand_filename(Xen_string_to_C_string(file));

  if (str)
    {
      int ifd;
      ifd = mus_sound_open_input(str);
      if (ifd != MUS_ERROR)
	{
	  int i, chans;
	  mus_float_t **bufs;
	  mus_long_t framples;
	  chans = mus_sound_chans(str);
	  framples = mus_sound_framples(str) + 8; /* + 8 for readers that wander off the end */
	  bufs = (mus_float_t **)malloc(chans * sizeof(mus_float_t *));
	  for (i = 0; i < chans; i++)
	    bufs[i] = (mus_float_t *)malloc(framples * sizeof(mus_float_t));
      
	  mus_file_seek_frample(ifd, 0);
	  mus_file_read_file(ifd, 0, chans, framples, bufs);
	  mus_sound_set_saved_data(str, bufs);
	  mus_sound_close_input(ifd);
	}
      free(str);
    }
  return(file);
}


/* global default clipping values */

static Xen g_mus_clipping(void)
{
  #define H_mus_clipping "(" S_mus_clipping "): whether sound data written to a file should be clipped"
  return(C_bool_to_Xen_boolean(mus_clipping()));
}


static Xen g_mus_set_clipping(Xen clipped)
{
  Xen_check_type(Xen_is_boolean(clipped), clipped, 1, S_set S_mus_clipping, "a boolean");
  return(C_bool_to_Xen_boolean(mus_set_clipping(Xen_boolean_to_C_bool(clipped))));
}


/* file local clipping values */

static Xen g_mus_file_clipping(Xen fd)
{
  #define H_mus_file_clipping "(" S_mus_file_clipping " fd): whether sound data written to file 'fd' should be clipped"
  Xen_check_type(Xen_is_integer(fd), fd, 1, S_mus_file_clipping, "an integer");
  return(C_bool_to_Xen_boolean(mus_file_clipping(Xen_integer_to_C_int(fd))));
}


static Xen g_mus_file_set_clipping(Xen fd, Xen clipped)
{
  Xen_check_type(Xen_is_integer(fd), fd, 1, S_set S_mus_file_clipping, "an integer");
  Xen_check_type(Xen_is_boolean(clipped), clipped, 2, S_set S_mus_file_clipping, "a boolean");
  return(C_bool_to_Xen_boolean(mus_file_set_clipping(Xen_integer_to_C_int(fd), Xen_boolean_to_C_bool(clipped))));
}



Xen g_mus_expand_filename(Xen file)
{
  #define H_mus_expand_filename "(" S_mus_expand_filename " name): expand 'name' into a canonical or absolute filename, that is, \
one in which all directories in the path are explicit."
  char *str = NULL;
  Xen result;

  Xen_check_type(Xen_is_string(file), file, 1, S_mus_expand_filename, "a string");
  str = mus_expand_filename(Xen_string_to_C_string(file));
  result = C_string_to_Xen_string(str);
  if (str) free(str);
  return(result);
}


static Xen g_mus_sound_report_cache(Xen file)
{
  #define H_mus_sound_report_cache "(" S_mus_sound_report_cache " (name)): print the current sound \
cache info to the file given or stdout"
  const char *name;

  if (!Xen_is_bound(file))
    {
      mus_sound_report_cache(stdout);
      return(Xen_false);
    }

  Xen_check_type(Xen_is_string(file), file, 1, S_mus_sound_report_cache, "a string");
  name = Xen_string_to_C_string(file);
  if (name)
    {
      char *str = NULL;
      str = mus_expand_filename(name);
      if (str)
	{
	  FILE *fd;
	  fd = FOPEN(str, "w");
	  free(str);
	  if (fd)
	    {
	      mus_sound_report_cache(fd);
	      FCLOSE(fd, name);
	      return(file);
	    }
	}
    }

  Xen_error(Xen_make_error_type("cannot-save"),
	    Xen_list_3(C_string_to_Xen_string(S_mus_sound_report_cache ": ~S ~A"),
		       file,
		       C_string_to_Xen_string(STRERROR(errno))));
  return(Xen_false);
}


static Xen g_mus_error_type_to_string(Xen err)
{
  #define H_mus_error_type_to_string "(" S_mus_error_type_to_string " err): string description of err (a sndlib error type)"
  Xen_check_type(Xen_is_integer(err), err, 1, S_mus_error_type_to_string, "an integer");
  return(C_string_to_Xen_string((char *)mus_error_type_to_string(Xen_integer_to_C_int(err))));
}


static Xen g_array_to_file(Xen filename, Xen data, Xen len, Xen srate, Xen channels)
{
  #define H_array_to_file "(" S_array_to_file " filename data len srate channels): write 'data', \
a " S_vct " of interleaved samples, to the sound file 'filename' set up to have the given \
srate and channels.  'len' samples are written."

  /* this exists for compatibility with the Common Lisp version of CLM. Ideally, we'd get rid
   *   of it and provide vct<->file and sound-data<->file instead so that the channels aren't
   *   handled through interleaving.  But that means extensive changes to the Lisp code...
   */

  mus_long_t olen, samps;
  vct *v;

  Xen_check_type(Xen_is_string(filename), filename, 1, S_array_to_file, "a string");
  Xen_check_type(mus_is_vct(data), data, 2, S_array_to_file, "a " S_vct);
  Xen_check_type(Xen_is_llong(len), len, 3, S_array_to_file, "an integer");
  Xen_check_type(Xen_is_integer(srate), srate, 4, S_array_to_file, "an integer");
  Xen_check_type(Xen_is_integer(channels), channels, 5, S_array_to_file, "an integer");

  v = Xen_to_vct(data);
  samps = Xen_llong_to_C_llong(len);
  if (samps <= 0)
    Xen_out_of_range_error(S_array_to_file, 3, len, "samples <= 0?");
  if (samps > mus_vct_length(v))
    samps = mus_vct_length(v);

  olen = mus_float_array_to_file(Xen_string_to_C_string(filename),
				 mus_vct_data(v),
				 samps,
				 Xen_integer_to_C_int(srate),
				 Xen_integer_to_C_int(channels));
  return(C_llong_to_Xen_llong(olen));
}


static Xen g_file_to_array(Xen filename, Xen chan, Xen start, Xen samples, Xen data)
{
  #define H_file_to_array "(" S_file_to_array " filename chan start samples data): read the sound file \
'filename' placing samples from channel 'chan' into the " S_vct " 'data' starting in the file \
at frample 'start' and reading 'samples' samples altogether."

  int chn, chans;
  mus_long_t samps;
  vct *v;
  const char *name = NULL;

  Xen_check_type(Xen_is_string(filename), filename, 1, S_file_to_array, "a string");
  Xen_check_type(Xen_is_integer(chan), chan, 2, S_file_to_array, "an integer");
  Xen_check_type(Xen_is_llong(start), start, 3, S_file_to_array, "an integer");
  Xen_check_type(Xen_is_llong(samples), samples, 4, S_file_to_array, "an integer");
  Xen_check_type((mus_is_vct(data)), data, 5, S_file_to_array, "a " S_vct);

  name = Xen_string_to_C_string(filename);
  if (!(mus_file_probe(name)))
    Xen_error(NO_SUCH_FILE,
	      Xen_list_3(C_string_to_Xen_string(S_file_to_array ": ~S ~A"),
			 filename,
			 C_string_to_Xen_string(STRERROR(errno))));

  v = Xen_to_vct(data);

  samps = Xen_llong_to_C_llong(samples);
  if (samps <= 0) 
    Xen_out_of_range_error(S_file_to_array, 4, samples, "samples <= 0?");
  if (samps > mus_vct_length(v))
    samps = mus_vct_length(v);

  chn = Xen_integer_to_C_int(chan);
  chans = mus_sound_chans(name);
  if ((chn < 0) || (chn > chans))
    Xen_error(NO_SUCH_CHANNEL,
	      Xen_list_4(C_string_to_Xen_string(S_file_to_array ": invalid chan: ~A, ~S has ~A chans"),
			 chan,
			 filename,
			 C_int_to_Xen_integer(chans)));

  if (chans <= 0)
    Xen_error(BAD_HEADER,
	      Xen_list_2(C_string_to_Xen_string(S_file_to_array ": ~S chans <= 0"),
			 filename));

  mus_file_to_float_array(name, chn, Xen_llong_to_C_llong(start), samps, mus_vct_data(v));
  return(data);
}


static Xen new_sound_hook;

static void g_new_sound_hook(const char *filename)
{
  if (Xen_hook_has_list(new_sound_hook))
    {
#if HAVE_SCHEME
      s7_call(s7, new_sound_hook, s7_cons(s7, C_string_to_Xen_string(filename), Xen_empty_list));
#else
      Xen procs, fname;
      fname = C_string_to_Xen_string(filename);
      procs = Xen_hook_list(new_sound_hook);
      while (!Xen_is_null(procs))
	{
	  Xen_call_with_1_arg(Xen_car(procs), fname, S_new_sound_hook);
	  procs = Xen_cdr(procs);
	}
#endif
    }
}


static Xen sound_path;
Xen g_mus_sound_path(void)
{
  #define H_mus_sound_path "(" S_mus_sound_path "): a list of directories to search for sound files."
  return(sound_path);
}

#if HAVE_SCHEME
  static int sound_path_loc = -1;
  static s7_pointer mus_sound_path_symbol;
#endif

static Xen g_mus_set_sound_path(Xen val)
{
  Xen_check_type(Xen_is_list(val), val, 1, S_set S_mus_sound_path, "a list");
#if HAVE_SCHEME
  if (sound_path_loc != -1)
    s7_gc_unprotect_at(s7, sound_path_loc);
  sound_path = val;
  sound_path_loc = s7_gc_protect(s7, sound_path);
  s7_symbol_set_value(s7, mus_sound_path_symbol, val);
#else
  if (sound_path != Xen_empty_list)
    Xen_GC_unprotect(sound_path);
  Xen_GC_protect(val);
  sound_path = val;
#endif
  return(val);
}


static Xen g_mus_max_malloc(void)
{
  #define H_mus_max_malloc "(" S_mus_max_malloc "): maximum number of bytes we will try to malloc."
  return(C_llong_to_Xen_llong(mus_max_malloc()));
}

#if HAVE_SCHEME
  static s7_pointer mus_max_malloc_symbol;
#endif

static Xen g_mus_set_max_malloc(Xen val)
{
  mus_long_t size;
  Xen_check_type(Xen_is_llong(val), val, 1, S_set S_mus_max_malloc, "an integer");
  size = Xen_llong_to_C_llong(val);
#if HAVE_SCHEME
  s7_symbol_set_value(s7, mus_max_malloc_symbol, s7_make_integer(s7, size));
#endif
  return(C_llong_to_Xen_llong(mus_set_max_malloc(size)));
}



static Xen g_mus_max_table_size(void)
{
  #define H_mus_max_table_size "(" S_mus_max_table_size "): maximum table size."
  return(C_llong_to_Xen_llong(mus_max_table_size()));
}


#if HAVE_SCHEME
  static s7_pointer mus_max_table_size_symbol;
#endif

static Xen g_mus_set_max_table_size(Xen val)
{
  mus_long_t size;
  Xen_check_type(Xen_is_llong(val), val, 1, S_set S_mus_max_table_size, "an integer");
  size = Xen_llong_to_C_llong(val);
#if HAVE_SCHEME
  s7_symbol_set_value(s7, mus_max_table_size_symbol, s7_make_integer(s7, size));
#endif
  return(C_llong_to_Xen_llong(mus_set_max_table_size(size)));
}


#if __APPLE__
#define S_mus_audio_output_properties_mutable "mus-audio-output-properties-mutable"
static Xen g_mus_audio_output_properties_mutable(Xen mut)
{
  #define H_mus_audio_output_properties_mutable "(" S_mus_audio_output_properties_mutable " val): can DAC settings be changed to match the current sound"
  Xen_check_type(Xen_is_boolean(mut), mut, 1, S_mus_audio_output_properties_mutable, "a boolean");
  return(C_bool_to_Xen_boolean(mus_audio_output_properties_mutable(Xen_boolean_to_C_bool(mut))));
}
#endif


Xen_wrap_1_arg(g_mus_sound_samples_w, g_mus_sound_samples)
Xen_wrap_2_args(g_mus_sound_set_samples_w, g_mus_sound_set_samples)
Xen_wrap_1_arg(g_mus_sound_framples_w, g_mus_sound_framples)
Xen_wrap_1_arg(g_mus_sound_duration_w, g_mus_sound_duration)
Xen_wrap_1_arg(g_mus_sound_datum_size_w, g_mus_sound_datum_size)
Xen_wrap_1_arg(g_mus_sound_data_location_w, g_mus_sound_data_location)
Xen_wrap_2_args(g_mus_sound_set_data_location_w, g_mus_sound_set_data_location)
Xen_wrap_1_arg(g_mus_sound_chans_w, g_mus_sound_chans)
Xen_wrap_2_args(g_mus_sound_set_chans_w, g_mus_sound_set_chans)
Xen_wrap_1_arg(g_mus_sound_srate_w, g_mus_sound_srate)
Xen_wrap_2_args(g_mus_sound_set_srate_w, g_mus_sound_set_srate)
Xen_wrap_1_arg(g_mus_sound_header_type_w, g_mus_sound_header_type)
Xen_wrap_2_args(g_mus_sound_set_header_type_w, g_mus_sound_set_header_type)
Xen_wrap_1_arg(g_mus_sound_sample_type_w, g_mus_sound_sample_type)
Xen_wrap_2_args(g_mus_sound_set_sample_type_w, g_mus_sound_set_sample_type)
Xen_wrap_1_arg(g_mus_sound_length_w, g_mus_sound_length)
Xen_wrap_1_arg(g_mus_sound_type_specifier_w, g_mus_sound_type_specifier)
Xen_wrap_1_arg(g_mus_header_type_name_w, g_mus_header_type_name)
Xen_wrap_1_arg(g_mus_header_type_to_string_w, g_mus_header_type_to_string)
Xen_wrap_1_arg(g_mus_sample_type_name_w, g_mus_sample_type_name)
Xen_wrap_1_arg(g_mus_sample_type_to_string_w, g_mus_sample_type_to_string)
Xen_wrap_1_arg(g_mus_sound_comment_w, g_mus_sound_comment)
Xen_wrap_1_arg(g_mus_sound_write_date_w, g_mus_sound_write_date)
Xen_wrap_1_arg(g_mus_bytes_per_sample_w, g_mus_bytes_per_sample)
Xen_wrap_1_arg(g_mus_sound_loop_info_w, g_mus_sound_loop_info)
Xen_wrap_1_arg(g_mus_sound_mark_info_w, g_mus_sound_mark_info)
Xen_wrap_1_arg(g_mus_sound_maxamp_w, g_mus_sound_maxamp)
Xen_wrap_2_args(g_mus_sound_set_maxamp_w, g_mus_sound_set_maxamp)
Xen_wrap_1_arg(g_mus_sound_maxamp_exists_w, g_mus_sound_maxamp_exists)
Xen_wrap_1_arg(g_mus_sound_preload_w, g_mus_sound_preload)

Xen_wrap_no_args(g_mus_clipping_w, g_mus_clipping)
Xen_wrap_1_arg(g_mus_set_clipping_w, g_mus_set_clipping)
Xen_wrap_1_arg(g_mus_file_clipping_w, g_mus_file_clipping)
Xen_wrap_2_args(g_mus_file_set_clipping_w, g_mus_file_set_clipping)
Xen_wrap_no_args(g_mus_header_raw_defaults_w, g_mus_header_raw_defaults)
Xen_wrap_1_arg(g_mus_header_set_raw_defaults_w, g_mus_header_set_raw_defaults)
Xen_wrap_2_args(g_mus_header_writable_w, g_mus_header_writable)
Xen_wrap_1_arg(g_mus_expand_filename_w, g_mus_expand_filename)
Xen_wrap_1_optional_arg(g_mus_sound_report_cache_w, g_mus_sound_report_cache)
Xen_wrap_1_arg(g_mus_sound_forget_w, g_mus_sound_forget)
Xen_wrap_no_args(g_mus_sound_prune_w, g_mus_sound_prune)
Xen_wrap_1_arg(g_mus_error_type_to_string_w, g_mus_error_type_to_string)
Xen_wrap_2_args(g_mus_oss_set_buffers_w, g_mus_oss_set_buffers)
Xen_wrap_5_args(g_array_to_file_w, g_array_to_file)
Xen_wrap_5_args(g_file_to_array_w, g_file_to_array)
Xen_wrap_no_args(g_mus_alsa_buffers_w, g_mus_alsa_buffers)
Xen_wrap_1_arg(g_mus_alsa_set_buffers_w, g_mus_alsa_set_buffers)
Xen_wrap_no_args(g_mus_alsa_buffer_size_w, g_mus_alsa_buffer_size)
Xen_wrap_1_arg(g_mus_alsa_set_buffer_size_w, g_mus_alsa_set_buffer_size)
Xen_wrap_no_args(g_mus_alsa_device_w, g_mus_alsa_device)
Xen_wrap_1_arg(g_mus_alsa_set_device_w, g_mus_alsa_set_device)
Xen_wrap_no_args(g_mus_alsa_playback_device_w, g_mus_alsa_playback_device)
Xen_wrap_1_arg(g_mus_alsa_set_playback_device_w, g_mus_alsa_set_playback_device)
Xen_wrap_no_args(g_mus_alsa_capture_device_w, g_mus_alsa_capture_device)
Xen_wrap_1_arg(g_mus_alsa_set_capture_device_w, g_mus_alsa_set_capture_device)
Xen_wrap_no_args(g_mus_alsa_squelch_warning_w, g_mus_alsa_squelch_warning)
Xen_wrap_1_arg(g_mus_alsa_set_squelch_warning_w, g_mus_alsa_set_squelch_warning)

#if __APPLE__
Xen_wrap_1_arg(g_mus_audio_output_properties_mutable_w, g_mus_audio_output_properties_mutable)
#endif

Xen_wrap_no_args(g_mus_max_malloc_w, g_mus_max_malloc)
Xen_wrap_1_arg(g_mus_set_max_malloc_w, g_mus_set_max_malloc)
Xen_wrap_no_args(g_mus_max_table_size_w, g_mus_max_table_size)
Xen_wrap_1_arg(g_mus_set_max_table_size_w, g_mus_set_max_table_size)
Xen_wrap_no_args(g_mus_sound_path_w, g_mus_sound_path)
Xen_wrap_1_arg(g_mus_set_sound_path_w, g_mus_set_sound_path)

#if HAVE_SCHEME
  static s7_pointer acc_mus_max_table_size(s7_scheme *sc, s7_pointer args) {return(g_mus_set_max_table_size(s7_cadr(args)));}  
  static s7_pointer acc_mus_max_malloc(s7_scheme *sc, s7_pointer args) {return(g_mus_set_max_malloc(s7_cadr(args)));}  
  static s7_pointer acc_mus_sound_path(s7_scheme *sc, s7_pointer args) {return(g_mus_set_sound_path(s7_cadr(args)));}  
#endif


void mus_sndlib_xen_initialize(void)
{
#if HAVE_SCHEME
  s7_pointer pl_is, pl_isi, pl_si, pl_ss, pl_ps, pl_psp, pl_i, pl_bii, pl_p, pl_rs, pl_bi, pl_bib, pl_b, pl_ls;
  s7_pointer pl_l, pl_isfiii, pl_fsiiif, pl_bs, pl_ts, pl_sh, pl_bhi;
#endif

  mus_sound_initialize();
  sound_path = Xen_empty_list;

#if HAVE_RUBY
  Init_Hook();
#endif

  Xen_define_constant(S_mus_out_format,           MUS_OUT_SAMPLE_TYPE,      "sample type for fastest IO");
  Xen_define_constant(S_mus_unknown_header,       MUS_UNKNOWN_HEADER,       "unknown header type");
  Xen_define_constant(S_mus_next,                 MUS_NEXT,                 "NeXT (Sun) sound header id");
  Xen_define_constant(S_mus_aifc,                 MUS_AIFC,                 "AIFC sound header id");
  Xen_define_constant(S_mus_rf64,                 MUS_RF64,                 "RF64 sound header id");
  Xen_define_constant(S_mus_riff,                 MUS_RIFF,                 "RIFF (MS wave) sound header id");
  Xen_define_constant(S_mus_nist,                 MUS_NIST,                 "NIST (Sphere) sound header id");
  Xen_define_constant(S_mus_raw,                  MUS_RAW,                  "raw (headerless) sound header id");
  Xen_define_constant(S_mus_ircam,                MUS_IRCAM,                "IRCAM sound header id");
  Xen_define_constant(S_mus_aiff,                 MUS_AIFF,                 "AIFF (old-style) sound header id");
  Xen_define_constant(S_mus_bicsf,                MUS_BICSF,                "BICSF header id");
  Xen_define_constant(S_mus_voc,                  MUS_VOC,                  "VOC header id");
  Xen_define_constant(S_mus_svx,                  MUS_SVX,                  "SVX (IFF) header id");
  Xen_define_constant(S_mus_soundfont,            MUS_SOUNDFONT,            "soundfont header id");
  Xen_define_constant(S_mus_caff,                 MUS_CAFF,                 "Apple Core Audio File Format header id");

  Xen_define_constant(S_mus_unknown_sample,       MUS_UNKNOWN_SAMPLE,       "unknown sample type");
  Xen_define_constant(S_mus_bshort,               MUS_BSHORT,               "big-endian short sample type id");
  Xen_define_constant(S_mus_lshort,               MUS_LSHORT,               "little-endian short sample type id");
  Xen_define_constant(S_mus_mulaw,                MUS_MULAW,                "mulaw (8-bit) sample type id");
  Xen_define_constant(S_mus_alaw,                 MUS_ALAW,                 "alaw (8-bit) sample type id");
  Xen_define_constant(S_mus_byte,                 MUS_BYTE,                 "signed byte sample type id");
  Xen_define_constant(S_mus_ubyte,                MUS_UBYTE,                "unsigned byte sample type id");
  Xen_define_constant(S_mus_bfloat,               MUS_BFLOAT,               "big-endian float sample type id");
  Xen_define_constant(S_mus_lfloat,               MUS_LFLOAT,               "little-endian float sample type id");
  Xen_define_constant(S_mus_bint,                 MUS_BINT,                 "big-endian int sample type id");
  Xen_define_constant(S_mus_lint,                 MUS_LINT,                 "little-endian int sample type id");
  Xen_define_constant(S_mus_bintn,                MUS_BINTN,                "normalized big-endian int sample type id");
  Xen_define_constant(S_mus_lintn,                MUS_LINTN,                "normalized little-endian int sample type id");
  Xen_define_constant(S_mus_b24int,               MUS_B24INT,               "big-endian 24-bit sample type id");
  Xen_define_constant(S_mus_l24int,               MUS_L24INT,               "little-endian 24-bit sample type id");
  Xen_define_constant(S_mus_bdouble,              MUS_BDOUBLE,              "big-endian double sample type id");
  Xen_define_constant(S_mus_ldouble,              MUS_LDOUBLE,              "little-endian double sample type id");
  Xen_define_constant(S_mus_ubshort,              MUS_UBSHORT,              "unsigned big-endian short sample type id");
  Xen_define_constant(S_mus_ulshort,              MUS_ULSHORT,              "unsigned little-endian short sample type id");
  Xen_define_constant(S_mus_bdouble_unscaled,     MUS_BDOUBLE_UNSCALED,     "unscaled big-endian double sample type id");
  Xen_define_constant(S_mus_ldouble_unscaled,     MUS_LDOUBLE_UNSCALED,     "unscaled little-endian double sample type id");
  Xen_define_constant(S_mus_bfloat_unscaled,      MUS_BFLOAT_UNSCALED,      "unscaled big-endian float sample type id");
  Xen_define_constant(S_mus_lfloat_unscaled,      MUS_LFLOAT_UNSCALED,      "unscaled little-endian float sample type id");

#if HAVE_SCHEME
  s7_eval_c_string(s7, 
    "(define (mus_header_t? form argn) \
       (let ((h (list-ref form argn))) \
         (if (not (memq h '(mus-next mus-aifc mus-riff mus-nist mus-raw mus-ircam mus-aiff mus-bicsf mus-voc mus-svx mus-soundfont mus-rf64 mus-caff))) \
             (if (not (integer? h)) \
                 'integer? \
                 (if (not (< 0 h 71)) \
                     \"an integer between 1 and 70\")))))");
  {
    s7_pointer s, i, p, b, r, f, t, l, h;
    s = s7_make_symbol(s7, "string?");
    i = s7_make_symbol(s7, "integer?");
    p = s7_make_symbol(s7, "pair?");
    l = s7_make_symbol(s7, "list?");
    b = s7_make_symbol(s7, "boolean?");
    r = s7_make_symbol(s7, "real?");
    f = s7_make_symbol(s7, "float-vector?");
    h = s7_make_symbol(s7, "mus_header_t?");
    t = s7_t(s7);
    pl_is = s7_make_signature(s7, 2, i, s);
    pl_isi = s7_make_signature(s7, 3, i, s, i);
    pl_si = s7_make_signature(s7, 2, s, i);
    pl_sh = s7_make_signature(s7, 2, s, h);
    pl_ss = s7_make_signature(s7, 2, s, s);
    pl_ts = s7_make_signature(s7, 2, t, s);
    pl_ls = s7_make_signature(s7, 2, l, s);
    pl_ps = s7_make_signature(s7, 2, p, s);
    pl_psp = s7_make_signature(s7, 3, p, s, p);
    pl_i = s7_make_circular_signature(s7, 0, 1, i);
    pl_bii = s7_make_signature(s7, 3, b, i, i);
    pl_bhi = s7_make_signature(s7, 3, b, h, i);
    pl_p = s7_make_circular_signature(s7, 0, 1, p);
    pl_l = s7_make_circular_signature(s7, 0, 1, l);
    pl_rs = s7_make_signature(s7, 2, r, s);
    pl_bi = s7_make_signature(s7, 2, b, i);
    pl_bib = s7_make_signature(s7, 3, b, i, b);
    pl_b = s7_make_circular_signature(s7, 0, 1, b);
    pl_bs = s7_make_signature(s7, 2, b, s);
    pl_isfiii = s7_make_signature(s7, 6, i, s, f, i, i, i);
    pl_fsiiif = s7_make_signature(s7, 6, f, s, i, i, i, f);
  }
#endif

  Xen_define_typed_dilambda(S_mus_sound_samples, g_mus_sound_samples_w, H_mus_sound_samples,  
			    S_set S_mus_sound_samples, g_mus_sound_set_samples_w, 1, 0, 2, 0, pl_is, pl_isi);
  Xen_define_typed_dilambda(S_mus_sound_data_location, g_mus_sound_data_location_w, H_mus_sound_data_location,
			    S_set S_mus_sound_data_location, g_mus_sound_set_data_location_w, 1, 0, 2, 0, pl_is, pl_isi);
  Xen_define_typed_dilambda(S_mus_sound_chans, g_mus_sound_chans_w, H_mus_sound_chans, 
			    S_set S_mus_sound_chans, g_mus_sound_set_chans_w, 1, 0, 2, 0, pl_is, pl_isi);
  Xen_define_typed_dilambda(S_mus_sound_srate, g_mus_sound_srate_w, H_mus_sound_srate,
			    S_set S_mus_sound_srate, g_mus_sound_set_srate_w, 1, 0, 2, 0, pl_is, pl_isi);
  Xen_define_typed_dilambda(S_mus_sound_header_type, g_mus_sound_header_type_w, H_mus_sound_header_type,
			    S_set S_mus_sound_header_type, g_mus_sound_set_header_type_w, 1, 0, 2, 0, pl_is, pl_isi);
  Xen_define_typed_dilambda(S_mus_sound_sample_type, g_mus_sound_sample_type_w, H_mus_sound_sample_type,
			    S_set S_mus_sound_sample_type, g_mus_sound_set_sample_type_w, 1, 0, 2, 0, pl_is, pl_isi);

  Xen_define_typed_procedure(S_mus_sound_framples,       g_mus_sound_framples_w,         1, 0, 0, H_mus_sound_framples,	       pl_is);
  Xen_define_typed_procedure("mus-sound-frames",         g_mus_sound_framples_w,         1, 0, 0, H_mus_sound_framples,        pl_is);
  Xen_define_typed_procedure(S_mus_sound_duration,       g_mus_sound_duration_w,         1, 0, 0, H_mus_sound_duration,        pl_rs);
  Xen_define_typed_procedure(S_mus_sound_datum_size,     g_mus_sound_datum_size_w,       1, 0, 0, H_mus_sound_datum_size,      pl_is);
  Xen_define_typed_procedure(S_mus_sound_length,         g_mus_sound_length_w,           1, 0, 0, H_mus_sound_length,          pl_is);
  Xen_define_typed_procedure(S_mus_sound_type_specifier, g_mus_sound_type_specifier_w,   1, 0, 0, H_mus_sound_type_specifier,  pl_is);
  Xen_define_typed_procedure(S_mus_header_type_name,     g_mus_header_type_name_w,       1, 0, 0, H_mus_header_type_name,      pl_sh);
  Xen_define_typed_procedure(S_mus_header_type_to_string,g_mus_header_type_to_string_w,  1, 0, 0, H_mus_header_type_to_string, pl_sh);
  Xen_define_typed_procedure(S_mus_header_writable,      g_mus_header_writable_w,        2, 0, 0, H_mus_header_writable,       pl_bhi);
  Xen_define_typed_procedure(S_mus_sample_type_name,     g_mus_sample_type_name_w,       1, 0, 0, H_mus_sample_type_name,      pl_si);
  Xen_define_typed_procedure(S_mus_sample_type_to_string,g_mus_sample_type_to_string_w,  1, 0, 0, H_mus_sample_type_to_string, pl_si);
  Xen_define_typed_procedure(S_mus_sound_comment,        g_mus_sound_comment_w,          1, 0, 0, H_mus_sound_comment,         pl_ts);
  Xen_define_typed_procedure(S_mus_sound_write_date,     g_mus_sound_write_date_w,       1, 0, 0, H_mus_sound_write_date,      pl_is);
  Xen_define_typed_procedure(S_mus_bytes_per_sample,     g_mus_bytes_per_sample_w,       1, 0, 0, H_mus_bytes_per_sample,      pl_i);
  Xen_define_typed_procedure(S_mus_sound_loop_info,      g_mus_sound_loop_info_w,        1, 0, 0, H_mus_sound_loop_info,       pl_ls);
  Xen_define_typed_procedure(S_mus_sound_mark_info,      g_mus_sound_mark_info_w,        1, 0, 0, H_mus_sound_mark_info,       pl_ps);
  Xen_define_typed_procedure(S_mus_sound_maxamp_exists,  g_mus_sound_maxamp_exists_w,    1, 0, 0, H_mus_sound_maxamp_exists,   pl_bs);
  Xen_define_typed_procedure(S_mus_sound_forget,         g_mus_sound_forget_w,           1, 0, 0, H_mus_sound_forget,          pl_is);
  Xen_define_typed_procedure(S_mus_sound_prune,          g_mus_sound_prune_w,            0, 0, 0, H_mus_sound_prune,           pl_i);

  Xen_define_typed_procedure(S_mus_expand_filename,      g_mus_expand_filename_w,        1, 0, 0, H_mus_expand_filename,       pl_ss);
  Xen_define_typed_procedure(S_mus_sound_report_cache,   g_mus_sound_report_cache_w,     0, 1, 0, H_mus_sound_report_cache,    NULL);
  Xen_define_typed_procedure(S_mus_error_type_to_string, g_mus_error_type_to_string_w,   1, 0, 0, H_mus_error_type_to_string,  pl_si);
  Xen_define_typed_procedure(S_mus_oss_set_buffers,      g_mus_oss_set_buffers_w,        2, 0, 0, H_mus_oss_set_buffers,       pl_bii);
  Xen_define_typed_procedure(S_array_to_file,            g_array_to_file_w,              5, 0, 0, H_array_to_file,             pl_isfiii);
  Xen_define_typed_procedure(S_file_to_array,            g_file_to_array_w,              5, 0, 0, H_file_to_array,             pl_fsiiif);

  Xen_define_typed_procedure(S_mus_sound_preload,        g_mus_sound_preload_w,          1, 0, 0, H_mus_sound_preload,         pl_ss);

  Xen_define_typed_dilambda(S_mus_header_raw_defaults, g_mus_header_raw_defaults_w, H_mus_header_raw_defaults,
			    S_set S_mus_header_raw_defaults, g_mus_header_set_raw_defaults_w, 0, 0, 1, 0, pl_p, pl_p);

  Xen_define_typed_dilambda(S_mus_clipping, g_mus_clipping_w, H_mus_clipping, 
			    S_set S_mus_clipping, g_mus_set_clipping_w, 0, 0, 1, 0, pl_b, pl_b);
  Xen_define_typed_dilambda(S_mus_file_clipping, g_mus_file_clipping_w, H_mus_file_clipping, 
			    S_set S_mus_file_clipping, g_mus_file_set_clipping_w, 1, 0, 2, 0, pl_bi, pl_bib);
  Xen_define_typed_dilambda(S_mus_sound_maxamp, g_mus_sound_maxamp_w, H_mus_sound_maxamp, 
			    S_set S_mus_sound_maxamp, g_mus_sound_set_maxamp_w, 1, 0, 2, 0, pl_ps, pl_psp);

  /* these are no-ops if not ALSA, but that makes it easier to maintain global initialization files */
  Xen_define_typed_dilambda(S_mus_alsa_buffers, g_mus_alsa_buffers_w, H_mus_alsa_buffers, S_set 
			    S_mus_alsa_buffers, g_mus_alsa_set_buffers_w, 0, 0, 1, 0, NULL, NULL);
  Xen_define_typed_dilambda(S_mus_alsa_buffer_size, g_mus_alsa_buffer_size_w, H_mus_alsa_buffer_size, 
			    S_set S_mus_alsa_buffer_size, g_mus_alsa_set_buffer_size_w, 0, 0, 1, 0, NULL, NULL);
  Xen_define_typed_dilambda(S_mus_alsa_device, g_mus_alsa_device_w, H_mus_alsa_device, 
			    S_set S_mus_alsa_device, g_mus_alsa_set_device_w, 0, 0, 1, 0, NULL, NULL);
  Xen_define_typed_dilambda(S_mus_alsa_playback_device, g_mus_alsa_playback_device_w, H_mus_alsa_playback_device, 
			    S_set S_mus_alsa_playback_device, g_mus_alsa_set_playback_device_w, 0, 0, 1, 0, NULL, NULL);
  Xen_define_typed_dilambda(S_mus_alsa_capture_device, g_mus_alsa_capture_device_w, H_mus_alsa_capture_device, 
			    S_set S_mus_alsa_capture_device, g_mus_alsa_set_capture_device_w, 0, 0, 1, 0, NULL, NULL);
  Xen_define_typed_dilambda(S_mus_alsa_squelch_warning, g_mus_alsa_squelch_warning_w, H_mus_alsa_squelch_warning,
			    S_set S_mus_alsa_squelch_warning, g_mus_alsa_set_squelch_warning_w, 0, 0, 1, 0, NULL, NULL);

  Xen_define_typed_dilambda(S_mus_max_malloc, g_mus_max_malloc_w, H_mus_max_malloc, 
			    S_set S_mus_max_malloc, g_mus_set_max_malloc_w, 0, 0, 1, 0, pl_i, pl_i);
  Xen_define_typed_dilambda(S_mus_max_table_size, g_mus_max_table_size_w, H_mus_max_table_size, 
			    S_set S_mus_max_table_size, g_mus_set_max_table_size_w, 0, 0, 1, 0, pl_i, pl_i);
  Xen_define_typed_dilambda(S_mus_sound_path, g_mus_sound_path_w, H_mus_sound_path, 
			    S_set S_mus_sound_path, g_mus_set_sound_path_w, 0, 0, 1, 0, pl_l, pl_l);

#if HAVE_SCHEME
  mus_max_table_size_symbol = s7_define_variable(s7, "*" S_mus_max_table_size "*", s7_make_integer(s7, MUS_MAX_TABLE_SIZE_DEFAULT));
  s7_symbol_set_documentation(s7, mus_max_table_size_symbol, "*mus-max-table-size*: maximum table size.");
  s7_symbol_set_access(s7, mus_max_table_size_symbol, s7_make_function(s7, "[acc-mus-max-table-size]" "]", acc_mus_max_table_size, 2, 0, false, "accessor"));

  mus_max_malloc_symbol = s7_define_variable(s7, "*" S_mus_max_malloc "*", s7_make_integer(s7, MUS_MAX_MALLOC_DEFAULT));
  s7_symbol_set_documentation(s7, mus_max_malloc_symbol, "*mus-max-malloc*: maximum number of bytes we will try to malloc.");
  s7_symbol_set_access(s7, mus_max_malloc_symbol, s7_make_function(s7, "[acc-mus-max-malloc]" "]", acc_mus_max_malloc, 2, 0, false, "accessor"));

  mus_sound_path_symbol = s7_define_variable(s7, "*" S_mus_sound_path "*", s7_nil(s7));
  s7_symbol_set_documentation(s7, mus_sound_path_symbol, "*" S_mus_sound_path "* is a list of directories to search for sound files");
  s7_symbol_set_access(s7, mus_sound_path_symbol, s7_make_function(s7, "[acc-mus-sound-path]" "]", acc_mus_sound_path, 2, 0, false, "accessor"));
#endif

#if __APPLE__
  Xen_define_typed_procedure(S_mus_audio_output_properties_mutable, g_mus_audio_output_properties_mutable_w, 1, 0, 0, H_mus_audio_output_properties_mutable, pl_b);
#endif

  #define H_new_sound_hook S_new_sound_hook "(name): called when a new sound file is being created"
  new_sound_hook = Xen_define_hook(S_new_sound_hook, "(make-hook 'name)", 1, H_new_sound_hook);
  mus_header_write_set_hook(g_new_sound_hook);

  Xen_provide_feature("sndlib");
}
