#include "snd.h"

#ifndef VMS
#if defined(NEXT) || defined(HAVE_SYS_DIR_H)
  #include <sys/dir.h>
  #include <sys/dirent.h>
  #define dirent direct
#else
  #ifdef WINDOZE
    #include <direct.h>
  #else
    #include <dirent.h>
  #endif
#endif
#else
#include "vms_dirent.h"
#define lstat stat
#endif

      
#if defined(SGI) || defined(SCO5) || defined(UW2) || defined(SOLARIS) || defined(HAVE_SYS_STATFS_H)
  #include <sys/statfs.h>
#else
  #if defined(HPUX) || defined(LINUX) || defined(NEXT) || defined(HAVE_SYS_VFS_H)
    #include <sys/vfs.h>
  #else
    #if defined(ALPHA)
      #include <sys/mount.h>
    #endif
  #endif
#endif

#if defined(BEOS) || defined(WINDOZE)
  int disk_kspace (int fd) {return(1234567);}
  time_t file_write_date(char *filename) {return(1234567);}
  int is_link(char *filename) {return(0);}

#else /* not BEOS */

int disk_kspace (int fd)
{
#ifndef VMS
  struct statfs buf;
  int err;
#if defined(SGI) || defined(SCO5) || defined(UW2) || defined(SOLARIS) || defined(HAVE_SYS_STATFS_H)
    err = fstatfs(fd,&buf,sizeof(buf),0);
#else
  #if defined(HPUX) || defined(LINUX) || defined(NEXT) || defined(ALPHA) || defined(HAVE_SYS_VFS_H)
    err = fstatfs(fd,&buf);
  #endif
#endif
  /* in 32 bit land, the number of bytes can easily go over 2^32, so we'll look at kbytes here */
  if (err == 0) 
    {
#ifndef NEXT
      if (buf.f_bsize == 1024) return(buf.f_bfree);
      else if (buf.f_bsize == 512) return(buf.f_bfree >> 1);
      else return(buf.f_bsize * ((float)(buf.f_bfree)/1024.0));
#else
      return(buf.f_bavail);
#endif
    }
  return(err);
#else
int fakespace;
fakespace = 1000000000;  /* False 1 Gbyte space indication */
return(fakespace);
#endif
}

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

int is_link(char *filename)
{
  struct stat statbuf;
  if (lstat(filename,&statbuf) >= 0) return(S_ISLNK(statbuf.st_mode));
  return(0);
}

#endif /* not BEOS */


file_info *make_file_info_1(char *fullname, snd_state *ss)
{
  int len;
  file_info *hdr;
  hdr = (file_info *)calloc(1,sizeof(file_info));
  hdr->s_type = make_snd_pointer_type(FILE_INFO);
  len=strlen(fullname);
  hdr->name = (char *)calloc(len+1,sizeof(char));
  strcpy(hdr->name,fullname);
  hdr->samples = sound_samples(fullname); /* total samples, not per channel */
  hdr->data_location = sound_data_location(fullname);
  hdr->srate = sound_srate(fullname);
  if ((hdr->srate) == 0) hdr->srate = 1;
  hdr->chans = sound_chans(fullname);
  if ((hdr->chans) == 0) hdr->chans = 1;
  hdr->format = sound_data_format(fullname);
  hdr->type = sound_header_type(fullname);
  hdr->distributed = sound_distributed(fullname);
  hdr->comment_start = sound_comment_start(fullname);
  hdr->comment_end = sound_comment_end(fullname);
  hdr->comment = NULL;
  return(hdr);
}

file_info *copy_header(char *fullname, file_info *ohdr)
{
  int len;
  file_info *hdr;
  hdr = (file_info *)calloc(1,sizeof(file_info));
  hdr->s_type = make_snd_pointer_type(FILE_INFO);
  len=strlen(fullname);
  hdr->name = (char *)calloc(len+1,sizeof(char));
  strcpy(hdr->name,fullname);
  hdr->samples = ohdr->samples;
  hdr->data_location = ohdr->data_location;
  hdr->srate = ohdr->srate;
  hdr->chans = ohdr->chans;
  hdr->format = ohdr->format;
  hdr->type = ohdr->type;
  hdr->distributed = ohdr->distributed;
  hdr->comment_start = 0;
  hdr->comment_end = 0;
  hdr->comment = NULL;
  return(hdr);
}

static file_info *translate_file(char *filename, snd_state *ss)
{
  char *newname,*tempname,*txt;
  file_info *hdr = NULL;
  int err,len,fd;
  len = strlen(filename);
  newname = (char *)calloc(len+5,sizeof(char));
  strcpy(newname,filename);
  /* too many special cases to do anything smart here -- I'll just tack on '.snd' */
  newname[len]='.';
  newname[len+1]='s';
  newname[len+2]='n';
  newname[len+3]='d';
  newname[len+4]='\0';
  /* before calling the translator we need to check for write-access to the current directory,
   * and if none, try to find a temp directory we can write to
   */
  fd = creat(newname,0666);
  if (fd == -1)
    {
      tempname = snd_tempnam(temp_dir(ss),"snd_");
      fd = creat(tempname,0666);
      if (fd == -1)
	{
	  fprintf(stderr,snd_string_cant_write_translator_file,newname,tempname);
	  /* this is trying to get an error message out no matter what state Snd is in */
	  txt = (char *)calloc(128,sizeof(char));
	  sprintf(txt,snd_string_cant_write_translator_file,newname,tempname);
	  snd_printf(ss,txt);
	  free(txt);
	  return(NULL);
	}
      free(newname);
      newname = tempname;
    }
  close(fd);
  err = snd_translate(ss,filename,newname);
  if (err == 0)
    {
      err = c_read_header(newname);
      if (err == 0)
	{
	  hdr = make_file_info_1(newname,ss);
	  if (hdr) ss->pending_change = newname;
	}
    }
  return(hdr);
}

static file_info *make_file_info_2(char *fullname, snd_state *ss)
{
  int type,format,orig,fd;
  /* not using clm_open_read via c_read_header to avoid perror */
  fd = open(fullname,O_RDONLY,0);
  if (fd != -1)
    {
      close(fd);  
      type = sound_header_type(fullname);
      format = sound_data_format(fullname);
      orig = sound_original_format(fullname);
      if ((sound_srate(fullname) <= 0) || (sound_srate(fullname) > 100000000) ||
	  (sound_chans(fullname) >= 256) || (sound_chans(fullname) <= 0))
	{
	  return(get_reasonable_file_info(fullname,ss,make_file_info_1(fullname,ss)));
	}
      if (format == snd_unsupported)
	{
	  /* look at original format and type and see if we can translate it */
	  switch (type)
	    {
	    case RIFF_sound_file: 
	      if ((orig == RIFF_IBM_CVSD) || (orig == RIFF_Intel_ADPCM) || (orig == RIFF_G723) || (orig == RIFF_MS_ADPCM) ||
		  (orig == RIFF_G721) || (orig == RIFF_Gsm610) || (orig == RIFF_Oki_ADPCM))
		return(translate_file(fullname,ss));
	      break;
	    case AIFF_sound_file:
	      if (orig == AIFF_IMA_ADPCM) return(translate_file(fullname,ss));
	      break;
	    case NeXT_sound_file:
	      if ((orig == NeXT_G721) || (orig == NeXT_G723) || (orig == NeXT_G723_5))
		return(translate_file(fullname,ss));
	      break;
	    case NIST_sound_file:
	      if (orig == NIST_shortpack) return(translate_file(fullname,ss));
	      break;
	    case IEEE_sound_file: case MUS10_sound_file: case HCOM_sound_file: case AVI_sound_file:
	    case Matlab_sound_file: case MIDI_sample_dump: case Yamaha_TX16_sound_file: case SVX_sound_file:
	      return(translate_file(fullname,ss));
	      break;
	    }
	  snd_IO_error = snd_unsupported_data_format;
	  return(NULL);
	}
      if (type == raw_sound_file)
	{
	  /* need type format chans srate (latter 2 need to over-ride current for hdr build below) */
	  return(get_file_info(fullname,ss));
	  /* this can become a pending request -- when user clicks "ok" we go on */
	}
      return(make_file_info_1(fullname,ss));
    }
  snd_IO_error = snd_cannot_find_file;
  return(NULL);
}

file_info *make_file_info(char *fullname, snd_state *ss)
{
  file_info *hdr;
  char *txt;
  hdr = make_file_info_2(fullname,ss);
  if (!hdr) 
    {
      if (snd_IO_error != snd_pending_open)
	{
	  if (snd_IO_error != snd_no_error)
	    {
	      txt = (char *)calloc(128,sizeof(char));
	      if (!(selected_sound(ss)))
		{
		  if (errno != 0)
		    sprintf(txt,"%s:\n  %s\n  (%s)",fullname,strerror(errno),snd_error_name(snd_IO_error));
		  else sprintf(txt,"%s:\n  %s",fullname,snd_error_name(snd_IO_error));
		}
	      else
		{
		  if (errno != 0)
		    sprintf(txt,"%s: %s (%s)",fullname,strerror(errno),snd_error_name(snd_IO_error));
		  else sprintf(txt,"%s: %s",fullname,snd_error_name(snd_IO_error));
		}
	      snd_printf(ss,txt);
	      free(txt);
	    }
	}
    }
  return(hdr);
}

file_info *make_temp_header(char *fullname, file_info *old_hdr, int samples)
{
  /* make a header for Sun/NeXT, 16-bit linear, no comment, copy other old_hdr fields */
  file_info *hdr;
  hdr = (file_info *)calloc(1,sizeof(file_info));
  hdr->s_type = make_snd_pointer_type(FILE_INFO);
  hdr->name = (char *)calloc(strlen(fullname)+1,sizeof(char));
  strcpy(hdr->name,fullname);
  hdr->samples = samples;
  hdr->data_location = 28;
  hdr->srate = old_hdr->srate;
  hdr->chans = old_hdr->chans;
  hdr->format = snd_16_linear;
  hdr->type = NeXT_sound_file;
  hdr->distributed = 0;
  hdr->comment_start = 24;
  hdr->comment_end = 27;
  hdr->comment = NULL;
  return(hdr);
}

file_info *free_file_info(file_info *hdr)
{
  if (snd_pointer_type(hdr) != FILE_INFO) snd_pointer_error("free_file_info",FILE_INFO,(void *)hdr);
  if (hdr)
    {
      if (hdr->name) free(hdr->name);
      if (hdr->comment) free(hdr->comment);
      free(hdr);
    }
  return(NULL);
}

#define FILE_STATE_SIZE 21

int *make_file_state(int fd, file_info *hdr, int direction, int chan, int suggested_bufsize, snd_state *ss)
{
  int *datai;
  int i,bufsize,chansize;
  bufsize = suggested_bufsize;
  if (direction == io_in_f)
    {
      chansize = (hdr->samples/hdr->chans);
      if (MAX_BUFFER_SIZE > chansize) bufsize = chansize+1;
    }
  else chansize = 0;
  datai = (int *)calloc(FILE_STATE_SIZE+hdr->chans,sizeof(int));
  datai[io_dir] = direction;
  datai[io_fd] = fd;
  datai[io_chans] = hdr->chans;
  datai[io_size] = chansize;
  datai[io_beg] = 0;
  datai[io_end] = bufsize-1;
  datai[io_bufsiz] = bufsize;
  datai[io_data_start] = 0; 
  datai[io_data_end] = 0;
  datai[io_open_index] = 0; 
  datai[io_hdr_end] = hdr->data_location; 
  datai[io_dats]=io_dats+6;      /* aref header size */
  datai[io_dats+c_aref_type]=0;
  datai[io_dats+c_aref_size]=hdr->chans;
  datai[io_dats+c_aref_dims]=1;  /* CLM array type */
  datai[io_dats+c_aref_dim_list_adr]=0;
  if (direction == io_in_f)
    {
#if LONG_INT_P
      datai[io_dats+6+chan] = list_ptr((int *)snd_calloc(ss,bufsize,sizeof(int)));
#else
      datai[io_dats+6+chan] = (int)snd_calloc(ss,bufsize,sizeof(int));
#endif
    }
  else
    {
      for (i=0;i<hdr->chans;i++) 
#if LONG_INT_P
	datai[io_dats+6+i] = list_ptr((int *)snd_calloc(ss,bufsize,sizeof(int)));
#else
	datai[io_dats+6+i] = (int)snd_calloc(ss,bufsize,sizeof(int));
#endif
    }
  if (direction == io_in_f) clm_file_reset(0,datai,datai); /* get ready to read -- we're assuming clm_read_chans here */
  return(datai);
}

int *free_file_state(int *datai)
{
  /* gotta free the IO buffers as well as the descriptor buffer */
  int i,chans;
  if (datai)
    {
      chans = datai[io_chans];
      for (i=0;i<chans;i++)
	{
#if LONG_INT_P
	  if (datai[io_dats+6+i]) freearray(datai[io_dats+6+i]);
#else
	  if (datai[io_dats+6+i]) free((int *)(datai[io_dats+6+i]));
#endif
	}
      free(datai);
    }
  return(NULL);
}

/* c_read_header here (or stripped-down equivalent) was very slow, and is just as easy to
 * fool as an extension check (file might start with the word ".snd" or whatever).  Also
 * the scanning functions in unix.c descend directories, and set up complete header records,
 * neither of which is needed at this point, so ...
 */

static dir *make_dir (char *name)
{
  dir *dp;
  dp = (dir *)calloc(1,sizeof(dir));
  dp->files = (char **)calloc(32,sizeof(char *));
  dp->name = (char *)calloc(strlen(name)+1,sizeof(char));
  strcpy(dp->name,name);
  dp->len = 0;
  dp->size = 32;
  return(dp);
}

dir *free_dir (dir *dp)
{
  int i;
  if (dp->name) free(dp->name);
  if (dp->files)
    {
      for (i=0;i<dp->len;i++) {if (dp->files[i]) free(dp->files[i]);}
    }
  free(dp);
  return(NULL);
}
  
static void add_snd_file_to_dir_list(dir *dp, char *name)
{
  int i;
  dp->files[dp->len] = copy_string(name);
  dp->len++;
  if (dp->len == dp->size) 
    {
      dp->size += 32;
      dp->files = (char **)realloc(dp->files,dp->size*sizeof(char *));
      for (i=dp->size-32;i<dp->size;i++) dp->files[i] = NULL;
    }
}

dir *find_sound_files_in_dir (char *name)
{
  struct dirent *dirp;
  DIR *dpos;
  char *dot,*sp;
  dir *dp = NULL;
  if ((dpos=opendir(name)) != NULL)
    {
      dp = make_dir(name);
      while ((dirp=readdir(dpos)) != NULL)
	{
	  if (dirp->d_name[0] != '.')
	    {
	      dot = NULL;
	      for (sp=dirp->d_name;(*sp) != '\0';sp++) if ((*sp) == '.') dot=sp;
	      if (dot)
		{
		  if ( (strcmp(dot,".snd") == 0)  ||
		       (strcmp(dot,".aiff") == 0) ||
		       (strcmp(dot,".aif") == 0)  ||
		       (strcmp(dot,".wav") == 0)  ||
		       (strcmp(dot,".au") == 0)   ||
		       (strcmp(dot,".aifc") == 0) ||
		       (strcmp(dot,".voc") == 0)  ||
		       (strcmp(dot,".wve") == 0)  ||
		       (strcmp(dot,".debugdata") == 0) )
		    add_snd_file_to_dir_list(dp,dirp->d_name);
		}
	    }
	}
#if defined(CLOSEDIR_VOID)
      closedir(dpos);
#else
      if (closedir(dpos) != 0) fprintf(stderr,"closedir failed!");
#endif
    }
  return(dp);
}

dir *find_session_files_in_dir (char *name)
{
  struct dirent *dirp;
  DIR *dpos;
  char *dot,*sp;
  dir *dp = NULL;
  if ((dpos=opendir(name)) != NULL)
    {
      dp = make_dir(name);
      while ((dirp=readdir(dpos)) != NULL)
	{
	  if (dirp->d_name[0] != '.')
	    {
	      dot = NULL;
	      for (sp=dirp->d_name;(*sp) != '\0';sp++) if ((*sp) == '.') dot=sp;
	      if ((dot) && (strcmp(dot,".snd-session") == 0))
		add_snd_file_to_dir_list(dp,dirp->d_name);
	    }
	}
#if defined(CLOSEDIR_VOID)
      closedir(dpos);
#else
      if (closedir(dpos) != 0) fprintf(stderr,"closedir failed!");
#endif
    }
  return(dp);
}

static int names_match(char *filename, char *pattern)
{
  /* just "*" for wildcards here */
  char *sn,*sp;
  sn = filename;
  sp = pattern;
  if ((!sn) || (!sp)) {if ((sn) || (sp)) return(0); else return(1);}
  while ((*sn) && (*sp))
    {
      if ((*sp) == '*') 
	{
	  sp++; 
	  while ((*sp) == '*') {sp++;} 
	  if (!(*sp)) return(1);
	  while ((*sn) && ((*sn) != (*sp))) {sn++;}
	  if (!(*sn)) return(0);
	}
      else 
	{
	  if ((*sn) != (*sp)) return(0);
	  sn++; sp++;
	}
    }
  return(1);
}

dir *filter_sound_files(dir *dp, char *pattern)
{
  int i;
  dir *ndp;
  ndp = make_dir("");
  for (i=0;i<dp->len;i++)
    {
      if (names_match(dp->files[i],pattern)) {add_snd_file_to_dir_list(ndp,dp->files[i]);}
    }
  return(ndp);
}

static int incf_active_sounds (snd_state *ss) {ss->active_sounds++; return(ss->active_sounds);}
static int decf_active_sounds (snd_state *ss) {ss->active_sounds--; return(ss->active_sounds);}

typedef struct {
  int active_sounds;
  char **names;
  int *sounds;
} active_sound_list;

static int add_sound_to_active_list (snd_info *sp, void *sptr1)
{
  active_sound_list *sptr = (active_sound_list *)sptr1;
  sptr->names[sptr->active_sounds] = sp->fullname;
  sptr->sounds[sptr->active_sounds] = sp->index;
  (sptr->active_sounds)++;
  return(0); /*assume no problem -- nothing can go wrong! */
}

static char title_buffer[4*(MAX_FILE_NAME)];

static void reflect_file_change_in_title(snd_state *ss)
{
  active_sound_list *alist;
  int i,j;
  char *s1,*s2;
  alist = (active_sound_list *)calloc(1,sizeof(active_sound_list));
  alist->sounds = (int *)calloc(ss->max_sounds,sizeof(int));
  alist->names = (char **)calloc(ss->max_sounds,sizeof(char *));
  map_over_sounds(ss,add_sound_to_active_list,alist);
  sprintf(title_buffer,"snd");
  if (alist->active_sounds > 0)
    {
      title_buffer[3] = ':'; title_buffer[4] = ' ';
      if (alist->active_sounds < 4) j=alist->active_sounds; else j=4;
      s1 = (char *)(title_buffer+5);
      for (i=0;i<j;i++)
	{
	  for (s2 = filename_without_home_directory(alist->names[i]);(*s1 = *s2) != '\0';++s1,++s2);
	  if (i<j-1) {*s1 = ','; s1++;  *s1 = ' '; s1++;}
	}
      if (alist->active_sounds>4) {for (i=0;i<3;i++) {*s1='.'; s1++;}}
    }
  set_title(ss,title_buffer);
  free(alist->sounds);
  free(alist->names);
  free(alist);
}

static char file_name_buf[MAX_FILE_NAME];

char *complete_filename(char *tok)
{
  /* fill out under-specified library pathnames and check for the damned '//' business (SGI file selection box uses this) */
  int i,j,len;
  len = snd_strlen(tok);
  j = 0;
  for (i=0;i<len-1;i++)
    {
      if ((tok[i] == '/') && (tok[i+1] == '/')) j=i+1;
    }
  if (j > 0)
    {
      for (i=0;j<len;i++,j++) tok[i] = tok[j];
      tok[i]='\0';
    }
  if (tok[0] != '/')
    {
      file_name_buf[0] = '\0';
      if (tok[0] == '~')
	{
	  strcpy(file_name_buf,getenv("HOME"));
	  strcat(file_name_buf,++tok);
	  tok = file_name_buf;
	}
      else
	{
#if defined(NEXT) || (defined(HAVE_CONFIG_H) && defined(HAVE_GETWD))
	  getwd(file_name_buf);
#else
  #if (!defined(HAVE_CONFIG_H)) || defined(HAVE_GETCWD)
	  getcwd(file_name_buf,MAX_FILE_NAME);
  #endif
#endif
#ifndef VMS
	  strcat(file_name_buf,"/");
	  strcat(file_name_buf,tok);
	  tok = file_name_buf;
#endif
	}
    }
  return(tok);
}

/* TODO: multiple views on one sound file -- if we do this as in Emacs,
 *   this will require splitting the edit-tree-relative state out of
 *   the chan_info structure so that the two "channels" in each case 
 *   point at the same edit info.
 */

snd_info *snd_open_file (char *filename, snd_state *ss)
{
  snd_info *sp;
  int files,val;
  if (dont_open(ss,filename)) return(NULL);
  sp = add_sound_window(complete_filename(filename),ss); /* snd-xsnd.c -> make_file_info */
  if (sp)
    {
      sp->write_date = file_write_date(sp->fullname);
      sp->need_update = 0;
      if (ss->viewing) sp->read_only = 1;
      files = incf_active_sounds(ss);
      if (files == 1) reflect_file_open_in_menu();
      set_normalize_option(active_channels(ss,0) > 1);
      reflect_file_change_in_title(ss);
      unlock_ctrls(sp);
      greet_me(ss,sp->shortname);
      check_group_max_out_chans(ss,sp->nchans);
    }
  map_over_separate_chans(ss,channel_open_pane,NULL);
  map_over_separate_chans(ss,channel_unlock_pane,NULL);
  ss->viewing = 0;
  if (sp) 
    {
      select_sound(ss,sp);
      /* now check for CLM-generated information about this sound */
      clm_add_marks(sp);
      clm_add_mixes(sp);
      if ((sp->combining != CHANNELS_SEPARATE) && (sp->nchans > 1)) 
	{
	  val = sp->combining;
	  sp->combining = CHANNELS_SEPARATE; 
	  if (val == CHANNELS_COMBINED)
	    combine_sound(sp);
	  else superimpose_sound(sp);
	}
    }
  return(sp);
}


void snd_close_file(snd_info *sp, snd_state *ss)
{
  int files;
  if (dont_close(ss,sp)) return;
  remember_me(ss,sp->shortname,sp->fullname);
  if (sp->playing) stop_playing(sp->playing);
  clear_minibuffer(sp);
  if ((region_ok(0)) && (selection_member(sp))) 
    {
      cancel_keyboard_selection();
      deactivate_selection();
    }
  unselect_channel(sp);
  free_snd_info(sp);
  files = decf_active_sounds(ss);
  if (files == 0) reflect_file_lack_in_menu();
  reflect_file_change_in_title(ss);
  set_normalize_option(active_channels(ss,0) > 1);
}


int copy_file(char *oldname, char *newname, snd_info *sp)
{
  /* make newname a copy of oldname */
  int ifd,ofd;
  long bytes,wb,total;
  char *prtbuf;
  char *buf = NULL;
  total = 0;
  ifd = open(oldname,O_RDONLY,0);
  if (ifd == -1) return(1);
  ofd = open(newname,O_RDWR,0);
  if (ofd == -1)
    {
      ofd = creat(newname,0666);
      if (ofd == -1) {close(ifd); return(2);}
    }
  buf = (char *)snd_calloc(sp->state,8192,sizeof(char));
  while ((bytes = read(ifd,buf,8192)))
    {
      total += bytes;
      wb = write(ofd,buf,bytes);
      if (wb != bytes) {close(ofd); close(ifd); free(buf); return(3);}
    }
  close(ifd);
  if (sp)
    {
      total = total >> 10;
      wb = disk_kspace(ofd);
      if (wb < 0) 
	report_in_minibuffer(sp,strerror(errno));
      else
	if (total > wb) 
	  {
	    prtbuf = (char *)calloc(64,sizeof(char));
	    sprintf(prtbuf,snd_string_were_getting_short_on_disk_space,(int)total,(int)wb);
	    report_in_minibuffer(sp,prtbuf);
	    free(prtbuf);
	  }
    }
  free(buf);
  close(ofd);
  return(0);
}

int output_type_and_format_ok(int type, int format)
{
  switch (type)
    {
    case NeXT_sound_file:
      switch (format)
	{
	case snd_8_mulaw: case snd_8_linear: case snd_16_linear: case snd_24_linear:
	case snd_32_linear: case snd_32_float: case snd_64_double: case snd_8_alaw: 
	  return(1); break;
	default: 
	  return(0); break;
	}
      break;
    case AIFF_sound_file:
      switch (format)
	{
	case snd_16_linear: case snd_24_linear: case snd_32_linear:
	case snd_8_linear: case snd_8_mulaw: case snd_8_alaw:
	  return(1);
	  break;
	default:
	  return(0);
	  break;
	}
      break;
    case RIFF_sound_file:
      switch (format)
	{
	case snd_8_mulaw: case snd_8_alaw: case snd_8_unsigned:
	case snd_16_linear_little_endian: case snd_32_linear_little_endian:
	  return(1);
	  break;
	default: 
	  return(0);
	  break;
	}
      break;
    case IRCAM_sound_file:
      switch (format)
	{
	case snd_8_mulaw: case snd_8_alaw: case snd_16_linear: case snd_32_linear: case snd_32_float:
	  return(1);
	  break;
	default:
	  return(0);
	  break;
	}
      break;
    case raw_sound_file:
      return(format != snd_32_vax_float); /* the only type we don't write */
      break;
    default: return(0); break;
    }
return(0);
}

snd_info *make_sound_readable(snd_state *ss, char *filename, int post_close)
{
  /* conjure up just enough Snd structure to make this sound readable by the edit-tree readers */
  snd_info *sp;
  chan_info *cp;
  file_info *hdr;
  snd_data *sd;
  int *datai;
  int i,fd,len;
  hdr = make_file_info_1(filename,ss);
  if (!hdr) return(NULL);
  sp = (snd_info *)calloc(1,sizeof(snd_info));
  sp->s_type = make_snd_pointer_type(SND_INFO);
  sp->nchans = sound_chans(filename);
  sp->chans = (chan_info **)calloc(sp->nchans,sizeof(chan_info *));
  sp->hdr = hdr;
  sp->state = ss;
  sp->actions = NULL;
  sp->expand = 1.0;
  sp->expanding = 0;
  sp->amp = 1.0;
  sp->srate = 1.0;
  sp->play_direction = 1;
  sp->contrasting = 0;
  sp->contrast = 0.0;
  sp->reverbing = 0;
  sp->revscl = 0.0;
  sp->filtering = 0;
  len = (hdr->samples)/(hdr->chans);
  for (i=0;i<sp->nchans;i++)
    {
      cp = make_chan_info(NULL,i,sp,ss);
      free((cp->cgx)->ax);
      free(cp->cgx);
      cp->cgx = NULL;
      sp->chans[i] = cp;
      add_channel_data_1(cp,sp,ss,0);
      cp->edits[0] = initial_ed_list(0,len);
      cp->edit_size = 0;
      cp->sound_size = 0;
      fd = snd_open_read(ss,filename);
      open_clm_file_descriptors(fd,hdr->format,sound_datum_size(filename),hdr->data_location);
      datai = make_file_state(fd,hdr,io_in_f,i,(post_close) ? MAX_BUFFER_SIZE : MIX_FILE_BUFFER_SIZE,ss);
#if LONG_INT_P
      cp->sounds[0] = make_snd_data_file(filename,datai,
					 delist_ptr(datai[io_dats+6+i]),
					 copy_header(hdr->name,hdr),
					 0,cp->edit_ctr,i);
#else
      cp->sounds[0] = make_snd_data_file(filename,datai,
					 (int *)(datai[io_dats+6+i]),
					 copy_header(hdr->name,hdr),
					 0,cp->edit_ctr,i);
#endif
      if (post_close) {snd_close(fd); sd = cp->sounds[0]; sd->open = FD_CLOSED; datai[io_fd] = -1;}
      /* this is not as crazy as it looks -- we've read in the first 64K (or whatever) samples,
       * and may need this file channel for other opens, so this file can be closed until clm_file_reset
       */
    }
  return(sp);
}

static snd_info *snd_update_1(snd_state *ss, snd_info *sp, char *ur_filename)
{
  /* we can't be real smart here because the channel number may have changed and so on */
  float *axis_data;
  int *ffts,*waves;
  int i,j,old_chans,old_sync,old_combine,need_update;
  int raw_def;
  float duration;
  chan_info *cp;
  axis_info *ap;
  snd_info *nsp;
  char *filename;
  need_update = 0;
  filename = copy_string(ur_filename);
  old_chans = sp->nchans;
  old_sync = sp->syncing;
  old_combine = sp->combining;
  axis_data = (float *)calloc(4*old_chans,sizeof(float));
  ffts = (int *)calloc(old_chans,sizeof(int));
  waves = (int *)calloc(old_chans,sizeof(int));
  for (i=0;i<old_chans;i++)
    {
      cp = sp->chans[i];
      ap = cp->axis;
      axis_data[(i*4)+0]=ap->x0;
      axis_data[(i*4)+1]=ap->x1;
      axis_data[(i*4)+2]=ap->y0;
      axis_data[(i*4)+3]=ap->y1;
      waves[i] = cp->waving;
      ffts[i] = cp->ffting;
    }
  snd_close_file(sp,ss);
  /* this normalizes the fft/lisp/wave state so we need to reset it after reopen */
  alert_new_file();
  /* if it's a raw sound file being updated, we don't want to re-confirm the sound format and whatnot
   * so we set the use-raw-defaults flag in a sort of ugly wrapper around the snd_open_file
   */
  raw_def = use_raw_defaults(ss);
  set_use_raw_defaults(ss,1);
  nsp = snd_open_file(filename,ss);
  set_use_raw_defaults(ss,raw_def);
  /* end wrapper */
  duration = (float)sound_samples(filename) / (float)(sound_chans(filename) * sound_srate(filename));
  for (i=0,j=0;i<nsp->nchans;i++)
    {
      cp = nsp->chans[i];
      if (duration < axis_data[(j*4)+0]) axis_data[(j*4)+0]=duration-.1;
      if (duration < axis_data[(j*4)+1]) axis_data[(j*4)+1]=duration;
      set_axes(cp,axis_data[(j*4)+0],axis_data[(j*4)+1],axis_data[(j*4)+2],axis_data[(j*4)+3]);
      update_graph(cp,NULL); /* get normalized state before messing with it */
      if (ffts[j]) {fftb(cp,TRUE); need_update = 1;}
      if (!(waves[j])) {waveb(cp,FALSE); need_update = 1;}
      if (j<(old_chans-1)) j++;
    }
  if (nsp->combining != old_combine) combineb(nsp,old_combine);
  if (nsp->syncing != old_sync) syncb(nsp,old_sync);
  if (need_update) {for (i=0;i<nsp->nchans;i++) update_graph(nsp->chans[i],NULL);}
  free(axis_data);
  free(waves);
  free(ffts);
  free(filename);
  return(nsp);
}

void snd_update(snd_state *ss, snd_info *sp)
{
  char *buf;
  save_window_size(ss);
  sp = snd_update_1(ss,sp,sp->fullname);
  buf = (char *)calloc(64,sizeof(char));
  sprintf(buf,snd_string_updated,sp->shortname);
  report_in_minibuffer(sp,buf);
  free(buf);
  restore_window_size(ss);
}

