xiRetimer

fit recorded audio to a tempo
git clone https://git.ce9e.org/xiRetimer.git

commit
8f7f438335d24d8025431e777e5a3ba111ab896e
parent
3c2970db2f559685f372facd7c89f752caf683e8
Author
Tobias Bengfort <tobias.bengfort@gmx.net>
Date
2010-11-04 17:58
stretch and interpolation modes

Diffstat

M src/main.h 1 -
M src/marker.cpp 81 +++++++++++++++++++++++++++++++++++++++++++------------------
M src/marker.h 25 ++++++++++++++++++++-----
M src/playback.cpp 7 +------
M src/sample.cpp 76 +++++++++++++++++--------------------------------------------
M src/sample.h 15 ++++++++++++---
M xiRetimer 0

7 files changed, 111 insertions, 94 deletions


diff --git a/src/main.h b/src/main.h

@@ -1,4 +1,3 @@
    1    -1 
    2     1 #ifndef __WXWIDGETSAPP_H
    3     2 #define __WXWIDGETSAPP_H
    4     3 

diff --git a/src/marker.cpp b/src/marker.cpp

@@ -5,6 +5,7 @@ Marker::Marker() {
    5     5   aold;
    6     6   add(0,0);
    7     7   add(1,1);
   -1     8   interpolationMode=0;
    8     9 }
    9    10 
   10    11 Marker::~Marker() {
@@ -54,6 +55,9 @@ void Marker::setNew(int pi, float pnew) {
   54    55   anew.set(resort(pi), pnew);
   55    56 }
   56    57 
   -1    58 int Marker::getInterpolationMode() {return interpolationMode;}
   -1    59 void Marker::setInterpolationMode(int m) {interpolationMode=m;}
   -1    60 
   57    61 float Marker::getRatio() {
   58    62   if (getLength()>0)
   59    63     return (getNew(getLength()-1)-getNew(0))/(getOld(getLength()-1)-getOld(0));
@@ -61,19 +65,30 @@ float Marker::getRatio() {
   61    65     return NULL;
   62    66 }
   63    67 
   64    -1 /*
   65    -1 float Marker::getRatio(int i) {
   66    -1   if (i>=0 && i<getLength()-1)
   67    -1     return (getNew(i+1)-getNew(i))/(getOld(i+1)-getOld(i));
   68    -1   else
   69    -1     return NULL;
   70    -1 }
   71    -1 */
   72    -1 
   73    68 float Marker::getRatio(float o) {
   74    -1   // TODO not exact at all
   75    -1   float n=100;
   76    -1   return ((old2new(o+1/n)-old2new(o-1/n))/(2/n));
   -1    69 // derivate of old2new
   -1    70 // hardcode the derivate to improve performance
   -1    71   switch (getInterpolationMode()) {
   -1    72     // linear
   -1    73     case 0: {
   -1    74       int i=getAreaOld(o);
   -1    75       float dold=(getOld(i+1)-getOld(i));
   -1    76       if (dold<=0) return 0;
   -1    77       return (getNew(i+1)-getNew(i))/dold;
   -1    78     } break;
   -1    79     default: {
   -1    80       // approxmiate ratio
   -1    81       float n=100; // TODO automate this
   -1    82       float o1=o-1/n;
   -1    83       if (o1<0) o1=0;
   -1    84       if (o1>1) return 0;
   -1    85       float o2=o+1/n;
   -1    86       if (o2<0) return 0;
   -1    87       if (o2>1) o2=1;
   -1    88       if (o2-o1<=0) return 0;
   -1    89       return ((old2new(o2)-old2new(o1))/(o2-o1));
   -1    90     }
   -1    91   }
   77    92 }
   78    93 
   79    94 int Marker::getLength() {
@@ -109,24 +124,42 @@ int Marker::resort(int pi) {
  109   124 }
  110   125 
  111   126 float Marker::old2new(float o) {
  112    -1 // !!linear
  113    -1 // converts old 0-1 values to new 0-1 values
  114    -1   int i=getAreaOld(o);
  115    -1   // linear interpolation
  116    -1   //      n    - n_i        o    - o_i
  117    -1   //   -------------- =  --------------
  118    -1   //   n_{i+1} - n_i     o_{i+1} - o_i
  119    -1   return (o-getOld(i))/(getOld(i+1)-getOld(i))*(getNew(i+1)-getNew(i))+getNew(i);
   -1   127 // this one does all the interpolation!
   -1   128 // for performance reasons you should also hard code the interpolation modes to getRatio(float) and new2old
   -1   129   switch (getInterpolationMode()) {
   -1   130 //    case 0: // linear is default
   -1   131     default: {
   -1   132       int i=getAreaOld(o);
   -1   133       // linear interpolation
   -1   134       //      n    - n_i        o    - o_i
   -1   135       //   -------------- =  --------------
   -1   136       //   n_{i+1} - n_i     o_{i+1} - o_i
   -1   137       return (o-getOld(i))/(getOld(i+1)-getOld(i))*(getNew(i+1)-getNew(i))+getNew(i);
   -1   138     }
   -1   139   }
  120   140 }
  121   141 
  122   142 float Marker::new2old(float n) {
  123    -1 // see old2new
  124    -1   int i=getAreaNew(n);
  125    -1   return (n-getNew(i))/(getNew(i+1)-getNew(i))*(getOld(i+1)-getOld(i))+getOld(i);
   -1   143 // inverse of old2new
   -1   144 // hardcode the derivate to improve performance
   -1   145   switch (getInterpolationMode()) {
   -1   146     // linear
   -1   147     case 0: {
   -1   148       int i=getAreaNew(n);
   -1   149       return (n-getNew(i))/(getNew(i+1)-getNew(i))*(getOld(i+1)-getOld(i))+getOld(i);
   -1   150     } break;
   -1   151     default: {
   -1   152       // approximate o;
   -1   153       float o=n;
   -1   154       for (int i=1; i<10; ++i) {
   -1   155         o+=(n-old2new(o))/i;
   -1   156       }
   -1   157       return o;
   -1   158     }
   -1   159   }
  126   160 }
  127   161 
  128   162 float Marker::new2nnew(float n) {
  129    -1   // normalizing
  130   163   return (n-getNew(0))/(getNew(getLength()-1)-getNew(0));
  131   164 }
  132   165 

diff --git a/src/marker.h b/src/marker.h

@@ -6,10 +6,23 @@
    6     6 
    7     7 /*
    8     8 marker works with float values
    9    -1 it mapps old values from 0 (start) to 1 (end) to any new float values
   10    -1 nnew values are normalized new values
   -1     9 it mapps old values (o) from 0 (start) to 1 (end) to any new float values (n)
   -1    10 nnew values (nn) are normalized new values
   11    11 most classes apart from marker use nnew values. Never forget to convert them!
   12    12 */
   -1    13 /*
   -1    14 For different modes of interpolation you have to edit 3 functions: 
   -1    15 old2new
   -1    16 new2old - inverse of old2new
   -1    17 getRatio - derivate of old2new
   -1    18 // TODO define new2old and getRatio from old2new to make sure everything works together
   -1    19 */
   -1    20 
   -1    21 /*
   -1    22 interpolation modes:
   -1    23 0 - LINEAR
   -1    24 // TODO define constants
   -1    25 */
   13    26 
   14    27 class Marker {
   15    28 public:
@@ -21,9 +34,6 @@ public:
   21    34   float getNew(int pi);
   22    35   float getOld(int pi);
   23    36   void setNew(int pi, float pnew);
   24    -1   float getRatio(); // factor by wich the whole sample is stretched; used to guess the length of the output array;
   25    -1 //  float getRatio(int i); // factor by wich this area is stretched;
   26    -1   float getRatio(float o); // factor by wich is stretched on this place;
   27    37   int getLength();
   28    38   void print();
   29    39   // 0-1 conversion
@@ -33,10 +43,15 @@ public:
   33    43   float nnew2new(float n);
   34    44   int getAreaNew(float n);
   35    45   int getAreaOld(float o);
   -1    46   float getRatio(); // factor by wich the whole sample is stretched; used to guess the length of the output array;
   -1    47   float getRatio(float o); // factor by wich is stretched on this place;
   -1    48   int getInterpolationMode();
   -1    49   void setInterpolationMode(int m);
   36    50 private:
   37    51   Buffer anew;
   38    52   Buffer aold;
   39    53   int resort(int pi);
   -1    54   int interpolationMode;
   40    55 };
   41    56 
   42    57 /*

diff --git a/src/playback.cpp b/src/playback.cpp

@@ -52,19 +52,14 @@ int Playback::play() {
   52    52 }
   53    53 
   54    54 int Playback::start() {
   55    -1 //    if ( sounds.dpos != sounds.dlen )
   56    -1 //        return 1;
   57    -1 
   58    55   SDL_LockAudio();
   59    -1 
   60    56 //    if ( sounds.data ) {
   61    57 //        free(sounds.data);
   62    58 //    }
   63    -1 
   64    59     int length=sample->getLength();
   65    60     Uint8 idata[length];
   66    61     for (int i=0; i<length; ++i) {
   67    -1       idata[i]=int(sample->data[i]*128);
   -1    62       idata[i]=int(sample->get(i/(float)length)*128);
   68    63     }
   69    64 std::cout << length << " ";
   70    65     /* Put the sound data in the slot (it starts playing immediately) */

diff --git a/src/sample.cpp b/src/sample.cpp

@@ -1,6 +1,5 @@
    1     1 #include "sample.h"
    2    -1 
    3    -1 // TODO quality!!!
   -1     2 #include "rbprocess.h"
    4     3 
    5     4 Sample::Sample(Marker* m) {
    6     5   marker=m;
@@ -8,6 +7,7 @@ Sample::Sample(Marker* m) {
    8     7   data=new float[0];
    9     8   olength=0;
   10     9   odata=new float[0];
   -1    10   stretchMode=-1;
   11    11 }
   12    12 
   13    13 Sample::~Sample() {
@@ -23,20 +23,22 @@ int Sample::getGuessedLength() {
   23    23 }
   24    24 
   25    25 float Sample::get(float nn) {
   26    -1   // TODO interpolation?
   27    -1   int i=int((getLength()-1)*nn);
   28    -1   if (i<0 || i>=getLength()) return NULL;
   -1    26   int i=int(length*nn);
   -1    27   if (i<0 || i>=length) return NULL;
   29    28   return data[i];
   30    29 }
   31    30 
   32    31 float Sample::getOld(float o) {
   33    -1   // TODO interpolation?
   34    32   int i=int((olength-1)*o);
   35    33   if (i<0 || i>=olength) return NULL;
   36    34   return odata[i];
   37    35 }
   38    36 
   -1    37 int Sample::getStretchMode() {return stretchMode;}
   -1    38 void Sample::setStretchMode(int m) {stretchMode=m;}
   -1    39 
   39    40 int Sample::loadFile(const char* fileName) {
   -1    41 // TODO multi filetype support
   40    42   SNDFILE *sndfile;
   41    43   sfinfo;
   42    44   // open file
@@ -71,6 +73,7 @@ int Sample::loadFile(const char* fileName) {
   71    73 }
   72    74 
   73    75 int Sample::writeFile(const char* fileNameOut) {  
   -1    76 // TODO multi filetype support
   74    77   if (length<=0) {
   75    78     std::cerr << "ERROR: Load a file first" << std::endl;
   76    79     return 1;
@@ -95,60 +98,23 @@ int Sample::writeFile(const char* fileNameOut) {
   95    98 }
   96    99 
   97   100 int Sample::process() {
   98    -1   //reads from odata and writes to data
   99    -1   const int bufferLength=4096; // important // 
  100    -1   float **ibuf = new float *[1];
  101    -1   ibuf[0]=new float[bufferLength];
  102    -1   float **obuf = new float *[1];
  103    -1   int count2=0; // position in odata
  104    -1   int avail2=0; // position in data
   -1   101 /*
   -1   102 This function does the nmain thing: it stretches the original data as defined by the marker object.
   -1   103 Therefore it reads data from odata and writes to data.
   -1   104 */
  105   105   // setup data
  106    -1   length=olength*marker->getRatio();
   -1   106   length=int(marker->getRatio()*olength);
  107   107   delete[] data;
  108   108   data=new float[length];
  109    -1   for (int i=0; i<marker->getLength()-1; ++i) {
  110    -1 //    float ratio=marker->getRatio(i); // redesign: getRatio(o)
  111    -1     int count=0; // position in section (o)
  112    -1     int frames=int((marker->getOld(i+1)-marker->getOld(i))*olength); // length of section (o)
  113    -1     while (count<frames && count2<olength && avail2<length) {
  114    -1       // load odata to ibuf
  115    -1       for (int j=0; j<bufferLength && count2+j<olength; ++j) {
  116    -1         ibuf[0][j]=odata[count2+j];
  117    -1         count++;
   -1   109   switch (getStretchMode()) {
   -1   110     // rubberband
   -1   111     case 0: RBprocess(odata, olength, data, length, marker); break;
   -1   112     default: {
   -1   113       for (int i=0; i<length; ++i) {
   -1   114         data[i]=getOld(marker->new2old(marker->nnew2new(i/(float)length)));
  118   115       }
  119    -1       float ratio=marker->getRatio(count2/(float)olength);
  120    -1       count2+=bufferLength;
  121    -1       if (count2>olength) count2=olength;
  122    -1       // process ibuf
  123    -1       RubberBand::RubberBandStretcher ts(sfinfo.samplerate, 1, 0, ratio);
  124    -1       ts.setMaxProcessSize(bufferLength*10);
  125    -1       ts.study(ibuf, bufferLength, true);
  126    -1       int a1=-1;
  127    -1       int a2=0;
  128    -1       while (a1!=a2) {
  129    -1         ts.process(ibuf, ts.getSamplesRequired(), false);
  130    -1         a1=a2;
  131    -1         a2=ts.available();
  132    -1       }
  133    -1       ts.process(ibuf, bufferLength, true); // hope two times is enough
  134    -1       int avail=ts.available();
  135    -1 
  136    -1       obuf[0]=new float[avail];
  137    -1       ts.retrieve(obuf, avail);
  138    -1       // write obuf to data
  139    -1       for (int j=0; j<avail && j+avail2<length; ++j) {
  140    -1         float value = obuf[0][j];
  141    -1         if (value > 1.f) value = 1.f;
  142    -1         if (value < -1.f) value = -1.f;
  143    -1         data[j+avail2] = value;
  144    -1       }
  145    -1       avail2+=avail;
  146    -1       delete[] obuf[0];
  147   116     }
  148   117   }
  149    -1   delete[] ibuf[0];
  150    -1   delete[] ibuf;
  151    -1   delete[] obuf;
  152    -1 }
  153   118 
   -1   119 }
  154   120 

diff --git a/src/sample.h b/src/sample.h

@@ -4,26 +4,35 @@
    4     4 #include <sndfile.h>
    5     5 #include <iostream>
    6     6 #include "marker.h"
    7    -1 #include <rubberband/RubberBandStretcher.h>
   -1     7 
   -1     8 /*
   -1     9 Stretchmodes
   -1    10 0 - none (plain sample copy)
   -1    11 1 - rubberband
   -1    12 // TODO define constants
   -1    13 */
    8    14 
    9    15 class Sample {
   10    16 public:
   11    17   Sample(Marker* m);
   12    18   ~Sample();
   13    -1   float *data;
   14    19   float get(float nn); // nnew
   15    20   float getOld(float o);
   16    21   int getLength();
   17    22   int getGuessedLength();
   18    23   int loadFile(const char* filename);
   19    24   int writeFile(const char* filename);
   20    -1   int process();
   -1    25   int process(); // implements the main functionality
   21    26   SF_INFO sfinfo;
   -1    27   int getStretchMode();
   -1    28   void setStretchMode(int m);
   22    29 private:
   -1    30   float *data;
   23    31   int length;
   24    32   Marker* marker;
   25    33   int olength;
   26    34   float *odata;
   -1    35   int stretchMode;
   27    36 };
   28    37 
   29    38 /*

diff --git a/xiRetimer b/xiRetimer

Binary files differ.