/* respeed -- change speaking rate without pitch change */

/* M.A.Huckvale - University College London */

/* version 0.1 - January 1994 */

#define  PROGNAME   "respeed"   #define   PROGVERS   "0.1"   char
*progname=PROGNAME;

/*---------
----------------------------------------------------------------*/
/**MAN


















































UCL                                                             1






RESPEED(1)               USER COMMANDS                 RESPEED(1)



NAME
     respeed - change speaking rate without change in pitch

SYNOPSIS
     respeed (-I) (-i item) (-r rate-change) file

DESCRIPTION
     respeed is a program to speed-up or  slow-down  a  piece  of
     speech  without changing the pitch.  It is based on the SOLA
     algorithm of Roucos and Wilgur (ICASSP-86).

OPTIONS
     -I         Identify program name and version number.

     -iitem     Select input item number.

     -rrate-change
                Relative speed up factor. 2.0 =  twice  as  fast,
                0.5=half as fast.  Default 1.

INPUT ITEMS
     SP         Speech item

OUTPUT ITEMS
     SP         Rate changed speech

HISTORY
     rate=
          proportional rate-change.

VERSION/AUTHOR
     1.0  Mark Huckvale

SEE ALSO
     solout

BUGS
     */                                               /*---------
     -----------------------------------------------------------------*/

     /* include files */  #include  <stdio.h>  #include  <math.h>
     #include  <string.h>  #include  <malloc.h>  #include "sfs.h"
     #define   MAX(x,y)   (((x)>(y))?(x):(y))   #define    ABS(x)
     (((x)>0)?(x):-(x))

     /* global defines */ #define WINTIME          0.032

     /*  global  data  */  struct   item_header  spitem;   struct
     item_header  opitem;   short               *isp,*osp;     /*
     input/output signal */ float               *risp,*rosp;   /*
     real            input/output            signal            */
     float               *wisp,*wosp;   /*   real    input/output



UCL                     Last change: SFS                        1






RESPEED(1)               USER COMMANDS                 RESPEED(1)



     window      */      int            stepsize,winsize,bufsize;
     double              newrate=2.0; int            verbose=0;

     /* main program */ main(argc,argv) int  argc;  char *argv[];
     {          /*     option     decoding     */          extern
     int     optind;        /*  option   index   */        extern
     char    *optarg;  /*      option     argument     ptr     */
          int       errflg  =  0;    /*  option  error  flag   */
          int       c;        /*       option      switch      */
          int       it;       /*      item      selection      */
          char      *ty;      /*     item     sub     type     */
          char      *sptype="0";       /*   file   variables   */
          char      filename[SFSMAXFILENAME];  /*  SFS  data file
     name */      int       fid;      /* input file descriptor */
          int       ofid;         double         curtime,endtime;
          double         halfwintime,steptime;
          int       cursamp,newsamp;
          int       frameno,framelen,offset;
          int       overtarg,overmin,overmax;

          /*   decode   switches   */        while   (    (c    =
     getopt(argc,argv,"Ii:r:v"))   !=   EOF   )   switch   (c)  {
               case      'I'      :     /*      Identify       */
                    fprintf(stderr,"%s:  V%s0,PROGNAME,PROGVERS);
                    exit(0);                break;           case
     'i'    :     /*    specific    item   */                  if
     (itspec(optarg,&it,&ty) == 0) {                      if  (it
     ==        SP_TYPE)                                sptype=ty;
                         else
                              error("unsuitable   item  specifier
     %s",optarg);                     }                      else
                         error("illegal       item      specifier
     %s",optarg);                  break;             case    'r'
     :     /*    output    rate   */                  newrate   =
     atof(optarg);                  break;             case   'v'
     :     /*      verbose      */                     verbose++;
                    break;           case '?' :     /* unknown */
                    errflg++;      }      if (errflg || (argc<2))
               error("usage: %s (-I) (-i item)  (-r  rate-change)
     (-v) file",PROGNAME);

          /*  get   filename   */        if   (optind   <   argc)
               strcpy(filename,sfsfile(argv[optind]));       else
               error("no database file specified",NULL);

          /*         open         file         */              if
     ((fid=sfsopen(filename,"w",NULL))<0)           error("access
     error on '%s'",filename);

          /*        locate        item         */              if
     (!sfsitem(fid,SP_TYPE,sptype,&spitem))
               error("unable    to    find    input    item    in



UCL                     Last change: SFS                        2






RESPEED(1)               USER COMMANDS                 RESPEED(1)



     '%s'",filename);

          /* get sizes */      halfwintime = WINTIME/2;      win-
     size  = WINTIME/spitem.frameduration;      bufsize = 10*win-
     size;      steptime =            /* get buffers  */       if
     ((isp=(short            *)sfsbuffer(&spitem,bufsize))==NULL)
               error("could  not  get  memory  buffer");       if
     ((osp=(short            *)sfsbuffer(&spitem,bufsize))==NULL)
               error("could  not  get  memory  buffer");       if
     ((risp=(float        *)calloc(bufsize,sizeof(float)))==NULL)
               error("could  not  get  memory  buffer");       if
     ((rosp=(float        *)calloc(bufsize,sizeof(float)))==NULL)
               error("could  not  get  memory  buffer");       if
     ((wisp=(float        *)calloc(bufsize,sizeof(float)))==NULL)
               error("could  not  get  memory  buffer");       if
     ((wosp=(float        *)calloc(bufsize,sizeof(float)))==NULL)
               error("could not get memory buffer");

          /*        make         output         header         */
          sfsheader(&opitem,SP_TYPE,0,2,1,spitem.frameduration,spitem.offset,1,0,0);
          sprintf(opitem.history,"%s(%d.%02d;rate=%g)",
                    PROGNAME,
                    spitem.datatype,spitem.subtype,
                    newrate);

          /*     get     output      channel      */           if
     ((ofid=sfschannel(filename,&opitem))<0)
               error("could   not   open   output   channel    to
     '%s'",filename);

          /*  pre-charge   buffer   with   half   a   window   */
          memset((char    *)osp,0,2*bufsize);        memset((char
     *)rosp,0,4*bufsize);      memset((char  *)wosp,0,4*bufsize);
          framelen         =        sfsread(fid,0,winsize/2,isp);
          halfwindow(isp,winsize/2,rosp,wosp);
          cursamp=winsize/2;         curtime=halfwintime;
          /*    create    'dummy'    output     in     osp     */
          outsynth(osp,rosp,wosp,cursamp);

          /*  calculate  min  and  max  overlap   alignments   */
          overtarg = winsize*(1-1/(2*newrate));      if (overtarg
     >= 7*winsize/8) overtarg = 7*winsize/8;      if (overtarg <=
     winsize/8)  overtarg  = winsize/8;      if (newrate > 1.0) {
               /*    speed    up    */              overmax     =
     (winsize+overtarg)/2;             overmin  =  (5*winsize)/8;
          }      else {           /* slow down */           over-
     max  = (3*winsize)/8;           overmin = overtarg/2;      }
          if (overmax >= 15*winsize/16) overmax =  15*winsize/16;
          if (overmin <= winsize/16) overmin = winsize/16; #ifdef
     IAG printf("winsize=%d overtarg=%d  overmin=%d  overmax=%d0,
          winsize,overtarg,overmin,overmax); #endif




UCL                     Last change: SFS                        3






RESPEED(1)               USER COMMANDS                 RESPEED(1)



          /*            processing            loop             */
          endtime=spitem.numframes*spitem.frameduration/newrate;
          while (curtime < endtime) {

               /*   calculate    next    frame    to    use    */
               frameno   =   curtime*newrate/halfwintime   -   1;
               if   (frameno   <   0)   frameno=0;   #ifdef   IAG
     printf("curtime=%g                                frameno=%d
     cursamp=%d0,curtime,frameno,cursamp);  #endif             /*
     load      this     frame     */               framelen     =
     sfsread(fid,(frameno*winsize)/2,winsize,isp);

               /*  align  with   what   we   have   so   far   */
               offset                                           =
     align(isp,framelen,osp,cursamp,overmin,overmax,overtarg);
     #ifdef        IAG       printf("cursamp=%d       framelen=%d
     offset=%d0,cursamp,framelen,offset); printf("target=%d rela-
     tive   frame   offset=%d0,overtarg,offset-cursamp);   #endif
               /*           window            frame            */
               window(isp,framelen,risp,wisp);

               /*    add    in     */               newsamp     =
     overlapadd(risp,wisp,framelen,rosp,wosp,offset);  #ifdef IAG
     printf("newsamp=%d0,newsamp); #endif           if  (verbose)
                    printf("Out     %6.3f-%.3fIn     %6.3f-%.3f0,
                         curtime+(offset-
     cursamp)*spitem.frameduration,
                         curtime+(newsamp-
     cursamp)*spitem.frameduration,
                         (frameno*winsize/2)*spitem.frameduration,
                         (frameno*winsize/2+framelen)*spitem.frameduration);

               /*      create       'dummy'       output       */
               outsynth(osp,rosp,wosp,newsamp);           curtime
     += (newsamp-cursamp)*spitem.frameduration;           cursamp
     = newsamp;

               /* purge output from time to time */            if
     (cursamp           >=          (bufsize-winsize))          {
                    outsynth(osp,rosp,wosp,bufsize/2);
                    /*     write     out     first     half    */
                    sfswrite(ofid,bufsize/2,osp);
                    memcpy((char                      *)osp,(char
     *)(osp+bufsize/2),bufsize);                     memcpy((char
     *)rosp,(char                  *)(rosp+bufsize/2),bufsize*2);
                    memcpy((char                     *)wosp,(char
     *)(wosp+bufsize/2),bufsize*2);                  memset((char
     *)(osp+bufsize/2),0,bufsize);                   memset((char
     *)(rosp+bufsize/2),0,bufsize*2);                memset((char
     *)(wosp+bufsize/2),0,bufsize*2);                 cursamp  -=
     bufsize/2;           }      }




UCL                     Last change: SFS                        4






RESPEED(1)               USER COMMANDS                 RESPEED(1)



          /*         write         last         buffer         */
          outsynth(osp,rosp,wosp,cursamp);
          sfswrite(ofid,cursamp,osp);

          /* that's all folks */       if  (!sfsupdate(filename))
               error("update     error     on    '%s'",filename);
          exit(0); }

     /*  half  window  signal  */   halfwindow(buf,len,obuf,wbuf)
     short     *buf;  int  len; float     *obuf; float     *wbuf;
     {      double    w;      int  i;

          w          =           M_PI/(len+1);                for
     (i=1;i<=len;i++,buf++,wbuf++,obuf++) {           *wbuf = 0.5
     +  0.5*cos(i*w);            *obuf  =  (float)*buf  *  *wbuf;
          } }

     /*    window     signal     */     window(buf,len,obuf,wbuf)
     short     *buf;  int  len; float     *obuf; float     *wbuf;
     {      double    w;      int  i;

          w         =         2.0*M_PI/(len+1);               for
     (i=1;i<=len;i++,buf++,wbuf++,obuf++) {           *wbuf = 0.5
     -  0.5*cos(i*w);            *obuf  =  (float)*buf  *  *wbuf;
          } }

     /*       align       window       with       output       */
     int  align(wbuf,wlen,sbuf,slen,omin,omax,otarg)
     short     *wbuf;  int  wlen;   short     *sbuf;   int  slen;
     int  omin;    int  omax;    int  otarg;   {        int  i,j;
          float     sum;                             int  offset;
          float     peakval;                  float     ac[1024];
          int  currdist;

          if (omax > slen) omax=slen;      offset = (slen<wlen/2)
     ? 0 : wlen/2;      for (i=omin;i<omax;i++) {           sum =
     0;             for   (j=0;j<i;j++)                  sum   +=
     (float)wbuf[i-j-1]  * (float)sbuf[slen-j-1];           ac[i]
     = sum/i;      }

          /*     smooth     */           peakval=0;           for
     (i=omin;i<omax;i++)   {             ac[i]  =  (ac[i-2]+ac[i-
     1]+ac[i]+ac[i+1]+ac[i+2])/5;            if   (ac[i]>peakval)
     peakval=ac[i];      }

          /* search for peak close to target */       peakval  *=
     0.75;      currdist=omax;      if (verbose) printf("peaks at
     ");      for (i=omin;i<omax;i++) if ((ac[i-1]  <  ac[i])  &&
     (ac[i]    >    ac[i+1]))    {             if   (verbose   &&
     (ac[i]>peakval))                  printf("%d(%g),",i,ac[i]);
               if  ((ac[i]>peakval) && (ABS(otarg-i)<currdist)) {
                    currdist           =            ABS(otarg-i);



UCL                     Last change: SFS                        5






RESPEED(1)               USER COMMANDS                 RESPEED(1)



                    offset  = i;           }      }      printf("
     chosen=%d0,offset);      return(slen-offset); }

     /*        overlap-add         two         signals         */
     int  overlapadd(s1buf,w1buf,s1len,s2buf,w2buf,s2off)
     float     *s1buf;       float     *w1buf;        int  s1len;
     float     *s2buf;     float     *w2buf;     int  s2off;    {
          int  i;

          s2buf  +=  s2off;        w2buf   +=   s2off;        for
     (i=0;i<s1len;i++,s1buf++,w1buf++,s2buf++,w2buf++)          {
               *s2buf  +=  *s1buf;            *w2buf  +=  *w1buf;
          }      return(s2off+s1len); }

     /*    re-synthesize     */     outsynth(obuf,sbuf,wbuf,slen)
     short     *obuf;      float     *sbuf;      float     *wbuf;
     int  slen; {      int  i;

          for       (i=0;i<slen;i++,obuf++,sbuf++,wbuf++)       {
               if                  (*wbuf==0.0)                 {
     /*             fprintf(stderr,"zero       window0);       */
                    *obuf  =  32767;            }            else
                    *obuf = (short)(*sbuf / *wbuf);      } }
































UCL                     Last change: SFS                        6



