| @@ -31,7 +31,7 @@ | |||
| #include <stdio.h> | |||
| #include <algorithm> | |||
| //using std::algorithm; | |||
| // using std::algorithm; | |||
| using namespace std; | |||
| @@ -533,6 +533,162 @@ Region::normalize ( void ) | |||
| } | |||
| /**********/ | |||
| /* Engine */ | |||
| /**********/ | |||
| enum fade_type_e { Linear, Cosine, Logarithmic, Parabolic }; | |||
| enum fade_dir_e { FADE_IN, FADE_OUT }; | |||
| /** Return gain for frame /index/ of /nframes/ on a gain curve of type /type/.*/ | |||
| /* FIXME: calling a function per sample is bad, switching on type mid | |||
| * fade is bad. */ | |||
| static inline float | |||
| fade_gain ( fade_type_e type, nframes_t index, nframes_t nframes ) | |||
| { | |||
| float g = 0; | |||
| const float fi = index / (float)nframes; | |||
| switch ( type ) | |||
| { | |||
| case Linear: | |||
| g = fi; | |||
| break; | |||
| case Cosine: | |||
| // g = sin( fi * M_PI / 2 ); | |||
| g = (1.0f - cos( fi * M_PI )) / 2.0f; | |||
| break; | |||
| case Logarithmic: | |||
| g = pow( 0.1f, (1.0f - fi) * 5.0f ); | |||
| break; | |||
| case Parabolic: | |||
| g = 1.0f - (1.0f - fi) * (1.0f - fi); | |||
| break; | |||
| } | |||
| return g; | |||
| } | |||
| /** Apply a (portion of) fade-out from /start/ to /end/ assuming a | |||
| * buffer size of /nframes/. /start/ and /end/ are relative to the | |||
| * given buffer, and /start/ may be negative. */ | |||
| static void | |||
| apply_fade ( sample_t *buf, fade_dir_e dir, fade_type_e type, long start, nframes_t end, nframes_t nframes ) | |||
| { | |||
| float gain = 1.0f; | |||
| printf( "apply fade %s: start=%ld end=%lu\n", dir == FADE_OUT ? "out" : "in", start, end ); | |||
| nframes_t i = start > 0 ? start : 0; | |||
| nframes_t e = end > nframes ? nframes : end; | |||
| float d = dir == FADE_OUT ? 1.0f : -1.0f; | |||
| if ( dir == FADE_OUT ) | |||
| for ( ; i < e; ++i ) | |||
| { | |||
| long n = end - start; | |||
| const float g = fade_gain( type, (n - 1) - (i - start), n); | |||
| printf( "gain for %lu is %f\n", i, g ); | |||
| buf[ i ] *= g; | |||
| } | |||
| else | |||
| for ( ; i < e; ++i ) | |||
| { | |||
| const float g = fade_gain( type, i - start, end - start ); | |||
| printf( "gain for %lu is %f\n", i, g ); | |||
| buf[ i ] *= g; | |||
| } | |||
| } | |||
| #if 0 | |||
| /** Compute the gain value (0 to 1f) for a fade-in/out curve of /type/ | |||
| * (LINEAR, QUADRAIC, CUBIC), of /nframes/ in length at point | |||
| * /offset/ */ | |||
| static inline | |||
| float gain_on_curve ( int type, int dir, nframes_t nframes, nframes_t offset, nframes_t length ) | |||
| { | |||
| float a, b; | |||
| /* FIXME: these first two sections should *definitely* be cached */ | |||
| /* calculate coefficients */ | |||
| if ( dir == FADE_OUT ) | |||
| { | |||
| a = -1.0f / (double)nframes; | |||
| /* fixme why would we need to know the clip length? */ | |||
| b = length / (double)nframes; | |||
| // b = nframes; | |||
| } | |||
| else | |||
| { | |||
| a = 1.0f / (double)nframes; | |||
| b = 0.0f; | |||
| } | |||
| float c[4]; | |||
| /* interpolate points */ | |||
| switch ( type ) | |||
| { | |||
| case Linear: | |||
| c[1] = a; | |||
| c[0] = b; | |||
| break; | |||
| case Quadratic: | |||
| c[2] = a * a; | |||
| c[1] = 2.0f * a * b; | |||
| c[0] = b * b; | |||
| break; | |||
| case Cubic: | |||
| { | |||
| const float a2 = a * a; | |||
| const float b2 = b * b; | |||
| c[3] = a * a2; | |||
| c[2] = 3.0f * a2 * b; | |||
| c[1] = 3.0f * a * b2; | |||
| c[0] = b * b2; | |||
| break; | |||
| } | |||
| default: | |||
| printf( "unknown curve order\n" ); | |||
| } | |||
| /* now get the gain for the given point */ | |||
| const float f = offset; | |||
| const float f2 = f * f; | |||
| float g = 1.0f; | |||
| switch ( type ) | |||
| { | |||
| case Linear: | |||
| g *= c[1] * f + c[0]; | |||
| break; | |||
| case Quadratic: | |||
| g *= c[2] * f2 + c[1] * f + c[0]; | |||
| break; | |||
| case Cubic: | |||
| g *= c[3] * f2 * f + c[2] * f2 + c[1] * f + c[0]; | |||
| break; | |||
| } | |||
| printf( "gain for %lu is %f\n", offset, g ); | |||
| return g; | |||
| } | |||
| #endif | |||
| /* THREAD: IO */ | |||
| /** read the overlapping part of /channel/ at /pos/ for /nframes/ of | |||
| this region into /buf/, where /pos/ is in timeline frames */ | |||
| /* this runs in the diskstream thread. */ | |||
| @@ -544,7 +700,6 @@ Region::normalize ( void ) | |||
| frames in the Audio_File class instead, so that sequential requests | |||
| for different channels at the same position avoid hitting the disk | |||
| again? */ | |||
| /* FIXME: should fade-out/fade-ins not be handled here? */ | |||
| nframes_t | |||
| Region::read ( sample_t *buf, nframes_t pos, nframes_t nframes, int channel ) const | |||
| { | |||
| @@ -601,6 +756,28 @@ Region::read ( sample_t *buf, nframes_t pos, nframes_t nframes, int channel ) co | |||
| for ( int i = cnt; i--; ) | |||
| buf[i] *= _scale; | |||
| /* TODO: do fade in/out here */ | |||
| /* perform declicking if necessary */ | |||
| const nframes_t declick_frames = 1024; | |||
| if ( start + cnt + declick_frames > r.end ) | |||
| { | |||
| /* declick end */ | |||
| const nframes_t d = r.end - start; | |||
| apply_fade( buf, FADE_OUT, Cosine, cnt + (long)d - declick_frames, cnt + d, cnt ); | |||
| } | |||
| if ( sofs < declick_frames ) | |||
| { | |||
| /* declick start */ | |||
| const long d = 0 - sofs; | |||
| apply_fade( buf + ofs, FADE_IN, Cosine, d, d + declick_frames, cnt - ofs ); | |||
| } | |||
| // printf( "read %lu frames\n", cnt ); | |||
| return cnt; | |||