#include "snd.h"

#ifdef NEXT
  #include <sys/dir.h>
  #include <sys/dirent.h>
#else
  #include <dirent.h>
#endif

#if defined(SGI) || defined(SCO5) || defined(UW2) || defined(SOLARIS)
  #include <sys/statfs.h>
#endif

#if defined(LINUX) || defined(NEXT)
  #include <sys/vfs.h>
#endif

#ifdef BEOS
int disk_kspace (int fd) {return(1234567);}
time_t file_write_date(char *filename) {return(1234567);}
#else
int disk_kspace (int fd)
{
  struct statfs buf;
  int err;
#if defined(SGI) || defined(SCO5) || defined(UW2) || defined(SOLARIS)
  err = fstatfs(fd,&buf,sizeof(buf),0);
#endif
#if defined(LINUX) || defined(NEXT)
  err = fstatfs(fd,&buf);
#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);
}

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));
}
#endif


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 = c_snd_header_data_size(); /* total samples, not per channel */
  hdr->data_location = c_snd_header_data_location();
  hdr->srate = c_snd_header_srate();
  if ((hdr->srate) == 0) hdr->srate = 1;
  hdr->chans = c_snd_header_chans();
  if ((hdr->chans) == 0) hdr->chans = 1;
  hdr->format = c_snd_header_format();
  hdr->type = c_snd_header_type();
  hdr->distributed = c_snd_header_distributed();
  hdr->comment_start = c_snd_header_comment_start();
  hdr->comment_end = c_snd_header_comment_end();
  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;
  return(hdr);
}

static file_info *translate_file(char *filename, snd_state *ss)
{
  char *newname;
  file_info *hdr = NULL;
  int err,len;
  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';
  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);
}

file_info *make_file_info(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)
    {
      c_read_header_with_fd(fd);
      close(fd);  
      type = c_snd_header_type();
      format = c_snd_header_format();
      orig = c_snd_header_original_format();
      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_G721) || (orig == RIFF_MPEG) || (orig == RIFF_Gsm610) || (orig == RIFF_Oki_ADPCM))
		return(translate_file(fullname,ss));
	      break;
	    case AIFF_sound_file:
	      if (orig == AIFC_G722) return(translate_file(fullname,ss));
	      break;
	    case NeXT_sound_file:
	      if ((orig == NeXT_G721) || (orig == NeXT_G722) || (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:
	      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_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;
  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);
      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)
{
  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)
    {
      datai[io_dats+6+chan] = (int)calloc(bufsize,sizeof(int));
    }
  else
    {
      for (i=0;i<hdr->chans;i++) 
	datai[io_dats+6+i] = (int)calloc(bufsize,sizeof(int));
    }
  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 (datai[io_dats+6+i]) free((int *)(datai[io_dats+6+i]));
	}
      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)
{
#ifdef NEXT
  struct direct *dirp;
#else
  struct dirent *dirp;
#endif
  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);
		}
	    }
	}
      closedir(dpos);
    }
  return(dp);
}

dir *find_session_files_in_dir (char *name)
{
#ifdef NEXT
  struct direct *dirp;
#else
  struct dirent *dirp;
#endif
  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);
	    }
	}
      closedir(dpos);
    }
  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);
}


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 = 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
	{
#ifdef NEXT
	  getwd(file_name_buf);
#else
	  getcwd(file_name_buf,MAX_FILE_NAME);
#endif
	  strcat(file_name_buf,"/");
	  strcat(file_name_buf,tok);
	  tok = file_name_buf;
	}
    }
  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;
  sp = add_sound_window(complete_filename(filename),ss);
  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);
    }
  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;
  remember_me(ss,sp->shortname,sp->fullname);
  if (sp->playing) stop_playing(sp->playing);
  clear_minibuffer(sp);
  if ((selection_is_ours()) && (selection_member(sp))) 
    {
      cancel_keyboard_selection();
      relinquish_selection_ownership();
    }
  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 *)calloc(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,err,len;
  err = c_read_header(filename);
  if (err == -1) return(NULL);
  sp = (snd_info *)calloc(1,sizeof(snd_info));
  sp->s_type = make_snd_pointer_type(SND_INFO);
  sp->nchans = c_snd_header_chans();
  sp->srate = c_snd_header_srate();
  sp->chans = (chan_info **)calloc(sp->nchans,sizeof(chan_info *));
  hdr = make_file_info_1(filename,ss);
  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,c_snd_datum_size(hdr->format),hdr->data_location);
      datai = make_file_state(fd,hdr,io_in_f,i,(post_close) ? MAX_BUFFER_SIZE : MIX_FILE_BUFFER_SIZE);
      cp->sounds[0] = make_snd_data_file(filename,datai,(int *)(datai[io_dats+6+i]),hdr,0,cp->edit_ctr);
      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);
}

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;
  chan_info *cp;
  axis_info *ap;
  snd_info *nsp;
  char *filename;
  filename = copy_string(ur_filename);
  old_chans = sp->nchans;
  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/ufunc/wave state so we need to reset it after reopen */
  alert_new_file();
  nsp = snd_open_file(filename,ss);
  for (i=0,j=0;i<nsp->nchans;i++)
    {
      cp = nsp->chans[i];
      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]) show_fft(cp);
      if (!(waves[j])) hide_wave(cp);
      if (j<(old_chans-1)) j++;
    }
  free(axis_data);
  free(waves);
  free(ffts);
  free(filename);
  return(nsp);
}

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

