mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-24 08:07:30 +09:00
Merge remote-tracking branch 'origin/tracking' into custom
This commit is contained in:
commit
5a1dddb843
39 changed files with 1060 additions and 613 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
[](https://travis-ci.org/kinetiknz/nestegg)
|
[](https://github.com/mozilla/nestegg/actions/workflows/build.yml)
|
||||||
|
|
||||||
See INSTALL for build instructions.
|
See INSTALL for build instructions.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system.
|
||||||
|
|
||||||
The nestegg git repository is: https://github.com/kinetiknz/nestegg
|
The nestegg git repository is: https://github.com/kinetiknz/nestegg
|
||||||
|
|
||||||
The git commit ID used was f7a0b7cedc893b6683cf15cb210b1656c086d964.
|
The git commit ID used was ec6adfbbf979678e3058cc4695257366f39e290b.
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,8 @@ extern "C" {
|
||||||
#define NESTEGG_CODEC_VP9 2 /**< Track uses Google On2 VP9 codec. */
|
#define NESTEGG_CODEC_VP9 2 /**< Track uses Google On2 VP9 codec. */
|
||||||
#define NESTEGG_CODEC_OPUS 3 /**< Track uses Xiph Opus codec. */
|
#define NESTEGG_CODEC_OPUS 3 /**< Track uses Xiph Opus codec. */
|
||||||
#define NESTEGG_CODEC_AV1 4 /**< Track uses AOMedia AV1 codec. */
|
#define NESTEGG_CODEC_AV1 4 /**< Track uses AOMedia AV1 codec. */
|
||||||
#define NESTEGG_CODEC_AVC1 5 /**< Track uses AVC1 'h264' */
|
#define NESTEGG_CODEC_AVC1 5 /**< Track uses AVC1 'h264' codec. */
|
||||||
#define NESTEGG_CODEC_AAC 6 /**< Track uses AAC 'mp4a' */
|
#define NESTEGG_CODEC_AAC 6 /**< Track uses AAC 'mp4a' codec. */
|
||||||
#define NESTEGG_CODEC_UNKNOWN INT_MAX /**< Track uses unknown codec. */
|
#define NESTEGG_CODEC_UNKNOWN INT_MAX /**< Track uses unknown codec. */
|
||||||
|
|
||||||
#define NESTEGG_VIDEO_MONO 0 /**< Track is mono video. */
|
#define NESTEGG_VIDEO_MONO 0 /**< Track is mono video. */
|
||||||
|
|
@ -152,6 +152,30 @@ typedef struct {
|
||||||
unsigned int crop_left; /**< Pixels to crop from the left of the frame. */
|
unsigned int crop_left; /**< Pixels to crop from the left of the frame. */
|
||||||
unsigned int crop_right; /**< Pixels to crop from the right of the frame. */
|
unsigned int crop_right; /**< Pixels to crop from the right of the frame. */
|
||||||
unsigned int alpha_mode; /**< 1 if an additional opacity stream is available, otherwise 0. */
|
unsigned int alpha_mode; /**< 1 if an additional opacity stream is available, otherwise 0. */
|
||||||
|
unsigned int matrix_coefficients; /**< See Table 4 of ISO/IEC 23001-8:2016. */
|
||||||
|
unsigned int range; /**< Clipping of color ranges. */
|
||||||
|
unsigned int transfer_characteristics; /**< See Table 3 of ISO/IEC 23091-4. */
|
||||||
|
unsigned int primaries; /**< See Table 2 of ISO/IEC 23091-4. */
|
||||||
|
double primary_r_chromacity_x; /**< Red X chromaticity coordinate per CIE 1931.
|
||||||
|
NaN means element not present. */
|
||||||
|
double primary_r_chromacity_y; /**< Red Y chromaticity coordinate per CIE 1931.
|
||||||
|
NaN means element not present. */
|
||||||
|
double primary_g_chromacity_x; /**< Green X chromaticity coordinate per CIE 1931.
|
||||||
|
NaN means element not present. */
|
||||||
|
double primary_g_chromacity_y; /**< Green Y chromaticity coordinate per CIE 1931.
|
||||||
|
NaN means element not present. */
|
||||||
|
double primary_b_chromacity_x; /**< Blue X chromaticity coordinate per CIE 1931.
|
||||||
|
NaN means element not present. */
|
||||||
|
double primary_b_chromacity_y; /**< Blue Y chromaticity coordinate per CIE 1931.
|
||||||
|
NaN means element not present. */
|
||||||
|
double white_point_chromaticity_x; /**< White X chromaticity coordinate per CIE 1931.
|
||||||
|
NaN means element not present. */
|
||||||
|
double white_point_chromaticity_y; /**< White Y chromaticity coordinate per CIE 1931.
|
||||||
|
NaN means element not present. */
|
||||||
|
double luminance_max; /**< Maximum luminance in cd/m2.
|
||||||
|
NaN means element not present. */
|
||||||
|
double luminance_min; /**< Minimum luminance in cd/m2.
|
||||||
|
NaN means element not present. */
|
||||||
} nestegg_video_params;
|
} nestegg_video_params;
|
||||||
|
|
||||||
/** Parameters specific to an audio track. */
|
/** Parameters specific to an audio track. */
|
||||||
|
|
|
||||||
59
media/libnestegg/mcp-avcaacsupport.patch
Normal file
59
media/libnestegg/mcp-avcaacsupport.patch
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
diff -u /include/nestegg.h /include/nestegg.h
|
||||||
|
--- /include/nestegg.h
|
||||||
|
+++ /include/nestegg.h
|
||||||
|
@@ -72,6 +72,8 @@
|
||||||
|
#define NESTEGG_CODEC_VP9 2 /**< Track uses Google On2 VP9 codec. */
|
||||||
|
#define NESTEGG_CODEC_OPUS 3 /**< Track uses Xiph Opus codec. */
|
||||||
|
#define NESTEGG_CODEC_AV1 4 /**< Track uses AOMedia AV1 codec. */
|
||||||
|
+#define NESTEGG_CODEC_AVC1 5 /**< Track uses AVC1 'h264' codec. */
|
||||||
|
+#define NESTEGG_CODEC_AAC 6 /**< Track uses AAC 'mp4a' codec. */
|
||||||
|
#define NESTEGG_CODEC_UNKNOWN INT_MAX /**< Track uses unknown codec. */
|
||||||
|
|
||||||
|
#define NESTEGG_VIDEO_MONO 0 /**< Track is mono video. */
|
||||||
|
diff -u /src/nestegg.c /src/nestegg.c
|
||||||
|
--- /src/nestegg.c
|
||||||
|
+++ /src/nestegg.c
|
||||||
|
@@ -177,6 +177,8 @@
|
||||||
|
#define TRACK_ID_AV1 "V_AV1"
|
||||||
|
#define TRACK_ID_VORBIS "A_VORBIS"
|
||||||
|
#define TRACK_ID_OPUS "A_OPUS"
|
||||||
|
+#define TRACK_ID_AVC1 "V_MPEG4/ISO/AVC"
|
||||||
|
+#define TRACK_ID_AAC "A_AAC"
|
||||||
|
|
||||||
|
/* Track Encryption */
|
||||||
|
#define CONTENT_ENC_ALGO_AES 5
|
||||||
|
@@ -2482,6 +2484,12 @@
|
||||||
|
if (strcmp(codec_id, TRACK_ID_OPUS) == 0)
|
||||||
|
return NESTEGG_CODEC_OPUS;
|
||||||
|
|
||||||
|
+ if (strcmp(codec_id, TRACK_ID_AVC1) == 0)
|
||||||
|
+ return NESTEGG_CODEC_AVC1;
|
||||||
|
+
|
||||||
|
+ if (strcmp(codec_id, TRACK_ID_AAC) == 0)
|
||||||
|
+ return NESTEGG_CODEC_AAC;
|
||||||
|
+
|
||||||
|
return NESTEGG_CODEC_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -2502,7 +2510,8 @@
|
||||||
|
|
||||||
|
codec_id = nestegg_track_codec_id(ctx, track);
|
||||||
|
|
||||||
|
- if (codec_id == NESTEGG_CODEC_OPUS) {
|
||||||
|
+ if (codec_id == NESTEGG_CODEC_OPUS ||
|
||||||
|
+ codec_id == NESTEGG_CODEC_AAC) {
|
||||||
|
*count = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@@ -2540,7 +2549,9 @@
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_VORBIS &&
|
||||||
|
- nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_OPUS)
|
||||||
|
+ nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_OPUS &&
|
||||||
|
+ nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_AVC1 &&
|
||||||
|
+ nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_AAC)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (ne_get_binary(entry->codec_private, &codec_private) != 0)
|
||||||
|
|
||||||
|
|
@ -88,6 +88,7 @@
|
||||||
#define ID_PIXEL_CROP_RIGHT 0x54dd
|
#define ID_PIXEL_CROP_RIGHT 0x54dd
|
||||||
#define ID_DISPLAY_WIDTH 0x54b0
|
#define ID_DISPLAY_WIDTH 0x54b0
|
||||||
#define ID_DISPLAY_HEIGHT 0x54ba
|
#define ID_DISPLAY_HEIGHT 0x54ba
|
||||||
|
#define ID_COLOUR 0x55b0
|
||||||
|
|
||||||
/* Audio Elements */
|
/* Audio Elements */
|
||||||
#define ID_AUDIO 0xe1
|
#define ID_AUDIO 0xe1
|
||||||
|
|
@ -116,6 +117,25 @@
|
||||||
#define ID_CONTENT_ENC_AES_SETTINGS 0x47e7
|
#define ID_CONTENT_ENC_AES_SETTINGS 0x47e7
|
||||||
#define ID_AES_SETTINGS_CIPHER_MODE 0x47e8
|
#define ID_AES_SETTINGS_CIPHER_MODE 0x47e8
|
||||||
|
|
||||||
|
/* Colour Elements */
|
||||||
|
#define ID_MATRIX_COEFFICIENTS 0x55b1
|
||||||
|
#define ID_RANGE 0x55b9
|
||||||
|
#define ID_TRANSFER_CHARACTERISTICS 0x55ba
|
||||||
|
#define ID_PRIMARIES 0x55bb
|
||||||
|
#define ID_MASTERING_METADATA 0x55d0
|
||||||
|
|
||||||
|
/* MasteringMetadata Elements */
|
||||||
|
#define ID_PRIMARY_R_CHROMATICITY_X 0x55d1
|
||||||
|
#define ID_PRIMARY_R_CHROMATICITY_Y 0x55d2
|
||||||
|
#define ID_PRIMARY_G_CHROMATICITY_X 0x55d3
|
||||||
|
#define ID_PRIMARY_G_CHROMATICITY_Y 0x55d4
|
||||||
|
#define ID_PRIMARY_B_CHROMATICITY_X 0x55d5
|
||||||
|
#define ID_PRIMARY_B_CHROMATICITY_Y 0x55d6
|
||||||
|
#define ID_WHITE_POINT_CHROMATICITY_X 0x55d7
|
||||||
|
#define ID_WHITE_POINT_CHROMATICITY_Y 0x55d8
|
||||||
|
#define ID_LUMINANCE_MAX 0x55d9
|
||||||
|
#define ID_LUMINANCE_MIN 0x55da
|
||||||
|
|
||||||
/* EBML Types */
|
/* EBML Types */
|
||||||
enum ebml_type_enum {
|
enum ebml_type_enum {
|
||||||
TYPE_UNKNOWN,
|
TYPE_UNKNOWN,
|
||||||
|
|
@ -236,6 +256,27 @@ struct info {
|
||||||
struct ebml_type duration;
|
struct ebml_type duration;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct mastering_metadata {
|
||||||
|
struct ebml_type primary_r_chromacity_x;
|
||||||
|
struct ebml_type primary_r_chromacity_y;
|
||||||
|
struct ebml_type primary_g_chromacity_x;
|
||||||
|
struct ebml_type primary_g_chromacity_y;
|
||||||
|
struct ebml_type primary_b_chromacity_x;
|
||||||
|
struct ebml_type primary_b_chromacity_y;
|
||||||
|
struct ebml_type white_point_chromaticity_x;
|
||||||
|
struct ebml_type white_point_chromaticity_y;
|
||||||
|
struct ebml_type luminance_max;
|
||||||
|
struct ebml_type luminance_min;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct colour {
|
||||||
|
struct ebml_type matrix_coefficients;
|
||||||
|
struct ebml_type range;
|
||||||
|
struct ebml_type transfer_characteristics;
|
||||||
|
struct ebml_type primaries;
|
||||||
|
struct mastering_metadata mastering_metadata;
|
||||||
|
};
|
||||||
|
|
||||||
struct video {
|
struct video {
|
||||||
struct ebml_type stereo_mode;
|
struct ebml_type stereo_mode;
|
||||||
struct ebml_type alpha_mode;
|
struct ebml_type alpha_mode;
|
||||||
|
|
@ -247,6 +288,7 @@ struct video {
|
||||||
struct ebml_type pixel_crop_right;
|
struct ebml_type pixel_crop_right;
|
||||||
struct ebml_type display_width;
|
struct ebml_type display_width;
|
||||||
struct ebml_type display_height;
|
struct ebml_type display_height;
|
||||||
|
struct colour colour;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct audio {
|
struct audio {
|
||||||
|
|
@ -454,6 +496,29 @@ static struct ebml_element_desc ne_info_elements[] = {
|
||||||
E_LAST
|
E_LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct ebml_element_desc ne_mastering_metadata_elements[] = {
|
||||||
|
E_FIELD(ID_PRIMARY_R_CHROMATICITY_X, TYPE_FLOAT, struct mastering_metadata, primary_r_chromacity_x),
|
||||||
|
E_FIELD(ID_PRIMARY_R_CHROMATICITY_Y, TYPE_FLOAT, struct mastering_metadata, primary_r_chromacity_y),
|
||||||
|
E_FIELD(ID_PRIMARY_G_CHROMATICITY_X, TYPE_FLOAT, struct mastering_metadata, primary_g_chromacity_x),
|
||||||
|
E_FIELD(ID_PRIMARY_G_CHROMATICITY_Y, TYPE_FLOAT, struct mastering_metadata, primary_g_chromacity_y),
|
||||||
|
E_FIELD(ID_PRIMARY_B_CHROMATICITY_X, TYPE_FLOAT, struct mastering_metadata, primary_b_chromacity_x),
|
||||||
|
E_FIELD(ID_PRIMARY_B_CHROMATICITY_Y, TYPE_FLOAT, struct mastering_metadata, primary_b_chromacity_y),
|
||||||
|
E_FIELD(ID_WHITE_POINT_CHROMATICITY_X, TYPE_FLOAT, struct mastering_metadata, white_point_chromaticity_x),
|
||||||
|
E_FIELD(ID_WHITE_POINT_CHROMATICITY_Y, TYPE_FLOAT, struct mastering_metadata, white_point_chromaticity_y),
|
||||||
|
E_FIELD(ID_LUMINANCE_MAX, TYPE_FLOAT, struct mastering_metadata, luminance_max),
|
||||||
|
E_FIELD(ID_LUMINANCE_MIN, TYPE_FLOAT, struct mastering_metadata, luminance_min),
|
||||||
|
E_LAST
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct ebml_element_desc ne_colour_elements[] = {
|
||||||
|
E_FIELD(ID_MATRIX_COEFFICIENTS, TYPE_UINT, struct colour, matrix_coefficients),
|
||||||
|
E_FIELD(ID_RANGE, TYPE_UINT, struct colour, range),
|
||||||
|
E_FIELD(ID_TRANSFER_CHARACTERISTICS, TYPE_UINT, struct colour, transfer_characteristics),
|
||||||
|
E_FIELD(ID_PRIMARIES, TYPE_UINT, struct colour, primaries),
|
||||||
|
E_SINGLE_MASTER(ID_MASTERING_METADATA, TYPE_MASTER, struct colour, mastering_metadata),
|
||||||
|
E_LAST
|
||||||
|
};
|
||||||
|
|
||||||
static struct ebml_element_desc ne_video_elements[] = {
|
static struct ebml_element_desc ne_video_elements[] = {
|
||||||
E_FIELD(ID_STEREO_MODE, TYPE_UINT, struct video, stereo_mode),
|
E_FIELD(ID_STEREO_MODE, TYPE_UINT, struct video, stereo_mode),
|
||||||
E_FIELD(ID_ALPHA_MODE, TYPE_UINT, struct video, alpha_mode),
|
E_FIELD(ID_ALPHA_MODE, TYPE_UINT, struct video, alpha_mode),
|
||||||
|
|
@ -465,6 +530,7 @@ static struct ebml_element_desc ne_video_elements[] = {
|
||||||
E_FIELD(ID_PIXEL_CROP_RIGHT, TYPE_UINT, struct video, pixel_crop_right),
|
E_FIELD(ID_PIXEL_CROP_RIGHT, TYPE_UINT, struct video, pixel_crop_right),
|
||||||
E_FIELD(ID_DISPLAY_WIDTH, TYPE_UINT, struct video, display_width),
|
E_FIELD(ID_DISPLAY_WIDTH, TYPE_UINT, struct video, display_width),
|
||||||
E_FIELD(ID_DISPLAY_HEIGHT, TYPE_UINT, struct video, display_height),
|
E_FIELD(ID_DISPLAY_HEIGHT, TYPE_UINT, struct video, display_height),
|
||||||
|
E_SINGLE_MASTER(ID_COLOUR, TYPE_MASTER, struct video, colour),
|
||||||
E_LAST
|
E_LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -770,7 +836,15 @@ ne_read_float(nestegg_io * io, double * val, uint64_t length)
|
||||||
{
|
{
|
||||||
union {
|
union {
|
||||||
uint64_t u;
|
uint64_t u;
|
||||||
float f;
|
struct {
|
||||||
|
#if defined(__FLOAT_WORD_ORDER__) && __FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
uint32_t _pad;
|
||||||
|
float f;
|
||||||
|
#else
|
||||||
|
float f;
|
||||||
|
uint32_t _pad;
|
||||||
|
#endif
|
||||||
|
} f;
|
||||||
double d;
|
double d;
|
||||||
} value;
|
} value;
|
||||||
int r;
|
int r;
|
||||||
|
|
@ -782,7 +856,7 @@ ne_read_float(nestegg_io * io, double * val, uint64_t length)
|
||||||
if (r != 1)
|
if (r != 1)
|
||||||
return r;
|
return r;
|
||||||
if (length == 4)
|
if (length == 4)
|
||||||
*val = value.f;
|
*val = value.f.f;
|
||||||
else
|
else
|
||||||
*val = value.d;
|
*val = value.d;
|
||||||
return 1;
|
return 1;
|
||||||
|
|
@ -2062,7 +2136,10 @@ ne_match_webm(nestegg_io io, int64_t max_offset)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ne_ctx_push(ctx, ne_top_level_elements, ctx);
|
if (ne_ctx_push(ctx, ne_top_level_elements, ctx) < 0) {
|
||||||
|
nestegg_destroy(ctx);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* we don't check the return value of ne_parse, that might fail because
|
/* we don't check the return value of ne_parse, that might fail because
|
||||||
max_offset is not on a valid element end point. We only want to check
|
max_offset is not on a valid element end point. We only want to check
|
||||||
|
|
@ -2118,7 +2195,10 @@ nestegg_init(nestegg ** context, nestegg_io io, nestegg_log callback, int64_t ma
|
||||||
|
|
||||||
ctx->log(ctx, NESTEGG_LOG_DEBUG, "ctx %p", ctx);
|
ctx->log(ctx, NESTEGG_LOG_DEBUG, "ctx %p", ctx);
|
||||||
|
|
||||||
ne_ctx_push(ctx, ne_top_level_elements, ctx);
|
if (ne_ctx_push(ctx, ne_top_level_elements, ctx) < 0) {
|
||||||
|
nestegg_destroy(ctx);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
r = ne_parse(ctx, NULL, max_offset);
|
r = ne_parse(ctx, NULL, max_offset);
|
||||||
while (ctx->ancestor)
|
while (ctx->ancestor)
|
||||||
|
|
@ -2178,7 +2258,8 @@ void
|
||||||
nestegg_destroy(nestegg * ctx)
|
nestegg_destroy(nestegg * ctx)
|
||||||
{
|
{
|
||||||
assert(ctx->ancestor == NULL);
|
assert(ctx->ancestor == NULL);
|
||||||
ne_pool_destroy(ctx->alloc_pool);
|
if (ctx->alloc_pool)
|
||||||
|
ne_pool_destroy(ctx->alloc_pool);
|
||||||
free(ctx->io);
|
free(ctx->io);
|
||||||
free(ctx);
|
free(ctx);
|
||||||
}
|
}
|
||||||
|
|
@ -2366,10 +2447,10 @@ nestegg_track_type(nestegg * ctx, unsigned int track)
|
||||||
if (ne_get_uint(entry->type, &type) != 0)
|
if (ne_get_uint(entry->type, &type) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (type & TRACK_TYPE_VIDEO)
|
if (type == TRACK_TYPE_VIDEO)
|
||||||
return NESTEGG_TRACK_VIDEO;
|
return NESTEGG_TRACK_VIDEO;
|
||||||
|
|
||||||
if (type & TRACK_TYPE_AUDIO)
|
if (type == TRACK_TYPE_AUDIO)
|
||||||
return NESTEGG_TRACK_AUDIO;
|
return NESTEGG_TRACK_AUDIO;
|
||||||
|
|
||||||
return NESTEGG_TRACK_UNKNOWN;
|
return NESTEGG_TRACK_UNKNOWN;
|
||||||
|
|
@ -2537,6 +2618,7 @@ nestegg_track_video_params(nestegg * ctx, unsigned int track,
|
||||||
{
|
{
|
||||||
struct track_entry * entry;
|
struct track_entry * entry;
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
|
double fvalue;
|
||||||
|
|
||||||
memset(params, 0, sizeof(*params));
|
memset(params, 0, sizeof(*params));
|
||||||
|
|
||||||
|
|
@ -2589,6 +2671,62 @@ nestegg_track_video_params(nestegg * ctx, unsigned int track,
|
||||||
ne_get_uint(entry->video.display_height, &value);
|
ne_get_uint(entry->video.display_height, &value);
|
||||||
params->display_height = value;
|
params->display_height = value;
|
||||||
|
|
||||||
|
value = 2;
|
||||||
|
ne_get_uint(entry->video.colour.matrix_coefficients, &value);
|
||||||
|
params->matrix_coefficients = value;
|
||||||
|
|
||||||
|
value = 0;
|
||||||
|
ne_get_uint(entry->video.colour.range, &value);
|
||||||
|
params->range = value;
|
||||||
|
|
||||||
|
value = 2;
|
||||||
|
ne_get_uint(entry->video.colour.transfer_characteristics, &value);
|
||||||
|
params->transfer_characteristics = value;
|
||||||
|
|
||||||
|
value = 2;
|
||||||
|
ne_get_uint(entry->video.colour.primaries, &value);
|
||||||
|
params->primaries = value;
|
||||||
|
|
||||||
|
fvalue = strtod("NaN", NULL);
|
||||||
|
ne_get_float(entry->video.colour.mastering_metadata.primary_r_chromacity_x, &fvalue);
|
||||||
|
params->primary_r_chromacity_x = fvalue;
|
||||||
|
|
||||||
|
fvalue = strtod("NaN", NULL);
|
||||||
|
ne_get_float(entry->video.colour.mastering_metadata.primary_r_chromacity_y, &fvalue);
|
||||||
|
params->primary_r_chromacity_y = fvalue;
|
||||||
|
|
||||||
|
fvalue = strtod("NaN", NULL);
|
||||||
|
ne_get_float(entry->video.colour.mastering_metadata.primary_g_chromacity_x, &fvalue);
|
||||||
|
params->primary_g_chromacity_x = fvalue;
|
||||||
|
|
||||||
|
fvalue = strtod("NaN", NULL);
|
||||||
|
ne_get_float(entry->video.colour.mastering_metadata.primary_g_chromacity_y, &fvalue);
|
||||||
|
params->primary_g_chromacity_y = fvalue;
|
||||||
|
|
||||||
|
fvalue = strtod("NaN", NULL);
|
||||||
|
ne_get_float(entry->video.colour.mastering_metadata.primary_b_chromacity_x, &fvalue);
|
||||||
|
params->primary_b_chromacity_x = fvalue;
|
||||||
|
|
||||||
|
fvalue = strtod("NaN", NULL);
|
||||||
|
ne_get_float(entry->video.colour.mastering_metadata.primary_b_chromacity_y, &fvalue);
|
||||||
|
params->primary_b_chromacity_y = fvalue;
|
||||||
|
|
||||||
|
fvalue = strtod("NaN", NULL);
|
||||||
|
ne_get_float(entry->video.colour.mastering_metadata.white_point_chromaticity_x, &fvalue);
|
||||||
|
params->white_point_chromaticity_x = fvalue;
|
||||||
|
|
||||||
|
fvalue = strtod("NaN", NULL);
|
||||||
|
ne_get_float(entry->video.colour.mastering_metadata.white_point_chromaticity_y, &fvalue);
|
||||||
|
params->white_point_chromaticity_y = fvalue;
|
||||||
|
|
||||||
|
fvalue = strtod("NaN", NULL);
|
||||||
|
ne_get_float(entry->video.colour.mastering_metadata.luminance_max, &fvalue);
|
||||||
|
params->luminance_max = fvalue;
|
||||||
|
|
||||||
|
fvalue = strtod("NaN", NULL);
|
||||||
|
ne_get_float(entry->video.colour.mastering_metadata.luminance_min, &fvalue);
|
||||||
|
params->luminance_min = fvalue;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2783,20 +2921,18 @@ nestegg_read_packet(nestegg * ctx, nestegg_packet ** pkt)
|
||||||
if (r != 1)
|
if (r != 1)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
/* Some files have a crc32 element, since it also has to be first it
|
/* Matroska may place a CRC32 before the Timecode. Skip and continue parsing. */
|
||||||
conflicts with the timecode spec. Just ignore it */
|
|
||||||
if (id == ID_CRC32) {
|
if (id == ID_CRC32) {
|
||||||
ctx->log(ctx, NESTEGG_LOG_DEBUG,
|
r = ne_io_read_skip(ctx->io, size);
|
||||||
"read_packet: skipping crc element in a cluster");
|
if (r != 1)
|
||||||
r = ne_io_read_skip(ctx->io, size);
|
return r;
|
||||||
if (r != 1)
|
|
||||||
return r;
|
r = ne_read_element(ctx, &id, &size);
|
||||||
r = ne_read_element(ctx, &id, &size);
|
if (r != 1)
|
||||||
if (r != 1)
|
return r;
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Timecode must be the first element in a Cluster, per spec. */
|
/* Timecode must be the first element in a Cluster, per WebM spec. */
|
||||||
if (id != ID_TIMECODE)
|
if (id != ID_TIMECODE)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@ cp $1/src/nestegg.c src
|
||||||
cp $1/LICENSE .
|
cp $1/LICENSE .
|
||||||
cp $1/README.md .
|
cp $1/README.md .
|
||||||
cp $1/AUTHORS .
|
cp $1/AUTHORS .
|
||||||
|
|
||||||
|
# Patch the imported files.
|
||||||
|
patch -p1 < mcp-avcaacsupport.patch
|
||||||
|
|
||||||
if [ -d $1/.git ]; then
|
if [ -d $1/.git ]; then
|
||||||
rev=$(cd $1 && git rev-parse --verify HEAD)
|
rev=$(cd $1 && git rev-parse --verify HEAD)
|
||||||
dirty=$(cd $1 && git diff-index --name-only HEAD)
|
dirty=$(cd $1 && git diff-index --name-only HEAD)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
The SoundTouch Library
|
The SoundTouch Library
|
||||||
Copyright © Olli Parviainen 2001-2012
|
Copyright © Olli Parviainen 2001-2021
|
||||||
|
|
||||||
http://www.surina.net/soundtouch/
|
http://www.surina.net/soundtouch/
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
Version 2.1, February 1999
|
Version 2.1, February 1999
|
||||||
|
|
||||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
@ -117,7 +117,7 @@ be combined with the library in order to run.
|
||||||
|
|
||||||
0. This License Agreement applies to any software library or other
|
0. This License Agreement applies to any software library or other
|
||||||
program which contains a notice placed by the copyright holder or
|
program which contains a notice placed by the copyright holder or
|
||||||
other authoried party saying it may be distributed under the terms of
|
other authorized party saying it may be distributed under the terms of
|
||||||
this Lesser General Public License (also called "this License").
|
this Lesser General Public License (also called "this License").
|
||||||
Each licensee is addressed as "you".
|
Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
These files are from the SoundTouch library (http://www.surina.net/soundtouch/),
|
These files are from the SoundTouch library (http://www.surina.net/soundtouch/),
|
||||||
and are extracted from the revision r222 of the svn repository at
|
and are extracted from tagged version 2.3.1 of the git repository at
|
||||||
https://soundtouch.svn.sourceforge.net/svnroot/soundtouch/trunk.
|
https://codeberg.org/soundtouch/soundtouch/.
|
||||||
|
|
||||||
The whole library is not used, only the relevant files are imported in the tree,
|
The whole library is not used, only the relevant files are imported in the tree,
|
||||||
using the script `update.sh`. Some changes have been made to the files, using
|
using the script `update.sh`. Some changes have been made to the files, using
|
||||||
13
media/libsoundtouch/mcp-disableexceptions.patch
Normal file
13
media/libsoundtouch/mcp-disableexceptions.patch
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
diff -u /src/STTypes.h /src/STTypes.h
|
||||||
|
--- /src/STTypes.h
|
||||||
|
+++ /src/STTypes.h
|
||||||
|
@@ -174,7 +174,7 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
// define ST_NO_EXCEPTION_HANDLING switch to disable throwing std exceptions:
|
||||||
|
-// #define ST_NO_EXCEPTION_HANDLING 1
|
||||||
|
+#define ST_NO_EXCEPTION_HANDLING 1
|
||||||
|
#ifdef ST_NO_EXCEPTION_HANDLING
|
||||||
|
// Exceptions disabled. Throw asserts instead if enabled.
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
|
@ -56,7 +56,7 @@ diff -u /src/cpu_detect_x86.cpp /src/cpu_detect_x86.cpp
|
||||||
diff -u /src/STTypes.h /src/STTypes.h
|
diff -u /src/STTypes.h /src/STTypes.h
|
||||||
--- /src/STTypes.h
|
--- /src/STTypes.h
|
||||||
+++ /src/STTypes.h
|
+++ /src/STTypes.h
|
||||||
@@ -54,12 +54,13 @@
|
@@ -54,12 +54,17 @@
|
||||||
#define SOUNDTOUCH_ALIGN_POINTER_16(x) ( ( (ulongptr)(x) + 15 ) & ~(ulongptr)15 )
|
#define SOUNDTOUCH_ALIGN_POINTER_16(x) ( ( (ulongptr)(x) + 15 ) & ~(ulongptr)15 )
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -68,9 +68,13 @@ diff -u /src/STTypes.h /src/STTypes.h
|
||||||
+#include "soundtouch_config.h"
|
+#include "soundtouch_config.h"
|
||||||
|
|
||||||
+#if defined(WIN32)
|
+#if defined(WIN32)
|
||||||
+#define EXPORT __declspec(dllexport)
|
+#if defined(BUILDING_SOUNDTOUCH)
|
||||||
|
+#define SOUNDTOUCH_API __declspec(dllexport)
|
||||||
+#else
|
+#else
|
||||||
+#define EXPORT
|
+#define SOUNDTOUCH_API __declspec(dllimport)
|
||||||
|
+#endif
|
||||||
|
+#else
|
||||||
|
+#define SOUNDTOUCH_API
|
||||||
+#endif
|
+#endif
|
||||||
|
|
||||||
namespace soundtouch
|
namespace soundtouch
|
||||||
|
|
@ -83,7 +87,7 @@ diff -u /src/SoundTouch.h /src/SoundTouch.h
|
||||||
#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7
|
#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7
|
||||||
|
|
||||||
-class SoundTouch : public FIFOProcessor
|
-class SoundTouch : public FIFOProcessor
|
||||||
+class EXPORT SoundTouch : public FIFOProcessor
|
+class SOUNDTOUCH_API SoundTouch : public FIFOProcessor
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
/// Rate transposer class instance
|
/// Rate transposer class instance
|
||||||
|
|
@ -117,25 +121,16 @@ diff -u /src/TDStretch.cpp /src/TDStretch.cpp
|
||||||
|
|
||||||
// Check if MMX/SSE instruction set extensions supported by CPU
|
// Check if MMX/SSE instruction set extensions supported by CPU
|
||||||
|
|
||||||
diff --git a/media/libsoundtouch/src/AAFilter.cpp b/media/libsoundtouch/src/AAFilter.cpp
|
diff -u /src/AAFilter.cpp /src/AAFilter.cpp
|
||||||
--- a/media/libsoundtouch/src/AAFilter.cpp
|
--- /src/AAFilter.cpp
|
||||||
+++ b/media/libsoundtouch/src/AAFilter.cpp
|
+++ /src/AAFilter.cpp
|
||||||
@@ -44,17 +44,17 @@
|
@@ -42,7 +42,7 @@
|
||||||
#include <assert.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "AAFilter.h"
|
|
||||||
#include "FIRFilter.h"
|
|
||||||
|
|
||||||
using namespace soundtouch;
|
using namespace soundtouch;
|
||||||
|
|
||||||
-#define PI 3.141592655357989
|
-#define PI 3.14159265358979323846
|
||||||
+#define PI M_PI
|
+#define PI M_PI
|
||||||
#define TWOPI (2 * PI)
|
#define TWOPI (2 * PI)
|
||||||
|
|
||||||
// define this to save AA filter coefficients to a file
|
// define this to save AA filter coefficients to a file
|
||||||
// #define _DEBUG_SAVE_AAFILTER_COEFFICIENTS 1
|
|
||||||
|
|
||||||
#ifdef _DEBUG_SAVE_AAFILTER_COEFFICIENTS
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-01-05 21:40:22 +0000 (Sun, 05 Jan 2014) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: AAFilter.cpp 177 2014-01-05 21:40:22Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -75,7 +68,6 @@ using namespace soundtouch;
|
||||||
#define _DEBUG_SAVE_AAFIR_COEFFS(x, y)
|
#define _DEBUG_SAVE_AAFIR_COEFFS(x, y)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
* Implementation of the class 'AAFilter'
|
* Implementation of the class 'AAFilter'
|
||||||
|
|
@ -90,14 +82,12 @@ AAFilter::AAFilter(uint len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AAFilter::~AAFilter()
|
AAFilter::~AAFilter()
|
||||||
{
|
{
|
||||||
delete pFIR;
|
delete pFIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new anti-alias filter cut-off edge frequency, scaled to
|
// Sets new anti-alias filter cut-off edge frequency, scaled to
|
||||||
// sampling frequency (nyquist frequency = 0.5).
|
// sampling frequency (nyquist frequency = 0.5).
|
||||||
// The filter will cut frequencies higher than the given frequency.
|
// The filter will cut frequencies higher than the given frequency.
|
||||||
|
|
@ -108,7 +98,6 @@ void AAFilter::setCutoffFreq(double newCutoffFreq)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets number of FIR filter taps
|
// Sets number of FIR filter taps
|
||||||
void AAFilter::setLength(uint newLength)
|
void AAFilter::setLength(uint newLength)
|
||||||
{
|
{
|
||||||
|
|
@ -117,7 +106,6 @@ void AAFilter::setLength(uint newLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Calculates coefficients for a low-pass FIR filter using Hamming window
|
// Calculates coefficients for a low-pass FIR filter using Hamming window
|
||||||
void AAFilter::calculateCoeffs()
|
void AAFilter::calculateCoeffs()
|
||||||
{
|
{
|
||||||
|
|
@ -177,12 +165,10 @@ void AAFilter::calculateCoeffs()
|
||||||
for (i = 0; i < length; i ++)
|
for (i = 0; i < length; i ++)
|
||||||
{
|
{
|
||||||
temp = work[i] * scaleCoeff;
|
temp = work[i] * scaleCoeff;
|
||||||
//#if SOUNDTOUCH_INTEGER_SAMPLES
|
|
||||||
// scale & round to nearest integer
|
// scale & round to nearest integer
|
||||||
temp += (temp >= 0) ? 0.5 : -0.5;
|
temp += (temp >= 0) ? 0.5 : -0.5;
|
||||||
// ensure no overfloods
|
// ensure no overfloods
|
||||||
assert(temp >= -32768 && temp <= 32767);
|
assert(temp >= -32768 && temp <= 32767);
|
||||||
//#endif
|
|
||||||
coeffs[i] = (SAMPLETYPE)temp;
|
coeffs[i] = (SAMPLETYPE)temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-01-07 19:41:23 +0000 (Tue, 07 Jan 2014) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: AAFilter.h 187 2014-01-07 19:41:23Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-11-08 18:53:01 +0000 (Thu, 08 Nov 2012) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: FIFOSampleBuffer.cpp 160 2012-11-08 18:53:01Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -80,7 +73,8 @@ void FIFOSampleBuffer::setChannels(int numChannels)
|
||||||
{
|
{
|
||||||
uint usedBytes;
|
uint usedBytes;
|
||||||
|
|
||||||
assert(numChannels > 0);
|
if (!verifyNumberOfChannels(numChannels)) return;
|
||||||
|
|
||||||
usedBytes = channels * samplesInBuffer;
|
usedBytes = channels * samplesInBuffer;
|
||||||
channels = (uint)numChannels;
|
channels = (uint)numChannels;
|
||||||
samplesInBuffer = usedBytes / channels;
|
samplesInBuffer = usedBytes / channels;
|
||||||
|
|
@ -131,7 +125,7 @@ void FIFOSampleBuffer::putSamples(uint nSamples)
|
||||||
//
|
//
|
||||||
// Parameter 'slackCapacity' tells the function how much free capacity (in
|
// Parameter 'slackCapacity' tells the function how much free capacity (in
|
||||||
// terms of samples) there _at least_ should be, in order to the caller to
|
// terms of samples) there _at least_ should be, in order to the caller to
|
||||||
// succesfully insert all the required samples to the buffer. When necessary,
|
// successfully insert all the required samples to the buffer. When necessary,
|
||||||
// the function grows the buffer size to comply with this requirement.
|
// the function grows the buffer size to comply with this requirement.
|
||||||
//
|
//
|
||||||
// When using this function as means for inserting new samples, also remember
|
// When using this function as means for inserting new samples, also remember
|
||||||
|
|
@ -158,7 +152,7 @@ SAMPLETYPE *FIFOSampleBuffer::ptrBegin()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Ensures that the buffer has enought capacity, i.e. space for _at least_
|
// Ensures that the buffer has enough capacity, i.e. space for _at least_
|
||||||
// 'capacityRequirement' number of samples. The buffer is grown in steps of
|
// 'capacityRequirement' number of samples. The buffer is grown in steps of
|
||||||
// 4 kilobytes to eliminate the need for frequently growing up the buffer,
|
// 4 kilobytes to eliminate the need for frequently growing up the buffer,
|
||||||
// as well as to round the buffer size up to the virtual memory page size.
|
// as well as to round the buffer size up to the virtual memory page size.
|
||||||
|
|
@ -272,3 +266,10 @@ uint FIFOSampleBuffer::adjustAmountOfSamples(uint numSamples)
|
||||||
return samplesInBuffer;
|
return samplesInBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Add silence to end of buffer
|
||||||
|
void FIFOSampleBuffer::addSilent(uint nSamples)
|
||||||
|
{
|
||||||
|
memset(ptrEnd(nSamples), 0, sizeof(SAMPLETYPE) * nSamples * channels);
|
||||||
|
samplesInBuffer += nSamples;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-01-05 21:40:22 +0000 (Sun, 05 Jan 2014) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: FIFOSampleBuffer.h 177 2014-01-05 21:40:22Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -119,7 +112,7 @@ public:
|
||||||
/// 'putSamples(numSamples)' function.
|
/// 'putSamples(numSamples)' function.
|
||||||
SAMPLETYPE *ptrEnd(
|
SAMPLETYPE *ptrEnd(
|
||||||
uint slackCapacity ///< How much free capacity (in samples) there _at least_
|
uint slackCapacity ///< How much free capacity (in samples) there _at least_
|
||||||
///< should be so that the caller can succesfully insert the
|
///< should be so that the caller can successfully insert the
|
||||||
///< desired samples to the buffer. If necessary, the function
|
///< desired samples to the buffer. If necessary, the function
|
||||||
///< grows the buffer size to comply with this requirement.
|
///< grows the buffer size to comply with this requirement.
|
||||||
);
|
);
|
||||||
|
|
@ -177,6 +170,9 @@ public:
|
||||||
/// allow trimming (downwards) amount of samples in pipeline.
|
/// allow trimming (downwards) amount of samples in pipeline.
|
||||||
/// Returns adjusted amount of samples
|
/// Returns adjusted amount of samples
|
||||||
uint adjustAmountOfSamples(uint numSamples);
|
uint adjustAmountOfSamples(uint numSamples);
|
||||||
|
|
||||||
|
/// Add silence to end of buffer
|
||||||
|
void addSilent(uint nSamples);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-06-13 19:29:53 +0000 (Wed, 13 Jun 2012) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: FIFOSamplePipe.h 143 2012-06-13 19:29:53Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -58,6 +51,18 @@ namespace soundtouch
|
||||||
/// Abstract base class for FIFO (first-in-first-out) sample processing classes.
|
/// Abstract base class for FIFO (first-in-first-out) sample processing classes.
|
||||||
class FIFOSamplePipe
|
class FIFOSamplePipe
|
||||||
{
|
{
|
||||||
|
protected:
|
||||||
|
|
||||||
|
bool verifyNumberOfChannels(int nChannels) const
|
||||||
|
{
|
||||||
|
if ((nChannels > 0) && (nChannels <= SOUNDTOUCH_MAX_CHANNELS))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ST_THROW_RT_ERROR("Error: Illegal number of channels");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// virtual default destructor
|
// virtual default destructor
|
||||||
virtual ~FIFOSamplePipe() {}
|
virtual ~FIFOSamplePipe() {}
|
||||||
|
|
@ -122,7 +127,6 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Base-class for sound processing routines working in FIFO principle. With this base
|
/// Base-class for sound processing routines working in FIFO principle. With this base
|
||||||
/// class it's easy to implement sound processing stages that can be chained together,
|
/// class it's easy to implement sound processing stages that can be chained together,
|
||||||
/// so that samples that are fed into beginning of the pipe automatically go through
|
/// so that samples that are fed into beginning of the pipe automatically go through
|
||||||
|
|
@ -145,7 +149,6 @@ protected:
|
||||||
output = pOutput;
|
output = pOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Constructor. Doesn't define output pipe; it has to be set be
|
/// Constructor. Doesn't define output pipe; it has to be set be
|
||||||
/// 'setOutPipe' function.
|
/// 'setOutPipe' function.
|
||||||
FIFOProcessor()
|
FIFOProcessor()
|
||||||
|
|
@ -153,7 +156,6 @@ protected:
|
||||||
output = NULL;
|
output = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Constructor. Configures output pipe.
|
/// Constructor. Configures output pipe.
|
||||||
FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe.
|
FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe.
|
||||||
)
|
)
|
||||||
|
|
@ -161,13 +163,11 @@ protected:
|
||||||
output = pOutput;
|
output = pOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Destructor.
|
/// Destructor.
|
||||||
virtual ~FIFOProcessor()
|
virtual ~FIFOProcessor()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Returns a pointer to the beginning of the output samples.
|
/// Returns a pointer to the beginning of the output samples.
|
||||||
/// This function is provided for accessing the output samples directly.
|
/// This function is provided for accessing the output samples directly.
|
||||||
/// Please be careful for not to corrupt the book-keeping!
|
/// Please be careful for not to corrupt the book-keeping!
|
||||||
|
|
@ -194,7 +194,6 @@ public:
|
||||||
return output->receiveSamples(outBuffer, maxSamples);
|
return output->receiveSamples(outBuffer, maxSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
/// sample buffer without copying them anywhere.
|
/// sample buffer without copying them anywhere.
|
||||||
///
|
///
|
||||||
|
|
@ -206,14 +205,12 @@ public:
|
||||||
return output->receiveSamples(maxSamples);
|
return output->receiveSamples(maxSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Returns number of samples currently available.
|
/// Returns number of samples currently available.
|
||||||
virtual uint numSamples() const
|
virtual uint numSamples() const
|
||||||
{
|
{
|
||||||
return output->numSamples();
|
return output->numSamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Returns nonzero if there aren't any samples available for outputting.
|
/// Returns nonzero if there aren't any samples available for outputting.
|
||||||
virtual int isEmpty() const
|
virtual int isEmpty() const
|
||||||
{
|
{
|
||||||
|
|
@ -226,7 +223,6 @@ public:
|
||||||
{
|
{
|
||||||
return output->adjustAmountOfSamples(numSamples);
|
return output->adjustAmountOfSamples(numSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,21 @@
|
||||||
///
|
///
|
||||||
/// General FIR digital filter routines with MMX optimization.
|
/// General FIR digital filter routines with MMX optimization.
|
||||||
///
|
///
|
||||||
/// Note : MMX optimized functions reside in a separate, platform-specific file,
|
/// Notes : MMX optimized functions reside in a separate, platform-specific file,
|
||||||
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||||
///
|
///
|
||||||
|
/// This source file contains OpenMP optimizations that allow speeding up the
|
||||||
|
/// corss-correlation algorithm by executing it in several threads / CPU cores
|
||||||
|
/// in parallel. See the following article link for more detailed discussion
|
||||||
|
/// about SoundTouch OpenMP optimizations:
|
||||||
|
/// http://www.softwarecoven.com/parallel-computing-in-embedded-mobile-devices
|
||||||
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
/// Author e-mail : oparviai 'at' iki.fi
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-02-21 21:24:29 +0000 (Sat, 21 Feb 2015) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: FIRFilter.cpp 202 2015-02-21 21:24:29Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -61,14 +60,17 @@ FIRFilter::FIRFilter()
|
||||||
length = 0;
|
length = 0;
|
||||||
lengthDiv8 = 0;
|
lengthDiv8 = 0;
|
||||||
filterCoeffs = NULL;
|
filterCoeffs = NULL;
|
||||||
|
filterCoeffsStereo = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FIRFilter::~FIRFilter()
|
FIRFilter::~FIRFilter()
|
||||||
{
|
{
|
||||||
delete[] filterCoeffs;
|
delete[] filterCoeffs;
|
||||||
|
delete[] filterCoeffsStereo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Usual C-version of the filter routine for stereo sound
|
// Usual C-version of the filter routine for stereo sound
|
||||||
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||||
{
|
{
|
||||||
|
|
@ -78,35 +80,26 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
|
||||||
// because division is much slower operation than multiplying.
|
// because division is much slower operation than multiplying.
|
||||||
double dScaler = 1.0 / (double)resultDivider;
|
double dScaler = 1.0 / (double)resultDivider;
|
||||||
#endif
|
#endif
|
||||||
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = length & -8;
|
||||||
|
|
||||||
assert(length != 0);
|
assert((length != 0) && (length == ilength) && (src != NULL) && (dest != NULL) && (filterCoeffs != NULL));
|
||||||
assert(src != NULL);
|
|
||||||
assert(dest != NULL);
|
|
||||||
assert(filterCoeffs != NULL);
|
|
||||||
|
|
||||||
end = 2 * (numSamples - length);
|
end = 2 * (numSamples - ilength);
|
||||||
|
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
for (j = 0; j < end; j += 2)
|
for (j = 0; j < end; j += 2)
|
||||||
{
|
{
|
||||||
const SAMPLETYPE *ptr;
|
const SAMPLETYPE *ptr;
|
||||||
LONG_SAMPLETYPE suml, sumr;
|
LONG_SAMPLETYPE suml, sumr;
|
||||||
uint i;
|
|
||||||
|
|
||||||
suml = sumr = 0;
|
suml = sumr = 0;
|
||||||
ptr = src + j;
|
ptr = src + j;
|
||||||
|
|
||||||
for (i = 0; i < length; i += 4)
|
for (int i = 0; i < ilength; i ++)
|
||||||
{
|
{
|
||||||
// loop is unrolled by factor of 4 here for efficiency
|
suml += ptr[2 * i] * filterCoeffsStereo[2 * i];
|
||||||
suml += ptr[2 * i + 0] * filterCoeffs[i + 0] +
|
sumr += ptr[2 * i + 1] * filterCoeffsStereo[2 * i + 1];
|
||||||
ptr[2 * i + 2] * filterCoeffs[i + 1] +
|
|
||||||
ptr[2 * i + 4] * filterCoeffs[i + 2] +
|
|
||||||
ptr[2 * i + 6] * filterCoeffs[i + 3];
|
|
||||||
sumr += ptr[2 * i + 1] * filterCoeffs[i + 0] +
|
|
||||||
ptr[2 * i + 3] * filterCoeffs[i + 1] +
|
|
||||||
ptr[2 * i + 5] * filterCoeffs[i + 2] +
|
|
||||||
ptr[2 * i + 7] * filterCoeffs[i + 3];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
|
@ -116,19 +109,14 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
|
||||||
suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml;
|
suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml;
|
||||||
// saturate to 16 bit integer limits
|
// saturate to 16 bit integer limits
|
||||||
sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr;
|
sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr;
|
||||||
#else
|
|
||||||
suml *= dScaler;
|
|
||||||
sumr *= dScaler;
|
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
dest[j] = (SAMPLETYPE)suml;
|
dest[j] = (SAMPLETYPE)suml;
|
||||||
dest[j + 1] = (SAMPLETYPE)sumr;
|
dest[j + 1] = (SAMPLETYPE)sumr;
|
||||||
}
|
}
|
||||||
return numSamples - length;
|
return numSamples - ilength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Usual C-version of the filter routine for mono sound
|
// Usual C-version of the filter routine for mono sound
|
||||||
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||||
{
|
{
|
||||||
|
|
@ -139,31 +127,28 @@ uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint
|
||||||
double dScaler = 1.0 / (double)resultDivider;
|
double dScaler = 1.0 / (double)resultDivider;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
assert(length != 0);
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = length & -8;
|
||||||
|
|
||||||
end = numSamples - length;
|
assert(ilength != 0);
|
||||||
|
|
||||||
|
end = numSamples - ilength;
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
for (j = 0; j < end; j ++)
|
for (j = 0; j < end; j ++)
|
||||||
{
|
{
|
||||||
const SAMPLETYPE *pSrc = src + j;
|
const SAMPLETYPE *pSrc = src + j;
|
||||||
LONG_SAMPLETYPE sum;
|
LONG_SAMPLETYPE sum;
|
||||||
uint i;
|
int i;
|
||||||
|
|
||||||
sum = 0;
|
sum = 0;
|
||||||
for (i = 0; i < length; i += 4)
|
for (i = 0; i < ilength; i ++)
|
||||||
{
|
{
|
||||||
// loop is unrolled by factor of 4 here for efficiency
|
sum += pSrc[i] * filterCoeffs[i];
|
||||||
sum += pSrc[i + 0] * filterCoeffs[i + 0] +
|
|
||||||
pSrc[i + 1] * filterCoeffs[i + 1] +
|
|
||||||
pSrc[i + 2] * filterCoeffs[i + 2] +
|
|
||||||
pSrc[i + 3] * filterCoeffs[i + 3];
|
|
||||||
}
|
}
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
sum >>= resultDivFactor;
|
sum >>= resultDivFactor;
|
||||||
// saturate to 16 bit integer limits
|
// saturate to 16 bit integer limits
|
||||||
sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum;
|
sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum;
|
||||||
#else
|
|
||||||
sum *= dScaler;
|
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
dest[j] = (SAMPLETYPE)sum;
|
dest[j] = (SAMPLETYPE)sum;
|
||||||
}
|
}
|
||||||
|
|
@ -187,14 +172,18 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
|
||||||
assert(filterCoeffs != NULL);
|
assert(filterCoeffs != NULL);
|
||||||
assert(numChannels < 16);
|
assert(numChannels < 16);
|
||||||
|
|
||||||
end = numChannels * (numSamples - length);
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = length & -8;
|
||||||
|
|
||||||
|
end = numChannels * (numSamples - ilength);
|
||||||
|
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
for (j = 0; j < end; j += numChannels)
|
for (j = 0; j < end; j += numChannels)
|
||||||
{
|
{
|
||||||
const SAMPLETYPE *ptr;
|
const SAMPLETYPE *ptr;
|
||||||
LONG_SAMPLETYPE sums[16];
|
LONG_SAMPLETYPE sums[16];
|
||||||
uint c, i;
|
uint c;
|
||||||
|
int i;
|
||||||
|
|
||||||
for (c = 0; c < numChannels; c ++)
|
for (c = 0; c < numChannels; c ++)
|
||||||
{
|
{
|
||||||
|
|
@ -203,7 +192,7 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
|
||||||
|
|
||||||
ptr = src + j;
|
ptr = src + j;
|
||||||
|
|
||||||
for (i = 0; i < length; i ++)
|
for (i = 0; i < ilength; i ++)
|
||||||
{
|
{
|
||||||
SAMPLETYPE coef=filterCoeffs[i];
|
SAMPLETYPE coef=filterCoeffs[i];
|
||||||
for (c = 0; c < numChannels; c ++)
|
for (c = 0; c < numChannels; c ++)
|
||||||
|
|
@ -217,13 +206,11 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
|
||||||
{
|
{
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
sums[c] >>= resultDivFactor;
|
sums[c] >>= resultDivFactor;
|
||||||
#else
|
|
||||||
sums[c] *= dScaler;
|
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
dest[j+c] = (SAMPLETYPE)sums[c];
|
dest[j+c] = (SAMPLETYPE)sums[c];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return numSamples - length;
|
return numSamples - ilength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -235,6 +222,13 @@ void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint u
|
||||||
assert(newLength > 0);
|
assert(newLength > 0);
|
||||||
if (newLength % 8) ST_THROW_RT_ERROR("FIR filter length not divisible by 8");
|
if (newLength % 8) ST_THROW_RT_ERROR("FIR filter length not divisible by 8");
|
||||||
|
|
||||||
|
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
// scale coefficients already here if using floating samples
|
||||||
|
double scale = 1.0 / resultDivider;
|
||||||
|
#else
|
||||||
|
short scale = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
lengthDiv8 = newLength / 8;
|
lengthDiv8 = newLength / 8;
|
||||||
length = lengthDiv8 * 8;
|
length = lengthDiv8 * 8;
|
||||||
assert(length == newLength);
|
assert(length == newLength);
|
||||||
|
|
@ -244,7 +238,16 @@ void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint u
|
||||||
|
|
||||||
delete[] filterCoeffs;
|
delete[] filterCoeffs;
|
||||||
filterCoeffs = new SAMPLETYPE[length];
|
filterCoeffs = new SAMPLETYPE[length];
|
||||||
memcpy(filterCoeffs, coeffs, length * sizeof(SAMPLETYPE));
|
delete[] filterCoeffsStereo;
|
||||||
|
filterCoeffsStereo = new SAMPLETYPE[length*2];
|
||||||
|
for (uint i = 0; i < length; i ++)
|
||||||
|
{
|
||||||
|
filterCoeffs[i] = (SAMPLETYPE)(coeffs[i] * scale);
|
||||||
|
// create also stereo set of filter coefficients: this allows compiler
|
||||||
|
// to autovectorize filter evaluation much more efficiently
|
||||||
|
filterCoeffsStereo[2 * i] = (SAMPLETYPE)(coeffs[i] * scale);
|
||||||
|
filterCoeffsStereo[2 * i + 1] = (SAMPLETYPE)(coeffs[i] * scale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -254,7 +257,6 @@ uint FIRFilter::getLength() const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Applies the filter to the given sequence of samples.
|
// Applies the filter to the given sequence of samples.
|
||||||
//
|
//
|
||||||
// Note : The amount of outputted samples is by value of 'filter_length'
|
// Note : The amount of outputted samples is by value of 'filter_length'
|
||||||
|
|
@ -284,7 +286,6 @@ uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSample
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||||
// depending on if we've a MMX-capable CPU available or not.
|
// depending on if we've a MMX-capable CPU available or not.
|
||||||
void * FIRFilter::operator new(size_t s)
|
void * FIRFilter::operator new(size_t s)
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-02-21 21:24:29 +0000 (Sat, 21 Feb 2015) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: FIRFilter.h 202 2015-02-21 21:24:29Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -64,6 +57,7 @@ protected:
|
||||||
|
|
||||||
// Memory for filter coefficients
|
// Memory for filter coefficients
|
||||||
SAMPLETYPE *filterCoeffs;
|
SAMPLETYPE *filterCoeffs;
|
||||||
|
SAMPLETYPE *filterCoeffsStereo;
|
||||||
|
|
||||||
virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
|
virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// $Id: InterpolateCubic.cpp 179 2014-01-06 18:41:42Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// $Id: InterpolateCubic.h 179 2014-01-06 18:41:42Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -45,7 +41,6 @@ namespace soundtouch
|
||||||
class InterpolateCubic : public TransposerBase
|
class InterpolateCubic : public TransposerBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
virtual void resetRegisters();
|
|
||||||
virtual int transposeMono(SAMPLETYPE *dest,
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples);
|
int &srcSamples);
|
||||||
|
|
@ -56,10 +51,17 @@ protected:
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples);
|
int &srcSamples);
|
||||||
|
|
||||||
float fract;
|
double fract;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InterpolateCubic();
|
InterpolateCubic();
|
||||||
|
|
||||||
|
virtual void resetRegisters();
|
||||||
|
|
||||||
|
int getLatency() const
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// $Id: InterpolateLinear.cpp 180 2014-01-06 19:16:02Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -146,7 +142,7 @@ int InterpolateLinearInteger::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE
|
||||||
LONG_SAMPLETYPE temp, vol1;
|
LONG_SAMPLETYPE temp, vol1;
|
||||||
|
|
||||||
assert(iFract < SCALE);
|
assert(iFract < SCALE);
|
||||||
vol1 = (SCALE - iFract);
|
vol1 = (LONG_SAMPLETYPE)(SCALE - iFract);
|
||||||
for (int c = 0; c < numChannels; c ++)
|
for (int c = 0; c < numChannels; c ++)
|
||||||
{
|
{
|
||||||
temp = vol1 * src[c] + iFract * src[c + numChannels];
|
temp = vol1 * src[c] + iFract * src[c + numChannels];
|
||||||
|
|
@ -170,9 +166,9 @@ int InterpolateLinearInteger::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE
|
||||||
|
|
||||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||||
// iRate, larger faster iRates.
|
// iRate, larger faster iRates.
|
||||||
void InterpolateLinearInteger::setRate(float newRate)
|
void InterpolateLinearInteger::setRate(double newRate)
|
||||||
{
|
{
|
||||||
iRate = (int)(newRate * SCALE + 0.5f);
|
iRate = (int)(newRate * SCALE + 0.5);
|
||||||
TransposerBase::setRate(newRate);
|
TransposerBase::setRate(newRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,7 +186,7 @@ InterpolateLinearFloat::InterpolateLinearFloat() : TransposerBase()
|
||||||
// Notice: use local function calling syntax for sake of clarity,
|
// Notice: use local function calling syntax for sake of clarity,
|
||||||
// to indicate the fact that C++ constructor can't call virtual functions.
|
// to indicate the fact that C++ constructor can't call virtual functions.
|
||||||
resetRegisters();
|
resetRegisters();
|
||||||
setRate(1.0f);
|
setRate(1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -275,12 +271,13 @@ int InterpolateLinearFloat::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *s
|
||||||
i = 0;
|
i = 0;
|
||||||
while (srcCount < srcSampleEnd)
|
while (srcCount < srcSampleEnd)
|
||||||
{
|
{
|
||||||
float temp, vol1;
|
float temp, vol1, fract_float;
|
||||||
|
|
||||||
vol1 = (1.0f- fract);
|
vol1 = (float)(1.0 - fract);
|
||||||
|
fract_float = (float)fract;
|
||||||
for (int c = 0; c < numChannels; c ++)
|
for (int c = 0; c < numChannels; c ++)
|
||||||
{
|
{
|
||||||
temp = vol1 * src[c] + fract * src[c + numChannels];
|
temp = vol1 * src[c] + fract_float * src[c + numChannels];
|
||||||
*dest = (SAMPLETYPE)temp;
|
*dest = (SAMPLETYPE)temp;
|
||||||
dest ++;
|
dest ++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// $Id: InterpolateLinear.h 179 2014-01-06 18:41:42Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -42,15 +38,13 @@
|
||||||
namespace soundtouch
|
namespace soundtouch
|
||||||
{
|
{
|
||||||
|
|
||||||
/// Linear transposer class that uses integer arithmetics
|
/// Linear transposer class that uses integer arithmetic
|
||||||
class InterpolateLinearInteger : public TransposerBase
|
class InterpolateLinearInteger : public TransposerBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
int iFract;
|
int iFract;
|
||||||
int iRate;
|
int iRate;
|
||||||
|
|
||||||
virtual void resetRegisters();
|
|
||||||
|
|
||||||
virtual int transposeMono(SAMPLETYPE *dest,
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples);
|
int &srcSamples);
|
||||||
|
|
@ -63,17 +57,22 @@ public:
|
||||||
|
|
||||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||||
/// rate, larger faster rates.
|
/// rate, larger faster rates.
|
||||||
virtual void setRate(float newRate);
|
virtual void setRate(double newRate);
|
||||||
|
|
||||||
|
virtual void resetRegisters();
|
||||||
|
|
||||||
|
int getLatency() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Linear transposer class that uses floating point arithmetics
|
/// Linear transposer class that uses floating point arithmetic
|
||||||
class InterpolateLinearFloat : public TransposerBase
|
class InterpolateLinearFloat : public TransposerBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
float fract;
|
double fract;
|
||||||
|
|
||||||
virtual void resetRegisters();
|
|
||||||
|
|
||||||
virtual int transposeMono(SAMPLETYPE *dest,
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
|
|
@ -85,6 +84,13 @@ protected:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InterpolateLinearFloat();
|
InterpolateLinearFloat();
|
||||||
|
|
||||||
|
virtual void resetRegisters();
|
||||||
|
|
||||||
|
int getLatency() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// $Id: InterpolateShannon.cpp 195 2014-04-06 15:57:21Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// $Id: InterpolateShannon.h 179 2014-01-06 18:41:42Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -50,7 +46,6 @@ namespace soundtouch
|
||||||
class InterpolateShannon : public TransposerBase
|
class InterpolateShannon : public TransposerBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
void resetRegisters();
|
|
||||||
int transposeMono(SAMPLETYPE *dest,
|
int transposeMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples);
|
int &srcSamples);
|
||||||
|
|
@ -61,10 +56,17 @@ protected:
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples);
|
int &srcSamples);
|
||||||
|
|
||||||
float fract;
|
double fract;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InterpolateShannon();
|
InterpolateShannon();
|
||||||
|
|
||||||
|
void resetRegisters();
|
||||||
|
|
||||||
|
int getLatency() const
|
||||||
|
{
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-04-06 15:57:21 +0000 (Sun, 06 Apr 2014) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: RateTransposer.cpp 195 2014-04-06 15:57:21Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -57,15 +50,21 @@ TransposerBase::ALGORITHM TransposerBase::algorithm = TransposerBase::CUBIC;
|
||||||
// Constructor
|
// Constructor
|
||||||
RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
|
RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
|
||||||
{
|
{
|
||||||
bUseAAFilter = true;
|
bUseAAFilter =
|
||||||
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
|
true;
|
||||||
|
#else
|
||||||
|
// Disable Anti-alias filter if desirable to avoid click at rate change zero value crossover
|
||||||
|
false;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Instantiates the anti-alias filter
|
// Instantiates the anti-alias filter
|
||||||
pAAFilter = new AAFilter(64);
|
pAAFilter = new AAFilter(64);
|
||||||
pTransposer = TransposerBase::newInstance();
|
pTransposer = TransposerBase::newInstance();
|
||||||
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
RateTransposer::~RateTransposer()
|
RateTransposer::~RateTransposer()
|
||||||
{
|
{
|
||||||
delete pAAFilter;
|
delete pAAFilter;
|
||||||
|
|
@ -73,11 +72,14 @@ RateTransposer::~RateTransposer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
||||||
void RateTransposer::enableAAFilter(bool newMode)
|
void RateTransposer::enableAAFilter(bool newMode)
|
||||||
{
|
{
|
||||||
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
|
// Disable Anti-alias filter if desirable to avoid click at rate change zero value crossover
|
||||||
bUseAAFilter = newMode;
|
bUseAAFilter = newMode;
|
||||||
|
clear();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -94,23 +96,22 @@ AAFilter *RateTransposer::getAAFilter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||||
// iRate, larger faster iRates.
|
// iRate, larger faster iRates.
|
||||||
void RateTransposer::setRate(float newRate)
|
void RateTransposer::setRate(double newRate)
|
||||||
{
|
{
|
||||||
double fCutoff;
|
double fCutoff;
|
||||||
|
|
||||||
pTransposer->setRate(newRate);
|
pTransposer->setRate(newRate);
|
||||||
|
|
||||||
// design a new anti-alias filter
|
// design a new anti-alias filter
|
||||||
if (newRate > 1.0f)
|
if (newRate > 1.0)
|
||||||
{
|
{
|
||||||
fCutoff = 0.5f / newRate;
|
fCutoff = 0.5 / newRate;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fCutoff = 0.5f * newRate;
|
fCutoff = 0.5 * newRate;
|
||||||
}
|
}
|
||||||
pAAFilter->setCutoffFreq(fCutoff);
|
pAAFilter->setCutoffFreq(fCutoff);
|
||||||
}
|
}
|
||||||
|
|
@ -177,11 +178,10 @@ void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
||||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void RateTransposer::setChannels(int nChannels)
|
void RateTransposer::setChannels(int nChannels)
|
||||||
{
|
{
|
||||||
assert(nChannels > 0);
|
if (!verifyNumberOfChannels(nChannels) ||
|
||||||
|
(pTransposer->numChannels == nChannels)) return;
|
||||||
|
|
||||||
if (pTransposer->numChannels == nChannels) return;
|
|
||||||
pTransposer->setChannels(nChannels);
|
pTransposer->setChannels(nChannels);
|
||||||
|
|
||||||
inputBuffer.setChannels(nChannels);
|
inputBuffer.setChannels(nChannels);
|
||||||
midBuffer.setChannels(nChannels);
|
midBuffer.setChannels(nChannels);
|
||||||
outputBuffer.setChannels(nChannels);
|
outputBuffer.setChannels(nChannels);
|
||||||
|
|
@ -194,6 +194,11 @@ void RateTransposer::clear()
|
||||||
outputBuffer.clear();
|
outputBuffer.clear();
|
||||||
midBuffer.clear();
|
midBuffer.clear();
|
||||||
inputBuffer.clear();
|
inputBuffer.clear();
|
||||||
|
pTransposer->resetRegisters();
|
||||||
|
|
||||||
|
// prefill buffer to avoid losing first samples at beginning of stream
|
||||||
|
int prefill = getLatency();
|
||||||
|
inputBuffer.addSilent(prefill);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -208,6 +213,14 @@ int RateTransposer::isEmpty() const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Return approximate initial input-output latency
|
||||||
|
int RateTransposer::getLatency() const
|
||||||
|
{
|
||||||
|
return pTransposer->getLatency() +
|
||||||
|
((bUseAAFilter) ? (pAAFilter->getLength() / 2) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// TransposerBase - Base class for interpolation
|
// TransposerBase - Base class for interpolation
|
||||||
|
|
@ -225,7 +238,7 @@ void TransposerBase::setAlgorithm(TransposerBase::ALGORITHM a)
|
||||||
int TransposerBase::transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src)
|
int TransposerBase::transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src)
|
||||||
{
|
{
|
||||||
int numSrcSamples = src.numSamples();
|
int numSrcSamples = src.numSamples();
|
||||||
int sizeDemand = (int)((float)numSrcSamples / rate) + 8;
|
int sizeDemand = (int)((double)numSrcSamples / rate) + 8;
|
||||||
int numOutput;
|
int numOutput;
|
||||||
SAMPLETYPE *psrc = src.ptrBegin();
|
SAMPLETYPE *psrc = src.ptrBegin();
|
||||||
SAMPLETYPE *pdest = dest.ptrEnd(sizeDemand);
|
SAMPLETYPE *pdest = dest.ptrEnd(sizeDemand);
|
||||||
|
|
@ -270,7 +283,7 @@ void TransposerBase::setChannels(int channels)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TransposerBase::setRate(float newRate)
|
void TransposerBase::setRate(double newRate)
|
||||||
{
|
{
|
||||||
rate = newRate;
|
rate = newRate;
|
||||||
}
|
}
|
||||||
|
|
@ -280,7 +293,7 @@ void TransposerBase::setRate(float newRate)
|
||||||
TransposerBase *TransposerBase::newInstance()
|
TransposerBase *TransposerBase::newInstance()
|
||||||
{
|
{
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
// Notice: For integer arithmetics support only linear algorithm (due to simplest calculus)
|
// Notice: For integer arithmetic support only linear algorithm (due to simplest calculus)
|
||||||
return ::new InterpolateLinearInteger;
|
return ::new InterpolateLinearInteger;
|
||||||
#else
|
#else
|
||||||
switch (algorithm)
|
switch (algorithm)
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-04-06 15:57:21 +0000 (Sun, 06 Apr 2014) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: RateTransposer.h 195 2014-04-06 15:57:21Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -66,8 +59,6 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void resetRegisters() = 0;
|
|
||||||
|
|
||||||
virtual int transposeMono(SAMPLETYPE *dest,
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples) = 0;
|
int &srcSamples) = 0;
|
||||||
|
|
@ -81,15 +72,18 @@ protected:
|
||||||
static ALGORITHM algorithm;
|
static ALGORITHM algorithm;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
float rate;
|
double rate;
|
||||||
int numChannels;
|
int numChannels;
|
||||||
|
|
||||||
TransposerBase();
|
TransposerBase();
|
||||||
virtual ~TransposerBase();
|
virtual ~TransposerBase();
|
||||||
|
|
||||||
virtual int transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src);
|
virtual int transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src);
|
||||||
virtual void setRate(float newRate);
|
virtual void setRate(double newRate);
|
||||||
virtual void setChannels(int channels);
|
virtual void setChannels(int channels);
|
||||||
|
virtual int getLatency() const = 0;
|
||||||
|
|
||||||
|
virtual void resetRegisters() = 0;
|
||||||
|
|
||||||
// static factory function
|
// static factory function
|
||||||
static TransposerBase *newInstance();
|
static TransposerBase *newInstance();
|
||||||
|
|
@ -132,21 +126,9 @@ public:
|
||||||
RateTransposer();
|
RateTransposer();
|
||||||
virtual ~RateTransposer();
|
virtual ~RateTransposer();
|
||||||
|
|
||||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
|
||||||
/// depending on if we're to use integer or floating point arithmetics.
|
|
||||||
// static void *operator new(size_t s);
|
|
||||||
|
|
||||||
/// Use this function instead of "new" operator to create a new instance of this class.
|
|
||||||
/// This function automatically chooses a correct implementation, depending on if
|
|
||||||
/// integer ot floating point arithmetics are to be used.
|
|
||||||
// static RateTransposer *newInstance();
|
|
||||||
|
|
||||||
/// Returns the output buffer object
|
/// Returns the output buffer object
|
||||||
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
||||||
|
|
||||||
/// Returns the store buffer object
|
|
||||||
// FIFOSamplePipe *getStore() { return &storeBuffer; };
|
|
||||||
|
|
||||||
/// Return anti-alias filter object
|
/// Return anti-alias filter object
|
||||||
AAFilter *getAAFilter();
|
AAFilter *getAAFilter();
|
||||||
|
|
||||||
|
|
@ -158,7 +140,7 @@ public:
|
||||||
|
|
||||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||||
/// rate, larger faster rates.
|
/// rate, larger faster rates.
|
||||||
virtual void setRate(float newRate);
|
virtual void setRate(double newRate);
|
||||||
|
|
||||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void setChannels(int channels);
|
void setChannels(int channels);
|
||||||
|
|
@ -172,6 +154,9 @@ public:
|
||||||
|
|
||||||
/// Returns nonzero if there aren't any samples available for outputting.
|
/// Returns nonzero if there aren't any samples available for outputting.
|
||||||
int isEmpty() const;
|
int isEmpty() const;
|
||||||
|
|
||||||
|
/// Return approximate initial input-output latency
|
||||||
|
int getLatency() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-05-18 15:25:07 +0000 (Mon, 18 May 2015) $
|
|
||||||
// File revision : $Revision: 3 $
|
|
||||||
//
|
|
||||||
// $Id: STTypes.h 215 2015-05-18 15:25:07Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -57,13 +50,20 @@ typedef unsigned long ulong;
|
||||||
#include "soundtouch_config.h"
|
#include "soundtouch_config.h"
|
||||||
|
|
||||||
#if defined(WIN32)
|
#if defined(WIN32)
|
||||||
#define EXPORT __declspec(dllexport)
|
#if defined(BUILDING_SOUNDTOUCH)
|
||||||
|
#define SOUNDTOUCH_API __declspec(dllexport)
|
||||||
#else
|
#else
|
||||||
#define EXPORT
|
#define SOUNDTOUCH_API __declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define SOUNDTOUCH_API
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace soundtouch
|
namespace soundtouch
|
||||||
{
|
{
|
||||||
|
/// Max allowed number of channels
|
||||||
|
#define SOUNDTOUCH_MAX_CHANNELS 16
|
||||||
|
|
||||||
/// Activate these undef's to overrule the possible sampletype
|
/// Activate these undef's to overrule the possible sampletype
|
||||||
/// setting inherited from some other header file:
|
/// setting inherited from some other header file:
|
||||||
//#undef SOUNDTOUCH_INTEGER_SAMPLES
|
//#undef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
|
@ -126,10 +126,10 @@ namespace soundtouch
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// If defined, allows the SIMD-optimized routines to take minor shortcuts
|
// If defined, allows the SIMD-optimized routines to skip unevenly aligned
|
||||||
// for improved performance. Undefine to require faithfully similar SIMD
|
// memory offsets that can cause performance penalty in some SIMD implementations.
|
||||||
// calculations as in normal C implementation.
|
// Causes slight compromise in sound quality.
|
||||||
#define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
|
// #define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
|
||||||
|
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
|
@ -144,16 +144,19 @@ namespace soundtouch
|
||||||
#endif // SOUNDTOUCH_FLOAT_SAMPLES
|
#endif // SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
||||||
// Allow MMX optimizations
|
// Allow MMX optimizations (not available in X64 mode)
|
||||||
#define SOUNDTOUCH_ALLOW_MMX 1
|
#if (!_M_X64)
|
||||||
|
#define SOUNDTOUCH_ALLOW_MMX 1
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
// floating point samples
|
// floating point samples
|
||||||
typedef float SAMPLETYPE;
|
typedef float SAMPLETYPE;
|
||||||
// data type for sample accumulation: Use double to utilize full precision.
|
// data type for sample accumulation: Use float also here to enable
|
||||||
typedef double LONG_SAMPLETYPE;
|
// efficient autovectorization
|
||||||
|
typedef float LONG_SAMPLETYPE;
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
||||||
// Allow SSE optimizations
|
// Allow SSE optimizations
|
||||||
|
|
@ -162,10 +165,16 @@ namespace soundtouch
|
||||||
|
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
|
||||||
};
|
#if ((SOUNDTOUCH_ALLOW_SSE) || (__SSE__) || (SOUNDTOUCH_USE_NEON))
|
||||||
|
#if SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION
|
||||||
|
#define ST_SIMD_AVOID_UNALIGNED
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// define ST_NO_EXCEPTION_HANDLING switch to disable throwing std exceptions:
|
// define ST_NO_EXCEPTION_HANDLING switch to disable throwing std exceptions:
|
||||||
// #define ST_NO_EXCEPTION_HANDLING 1
|
#define ST_NO_EXCEPTION_HANDLING 1
|
||||||
#ifdef ST_NO_EXCEPTION_HANDLING
|
#ifdef ST_NO_EXCEPTION_HANDLING
|
||||||
// Exceptions disabled. Throw asserts instead if enabled.
|
// Exceptions disabled. Throw asserts instead if enabled.
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
|
||||||
|
|
@ -41,13 +41,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-10-08 15:26:57 +0000 (Wed, 08 Oct 2014) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: SoundTouch.cpp 201 2014-10-08 15:26:57Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -110,12 +103,14 @@ SoundTouch::SoundTouch()
|
||||||
|
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
|
|
||||||
|
samplesExpectedOut = 0;
|
||||||
|
samplesOutput = 0;
|
||||||
|
|
||||||
channels = 0;
|
channels = 0;
|
||||||
bSrateSet = false;
|
bSrateSet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SoundTouch::~SoundTouch()
|
SoundTouch::~SoundTouch()
|
||||||
{
|
{
|
||||||
delete pRateTransposer;
|
delete pRateTransposer;
|
||||||
|
|
@ -123,7 +118,6 @@ SoundTouch::~SoundTouch()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Get SoundTouch library version string
|
/// Get SoundTouch library version string
|
||||||
const char *SoundTouch::getVersionString()
|
const char *SoundTouch::getVersionString()
|
||||||
{
|
{
|
||||||
|
|
@ -143,90 +137,79 @@ uint SoundTouch::getVersionId()
|
||||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void SoundTouch::setChannels(uint numChannels)
|
void SoundTouch::setChannels(uint numChannels)
|
||||||
{
|
{
|
||||||
/*if (numChannels != 1 && numChannels != 2)
|
if (!verifyNumberOfChannels(numChannels)) return;
|
||||||
{
|
|
||||||
//ST_THROW_RT_ERROR("Illegal number of channels");
|
|
||||||
return;
|
|
||||||
}*/
|
|
||||||
channels = numChannels;
|
channels = numChannels;
|
||||||
pRateTransposer->setChannels((int)numChannels);
|
pRateTransposer->setChannels((int)numChannels);
|
||||||
pTDStretch->setChannels((int)numChannels);
|
pTDStretch->setChannels((int)numChannels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new rate control value. Normal rate = 1.0, smaller values
|
// Sets new rate control value. Normal rate = 1.0, smaller values
|
||||||
// represent slower rate, larger faster rates.
|
// represent slower rate, larger faster rates.
|
||||||
void SoundTouch::setRate(float newRate)
|
void SoundTouch::setRate(double newRate)
|
||||||
{
|
{
|
||||||
virtualRate = newRate;
|
virtualRate = newRate;
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new rate control value as a difference in percents compared
|
// Sets new rate control value as a difference in percents compared
|
||||||
// to the original rate (-50 .. +100 %)
|
// to the original rate (-50 .. +100 %)
|
||||||
void SoundTouch::setRateChange(float newRate)
|
void SoundTouch::setRateChange(double newRate)
|
||||||
{
|
{
|
||||||
virtualRate = 1.0f + 0.01f * newRate;
|
virtualRate = 1.0 + 0.01 * newRate;
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
||||||
// represent slower tempo, larger faster tempo.
|
// represent slower tempo, larger faster tempo.
|
||||||
void SoundTouch::setTempo(float newTempo)
|
void SoundTouch::setTempo(double newTempo)
|
||||||
{
|
{
|
||||||
virtualTempo = newTempo;
|
virtualTempo = newTempo;
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new tempo control value as a difference in percents compared
|
// Sets new tempo control value as a difference in percents compared
|
||||||
// to the original tempo (-50 .. +100 %)
|
// to the original tempo (-50 .. +100 %)
|
||||||
void SoundTouch::setTempoChange(float newTempo)
|
void SoundTouch::setTempoChange(double newTempo)
|
||||||
{
|
{
|
||||||
virtualTempo = 1.0f + 0.01f * newTempo;
|
virtualTempo = 1.0 + 0.01 * newTempo;
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new pitch control value. Original pitch = 1.0, smaller values
|
// Sets new pitch control value. Original pitch = 1.0, smaller values
|
||||||
// represent lower pitches, larger values higher pitch.
|
// represent lower pitches, larger values higher pitch.
|
||||||
void SoundTouch::setPitch(float newPitch)
|
void SoundTouch::setPitch(double newPitch)
|
||||||
{
|
{
|
||||||
virtualPitch = newPitch;
|
virtualPitch = newPitch;
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets pitch change in octaves compared to the original pitch
|
// Sets pitch change in octaves compared to the original pitch
|
||||||
// (-1.00 .. +1.00)
|
// (-1.00 .. +1.00)
|
||||||
void SoundTouch::setPitchOctaves(float newPitch)
|
void SoundTouch::setPitchOctaves(double newPitch)
|
||||||
{
|
{
|
||||||
virtualPitch = (float)exp(0.69314718056f * newPitch);
|
virtualPitch = exp(0.69314718056 * newPitch);
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets pitch change in semi-tones compared to the original pitch
|
// Sets pitch change in semi-tones compared to the original pitch
|
||||||
// (-12 .. +12)
|
// (-12 .. +12)
|
||||||
void SoundTouch::setPitchSemiTones(int newPitch)
|
void SoundTouch::setPitchSemiTones(int newPitch)
|
||||||
{
|
{
|
||||||
setPitchOctaves((float)newPitch / 12.0f);
|
setPitchOctaves((double)newPitch / 12.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SoundTouch::setPitchSemiTones(double newPitch)
|
||||||
void SoundTouch::setPitchSemiTones(float newPitch)
|
|
||||||
{
|
{
|
||||||
setPitchOctaves(newPitch / 12.0f);
|
setPitchOctaves(newPitch / 12.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -234,8 +217,8 @@ void SoundTouch::setPitchSemiTones(float newPitch)
|
||||||
// nominal control values.
|
// nominal control values.
|
||||||
void SoundTouch::calcEffectiveRateAndTempo()
|
void SoundTouch::calcEffectiveRateAndTempo()
|
||||||
{
|
{
|
||||||
float oldTempo = tempo;
|
double oldTempo = tempo;
|
||||||
float oldRate = rate;
|
double oldRate = rate;
|
||||||
|
|
||||||
tempo = virtualTempo / virtualPitch;
|
tempo = virtualTempo / virtualPitch;
|
||||||
rate = virtualPitch * virtualRate;
|
rate = virtualPitch * virtualRate;
|
||||||
|
|
@ -302,23 +285,12 @@ void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||||
ST_THROW_RT_ERROR("SoundTouch : Number of channels not defined");
|
ST_THROW_RT_ERROR("SoundTouch : Number of channels not defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transpose the rate of the new samples if necessary
|
// accumulate how many samples are expected out from processing, given the current
|
||||||
/* Bypass the nominal setting - can introduce a click in sound when tempo/pitch control crosses the nominal value...
|
// processing setting
|
||||||
if (rate == 1.0f)
|
samplesExpectedOut += (double)nSamples / ((double)rate * (double)tempo);
|
||||||
{
|
|
||||||
// The rate value is same as the original, simply evaluate the tempo changer.
|
|
||||||
assert(output == pTDStretch);
|
|
||||||
if (pRateTransposer->isEmpty() == 0)
|
|
||||||
{
|
|
||||||
// yet flush the last samples in the pitch transposer buffer
|
|
||||||
// (may happen if 'rate' changes from a non-zero value to zero)
|
|
||||||
pTDStretch->moveSamples(*pRateTransposer);
|
|
||||||
}
|
|
||||||
pTDStretch->putSamples(samples, nSamples);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
else if (rate <= 1.0f)
|
if (rate <= 1.0f)
|
||||||
{
|
{
|
||||||
// transpose the rate down, output the transposed sound to tempo changer buffer
|
// transpose the rate down, output the transposed sound to tempo changer buffer
|
||||||
assert(output == pTDStretch);
|
assert(output == pTDStretch);
|
||||||
|
|
@ -346,44 +318,30 @@ void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||||
void SoundTouch::flush()
|
void SoundTouch::flush()
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int nUnprocessed;
|
int numStillExpected;
|
||||||
int nOut;
|
SAMPLETYPE *buff = new SAMPLETYPE[128 * channels];
|
||||||
SAMPLETYPE *buff = new SAMPLETYPE[64 * channels];
|
|
||||||
|
|
||||||
// check how many samples still await processing, and scale
|
// how many samples are still expected to output
|
||||||
// that by tempo & rate to get expected output sample count
|
numStillExpected = (int)((long)(samplesExpectedOut + 0.5) - samplesOutput);
|
||||||
nUnprocessed = numUnprocessedSamples();
|
if (numStillExpected < 0) numStillExpected = 0;
|
||||||
nUnprocessed = (int)((double)nUnprocessed / (tempo * rate) + 0.5);
|
|
||||||
|
|
||||||
nOut = numSamples(); // ready samples currently in buffer ...
|
memset(buff, 0, 128 * channels * sizeof(SAMPLETYPE));
|
||||||
nOut += nUnprocessed; // ... and how many we expect there to be in the end
|
|
||||||
|
|
||||||
memset(buff, 0, 64 * channels * sizeof(SAMPLETYPE));
|
|
||||||
// "Push" the last active samples out from the processing pipeline by
|
// "Push" the last active samples out from the processing pipeline by
|
||||||
// feeding blank samples into the processing pipeline until new,
|
// feeding blank samples into the processing pipeline until new,
|
||||||
// processed samples appear in the output (not however, more than
|
// processed samples appear in the output (not however, more than
|
||||||
// 8ksamples in any case)
|
// 24ksamples in any case)
|
||||||
for (i = 0; i < 128; i ++)
|
for (i = 0; (numStillExpected > (int)numSamples()) && (i < 200); i ++)
|
||||||
{
|
{
|
||||||
putSamples(buff, 64);
|
putSamples(buff, 128);
|
||||||
if ((int)numSamples() >= nOut)
|
|
||||||
{
|
|
||||||
// Enough new samples have appeared into the output!
|
|
||||||
// As samples come from processing with bigger chunks, now truncate it
|
|
||||||
// back to maximum "nOut" samples to improve duration accuracy
|
|
||||||
adjustAmountOfSamples(nOut);
|
|
||||||
|
|
||||||
// finish
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adjustAmountOfSamples(numStillExpected);
|
||||||
|
|
||||||
delete[] buff;
|
delete[] buff;
|
||||||
|
|
||||||
// Clear working buffers
|
// Clear input buffers
|
||||||
pRateTransposer->clear();
|
|
||||||
pTDStretch->clearInput();
|
pTDStretch->clearInput();
|
||||||
// yet leave the 'tempoChanger' output intouched as that's where the
|
// yet leave the output intouched as that's where the
|
||||||
// flushed samples are!
|
// flushed samples are!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -452,7 +410,7 @@ int SoundTouch::getSetting(int settingId) const
|
||||||
return pRateTransposer->getAAFilter()->getLength();
|
return pRateTransposer->getAAFilter()->getLength();
|
||||||
|
|
||||||
case SETTING_USE_QUICKSEEK :
|
case SETTING_USE_QUICKSEEK :
|
||||||
return (uint) pTDStretch->isQuickSeekEnabled();
|
return (uint)pTDStretch->isQuickSeekEnabled();
|
||||||
|
|
||||||
case SETTING_SEQUENCE_MS:
|
case SETTING_SEQUENCE_MS:
|
||||||
pTDStretch->getParameters(NULL, &temp, NULL, NULL);
|
pTDStretch->getParameters(NULL, &temp, NULL, NULL);
|
||||||
|
|
@ -466,13 +424,53 @@ int SoundTouch::getSetting(int settingId) const
|
||||||
pTDStretch->getParameters(NULL, NULL, NULL, &temp);
|
pTDStretch->getParameters(NULL, NULL, NULL, &temp);
|
||||||
return temp;
|
return temp;
|
||||||
|
|
||||||
case SETTING_NOMINAL_INPUT_SEQUENCE :
|
case SETTING_NOMINAL_INPUT_SEQUENCE :
|
||||||
return pTDStretch->getInputSampleReq();
|
{
|
||||||
|
int size = pTDStretch->getInputSampleReq();
|
||||||
|
|
||||||
case SETTING_NOMINAL_OUTPUT_SEQUENCE :
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
return pTDStretch->getOutputBatchSize();
|
if (rate <= 1.0)
|
||||||
|
{
|
||||||
|
// transposing done before timestretch, which impacts latency
|
||||||
|
return (int)(size * rate + 0.5);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
default :
|
case SETTING_NOMINAL_OUTPUT_SEQUENCE :
|
||||||
|
{
|
||||||
|
int size = pTDStretch->getOutputBatchSize();
|
||||||
|
|
||||||
|
if (rate > 1.0)
|
||||||
|
{
|
||||||
|
// transposing done after timestretch, which impacts latency
|
||||||
|
return (int)(size / rate + 0.5);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SETTING_INITIAL_LATENCY:
|
||||||
|
{
|
||||||
|
double latency = pTDStretch->getLatency();
|
||||||
|
int latency_tr = pRateTransposer->getLatency();
|
||||||
|
|
||||||
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
|
if (rate <= 1.0)
|
||||||
|
{
|
||||||
|
// transposing done before timestretch, which impacts latency
|
||||||
|
latency = (latency + latency_tr) * rate;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
latency += (double)latency_tr / rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)(latency + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
default :
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -482,12 +480,13 @@ int SoundTouch::getSetting(int settingId) const
|
||||||
// buffers.
|
// buffers.
|
||||||
void SoundTouch::clear()
|
void SoundTouch::clear()
|
||||||
{
|
{
|
||||||
|
samplesExpectedOut = 0;
|
||||||
|
samplesOutput = 0;
|
||||||
pRateTransposer->clear();
|
pRateTransposer->clear();
|
||||||
pTDStretch->clear();
|
pTDStretch->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Returns number of samples currently unprocessed.
|
/// Returns number of samples currently unprocessed.
|
||||||
uint SoundTouch::numUnprocessedSamples() const
|
uint SoundTouch::numUnprocessedSamples() const
|
||||||
{
|
{
|
||||||
|
|
@ -502,3 +501,38 @@ uint SoundTouch::numUnprocessedSamples() const
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||||
|
/// output buffer and removes them from the sample buffer. If there are less than
|
||||||
|
/// 'numsample' samples in the buffer, returns all that available.
|
||||||
|
///
|
||||||
|
/// \return Number of samples returned.
|
||||||
|
uint SoundTouch::receiveSamples(SAMPLETYPE *output, uint maxSamples)
|
||||||
|
{
|
||||||
|
uint ret = FIFOProcessor::receiveSamples(output, maxSamples);
|
||||||
|
samplesOutput += (long)ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
|
/// sample buffer without copying them anywhere.
|
||||||
|
///
|
||||||
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
|
/// with 'ptrBegin' function.
|
||||||
|
uint SoundTouch::receiveSamples(uint maxSamples)
|
||||||
|
{
|
||||||
|
uint ret = FIFOProcessor::receiveSamples(maxSamples);
|
||||||
|
samplesOutput += (long)ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Get ratio between input and output audio durations, useful for calculating
|
||||||
|
/// processed output duration: if you'll process a stream of N samples, then
|
||||||
|
/// you can expect to get out N * getInputOutputSampleRatio() samples.
|
||||||
|
double SoundTouch::getInputOutputSampleRatio()
|
||||||
|
{
|
||||||
|
return 1.0 / (tempo * rate);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,13 +41,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-05-18 15:28:41 +0000 (Mon, 18 May 2015) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: SoundTouch.h 216 2015-05-18 15:28:41Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -79,10 +72,10 @@ namespace soundtouch
|
||||||
{
|
{
|
||||||
|
|
||||||
/// Soundtouch library version string
|
/// Soundtouch library version string
|
||||||
#define SOUNDTOUCH_VERSION "1.9.0"
|
#define SOUNDTOUCH_VERSION "2.3.1"
|
||||||
|
|
||||||
/// SoundTouch library version id
|
/// SoundTouch library version id
|
||||||
#define SOUNDTOUCH_VERSION_ID (10900)
|
#define SOUNDTOUCH_VERSION_ID (20301)
|
||||||
|
|
||||||
//
|
//
|
||||||
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
||||||
|
|
@ -116,17 +109,19 @@ namespace soundtouch
|
||||||
#define SETTING_OVERLAP_MS 5
|
#define SETTING_OVERLAP_MS 5
|
||||||
|
|
||||||
|
|
||||||
/// Call "getSetting" with this ID to query nominal average processing sequence
|
/// Call "getSetting" with this ID to query processing sequence size in samples.
|
||||||
/// size in samples. This value tells approcimate value how many input samples
|
/// This value gives approximate value of how many input samples you'll need to
|
||||||
/// SoundTouch needs to gather before it does DSP processing run for the sample batch.
|
/// feed into SoundTouch after initial buffering to get out a new batch of
|
||||||
|
/// output samples.
|
||||||
|
///
|
||||||
|
/// This value does not include initial buffering at beginning of a new processing
|
||||||
|
/// stream, use SETTING_INITIAL_LATENCY to get the initial buffering size.
|
||||||
///
|
///
|
||||||
/// Notices:
|
/// Notices:
|
||||||
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||||
/// - Returned value is approximate average value, exact processing batch
|
/// - This parameter value is not constant but change depending on
|
||||||
/// size may wary from time to time
|
|
||||||
/// - This parameter value is not constant but may change depending on
|
|
||||||
/// tempo/pitch/rate/samplerate settings.
|
/// tempo/pitch/rate/samplerate settings.
|
||||||
#define SETTING_NOMINAL_INPUT_SEQUENCE 6
|
#define SETTING_NOMINAL_INPUT_SEQUENCE 6
|
||||||
|
|
||||||
|
|
||||||
/// Call "getSetting" with this ID to query nominal average processing output
|
/// Call "getSetting" with this ID to query nominal average processing output
|
||||||
|
|
@ -135,13 +130,42 @@ namespace soundtouch
|
||||||
///
|
///
|
||||||
/// Notices:
|
/// Notices:
|
||||||
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||||
/// - Returned value is approximate average value, exact processing batch
|
/// - This parameter value is not constant but change depending on
|
||||||
/// size may wary from time to time
|
|
||||||
/// - This parameter value is not constant but may change depending on
|
|
||||||
/// tempo/pitch/rate/samplerate settings.
|
/// tempo/pitch/rate/samplerate settings.
|
||||||
#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7
|
#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7
|
||||||
|
|
||||||
class EXPORT SoundTouch : public FIFOProcessor
|
|
||||||
|
/// Call "getSetting" with this ID to query initial processing latency, i.e.
|
||||||
|
/// approx. how many samples you'll need to enter to SoundTouch pipeline before
|
||||||
|
/// you can expect to get first batch of ready output samples out.
|
||||||
|
///
|
||||||
|
/// After the first output batch, you can then expect to get approx.
|
||||||
|
/// SETTING_NOMINAL_OUTPUT_SEQUENCE ready samples out for every
|
||||||
|
/// SETTING_NOMINAL_INPUT_SEQUENCE samples that you enter into SoundTouch.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// processing with parameter -tempo=5
|
||||||
|
/// => initial latency = 5509 samples
|
||||||
|
/// input sequence = 4167 samples
|
||||||
|
/// output sequence = 3969 samples
|
||||||
|
///
|
||||||
|
/// Accordingly, you can expect to feed in approx. 5509 samples at beginning of
|
||||||
|
/// the stream, and then you'll get out the first 3969 samples. After that, for
|
||||||
|
/// every approx. 4167 samples that you'll put in, you'll receive again approx.
|
||||||
|
/// 3969 samples out.
|
||||||
|
///
|
||||||
|
/// This also means that average latency during stream processing is
|
||||||
|
/// INITIAL_LATENCY-OUTPUT_SEQUENCE/2, in the above example case 5509-3969/2
|
||||||
|
/// = 3524 samples
|
||||||
|
///
|
||||||
|
/// Notices:
|
||||||
|
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||||
|
/// - This parameter value is not constant but change depending on
|
||||||
|
/// tempo/pitch/rate/samplerate settings.
|
||||||
|
#define SETTING_INITIAL_LATENCY 8
|
||||||
|
|
||||||
|
|
||||||
|
class SOUNDTOUCH_API SoundTouch : public FIFOProcessor
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
/// Rate transposer class instance
|
/// Rate transposer class instance
|
||||||
|
|
@ -151,17 +175,24 @@ private:
|
||||||
class TDStretch *pTDStretch;
|
class TDStretch *pTDStretch;
|
||||||
|
|
||||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||||
float virtualRate;
|
double virtualRate;
|
||||||
|
|
||||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||||
float virtualTempo;
|
double virtualTempo;
|
||||||
|
|
||||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||||
float virtualPitch;
|
double virtualPitch;
|
||||||
|
|
||||||
/// Flag: Has sample rate been set?
|
/// Flag: Has sample rate been set?
|
||||||
bool bSrateSet;
|
bool bSrateSet;
|
||||||
|
|
||||||
|
/// Accumulator for how many samples in total will be expected as output vs. samples put in,
|
||||||
|
/// considering current processing settings.
|
||||||
|
double samplesExpectedOut;
|
||||||
|
|
||||||
|
/// Accumulator for how many samples in total have been read out from the processing so far
|
||||||
|
long samplesOutput;
|
||||||
|
|
||||||
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
|
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
|
||||||
/// 'virtualPitch' parameters.
|
/// 'virtualPitch' parameters.
|
||||||
void calcEffectiveRateAndTempo();
|
void calcEffectiveRateAndTempo();
|
||||||
|
|
@ -171,10 +202,10 @@ protected :
|
||||||
uint channels;
|
uint channels;
|
||||||
|
|
||||||
/// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
|
/// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
|
||||||
float rate;
|
double rate;
|
||||||
|
|
||||||
/// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
|
/// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
|
||||||
float tempo;
|
double tempo;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SoundTouch();
|
SoundTouch();
|
||||||
|
|
@ -188,32 +219,32 @@ public:
|
||||||
|
|
||||||
/// Sets new rate control value. Normal rate = 1.0, smaller values
|
/// Sets new rate control value. Normal rate = 1.0, smaller values
|
||||||
/// represent slower rate, larger faster rates.
|
/// represent slower rate, larger faster rates.
|
||||||
void setRate(float newRate);
|
void setRate(double newRate);
|
||||||
|
|
||||||
/// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
/// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
||||||
/// represent slower tempo, larger faster tempo.
|
/// represent slower tempo, larger faster tempo.
|
||||||
void setTempo(float newTempo);
|
void setTempo(double newTempo);
|
||||||
|
|
||||||
/// Sets new rate control value as a difference in percents compared
|
/// Sets new rate control value as a difference in percents compared
|
||||||
/// to the original rate (-50 .. +100 %)
|
/// to the original rate (-50 .. +100 %)
|
||||||
void setRateChange(float newRate);
|
void setRateChange(double newRate);
|
||||||
|
|
||||||
/// Sets new tempo control value as a difference in percents compared
|
/// Sets new tempo control value as a difference in percents compared
|
||||||
/// to the original tempo (-50 .. +100 %)
|
/// to the original tempo (-50 .. +100 %)
|
||||||
void setTempoChange(float newTempo);
|
void setTempoChange(double newTempo);
|
||||||
|
|
||||||
/// Sets new pitch control value. Original pitch = 1.0, smaller values
|
/// Sets new pitch control value. Original pitch = 1.0, smaller values
|
||||||
/// represent lower pitches, larger values higher pitch.
|
/// represent lower pitches, larger values higher pitch.
|
||||||
void setPitch(float newPitch);
|
void setPitch(double newPitch);
|
||||||
|
|
||||||
/// Sets pitch change in octaves compared to the original pitch
|
/// Sets pitch change in octaves compared to the original pitch
|
||||||
/// (-1.00 .. +1.00)
|
/// (-1.00 .. +1.00)
|
||||||
void setPitchOctaves(float newPitch);
|
void setPitchOctaves(double newPitch);
|
||||||
|
|
||||||
/// Sets pitch change in semi-tones compared to the original pitch
|
/// Sets pitch change in semi-tones compared to the original pitch
|
||||||
/// (-12 .. +12)
|
/// (-12 .. +12)
|
||||||
void setPitchSemiTones(int newPitch);
|
void setPitchSemiTones(int newPitch);
|
||||||
void setPitchSemiTones(float newPitch);
|
void setPitchSemiTones(double newPitch);
|
||||||
|
|
||||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void setChannels(uint numChannels);
|
void setChannels(uint numChannels);
|
||||||
|
|
@ -221,6 +252,24 @@ public:
|
||||||
/// Sets sample rate.
|
/// Sets sample rate.
|
||||||
void setSampleRate(uint srate);
|
void setSampleRate(uint srate);
|
||||||
|
|
||||||
|
/// Get ratio between input and output audio durations, useful for calculating
|
||||||
|
/// processed output duration: if you'll process a stream of N samples, then
|
||||||
|
/// you can expect to get out N * getInputOutputSampleRatio() samples.
|
||||||
|
///
|
||||||
|
/// This ratio will give accurate target duration ratio for a full audio track,
|
||||||
|
/// given that the the whole track is processed with same processing parameters.
|
||||||
|
///
|
||||||
|
/// If this ratio is applied to calculate intermediate offsets inside a processing
|
||||||
|
/// stream, then this ratio is approximate and can deviate +- some tens of milliseconds
|
||||||
|
/// from ideal offset, yet by end of the audio stream the duration ratio will become
|
||||||
|
/// exact.
|
||||||
|
///
|
||||||
|
/// Example: if processing with parameters "-tempo=15 -pitch=-3", the function
|
||||||
|
/// will return value 0.8695652... Now, if processing an audio stream whose duration
|
||||||
|
/// is exactly one million audio samples, then you can expect the processed
|
||||||
|
/// output duration be 0.869565 * 1000000 = 869565 samples.
|
||||||
|
double getInputOutputSampleRatio();
|
||||||
|
|
||||||
/// Flushes the last samples from the processing pipeline to the output.
|
/// Flushes the last samples from the processing pipeline to the output.
|
||||||
/// Clears also the internal processing buffers.
|
/// Clears also the internal processing buffers.
|
||||||
//
|
//
|
||||||
|
|
@ -240,6 +289,23 @@ public:
|
||||||
///< contains data for both channels.
|
///< contains data for both channels.
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||||
|
/// output buffer and removes them from the sample buffer. If there are less than
|
||||||
|
/// 'numsample' samples in the buffer, returns all that available.
|
||||||
|
///
|
||||||
|
/// \return Number of samples returned.
|
||||||
|
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
|
||||||
|
uint maxSamples ///< How many samples to receive at max.
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
|
/// sample buffer without copying them anywhere.
|
||||||
|
///
|
||||||
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
|
/// with 'ptrBegin' function.
|
||||||
|
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
||||||
|
);
|
||||||
|
|
||||||
/// Clears all the samples in the object's output and internal processing
|
/// Clears all the samples in the object's output and internal processing
|
||||||
/// buffers.
|
/// buffers.
|
||||||
virtual void clear();
|
virtual void clear();
|
||||||
|
|
@ -247,7 +313,7 @@ public:
|
||||||
/// Changes a setting controlling the processing system behaviour. See the
|
/// Changes a setting controlling the processing system behaviour. See the
|
||||||
/// 'SETTING_...' defines for available setting ID's.
|
/// 'SETTING_...' defines for available setting ID's.
|
||||||
///
|
///
|
||||||
/// \return 'true' if the setting was succesfully changed
|
/// \return 'true' if the setting was successfully changed
|
||||||
bool setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
|
bool setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
|
||||||
int value ///< New setting value.
|
int value ///< New setting value.
|
||||||
);
|
);
|
||||||
|
|
@ -262,6 +328,11 @@ public:
|
||||||
/// Returns number of samples currently unprocessed.
|
/// Returns number of samples currently unprocessed.
|
||||||
virtual uint numUnprocessedSamples() const;
|
virtual uint numUnprocessedSamples() const;
|
||||||
|
|
||||||
|
/// Return number of channels
|
||||||
|
uint numChannels() const
|
||||||
|
{
|
||||||
|
return channels;
|
||||||
|
}
|
||||||
|
|
||||||
/// Other handy functions that are implemented in the ancestor classes (see
|
/// Other handy functions that are implemented in the ancestor classes (see
|
||||||
/// classes 'FIFOProcessor' and 'FIFOSamplePipe')
|
/// classes 'FIFOProcessor' and 'FIFOSamplePipe')
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,14 @@
|
||||||
namespace soundtouch
|
namespace soundtouch
|
||||||
{
|
{
|
||||||
|
|
||||||
EXPORT
|
SOUNDTOUCH_API
|
||||||
soundtouch::SoundTouch*
|
soundtouch::SoundTouch*
|
||||||
createSoundTouchObj()
|
createSoundTouchObj()
|
||||||
{
|
{
|
||||||
return new soundtouch::SoundTouch();
|
return new soundtouch::SoundTouch();
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT
|
SOUNDTOUCH_API
|
||||||
void
|
void
|
||||||
destroySoundTouchObj(soundtouch::SoundTouch* aObj)
|
destroySoundTouchObj(soundtouch::SoundTouch* aObj)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,11 @@
|
||||||
|
|
||||||
namespace soundtouch
|
namespace soundtouch
|
||||||
{
|
{
|
||||||
EXPORT
|
SOUNDTOUCH_API
|
||||||
soundtouch::SoundTouch*
|
soundtouch::SoundTouch*
|
||||||
createSoundTouchObj();
|
createSoundTouchObj();
|
||||||
|
|
||||||
EXPORT
|
SOUNDTOUCH_API
|
||||||
void
|
void
|
||||||
destroySoundTouchObj(soundtouch::SoundTouch* aObj);
|
destroySoundTouchObj(soundtouch::SoundTouch* aObj);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,17 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||||
/// while maintaining the original pitch by using a time domain WSOLA-like
|
/// while maintaining the original pitch by using a time domain WSOLA-like
|
||||||
/// method with several performance-increasing tweaks.
|
/// method with several performance-increasing tweaks.
|
||||||
///
|
///
|
||||||
/// Note : MMX optimized functions reside in a separate, platform-specific
|
/// Notes : MMX optimized functions reside in a separate, platform-specific
|
||||||
/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'.
|
||||||
|
///
|
||||||
|
/// This source file contains OpenMP optimizations that allow speeding up the
|
||||||
|
/// corss-correlation algorithm by executing it in several threads / CPU cores
|
||||||
|
/// in parallel. See the following article link for more detailed discussion
|
||||||
|
/// about SoundTouch OpenMP optimizations:
|
||||||
|
/// http://www.softwarecoven.com/parallel-computing-in-embedded-mobile-devices
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
/// Author e-mail : oparviai 'at' iki.fi
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
|
@ -13,13 +19,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-02-22 15:07:12 +0000 (Sun, 22 Feb 2015) $
|
|
||||||
// File revision : $Revision: 1.12 $
|
|
||||||
//
|
|
||||||
// $Id: TDStretch.cpp 205 2015-02-22 15:07:12Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -55,7 +54,6 @@ using namespace soundtouch;
|
||||||
|
|
||||||
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
* Constant definitions
|
* Constant definitions
|
||||||
|
|
@ -63,7 +61,7 @@ using namespace soundtouch;
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
// Table for the hierarchical mixing position seeking algorithm
|
// Table for the hierarchical mixing position seeking algorithm
|
||||||
static const short _scanOffsets[5][24]={
|
const short _scanOffsets[5][24]={
|
||||||
{ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
|
{ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
|
||||||
868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
|
868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
|
||||||
{-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
|
{-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
|
||||||
|
|
@ -94,9 +92,6 @@ TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
||||||
bAutoSeqSetting = true;
|
bAutoSeqSetting = true;
|
||||||
bAutoSeekSetting = true;
|
bAutoSeekSetting = true;
|
||||||
|
|
||||||
// outDebt = 0;
|
|
||||||
skipFract = 0;
|
|
||||||
|
|
||||||
tempo = 1.0f;
|
tempo = 1.0f;
|
||||||
setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
|
setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
|
||||||
setTempo(1.0f);
|
setTempo(1.0f);
|
||||||
|
|
@ -222,6 +217,10 @@ void TDStretch::clearInput()
|
||||||
{
|
{
|
||||||
inputBuffer.clear();
|
inputBuffer.clear();
|
||||||
clearMidBuffer();
|
clearMidBuffer();
|
||||||
|
isBeginning = true;
|
||||||
|
maxnorm = 0;
|
||||||
|
maxnormf = 1e8;
|
||||||
|
skipFract = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -287,7 +286,6 @@ inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, ui
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
||||||
// routine
|
// routine
|
||||||
//
|
//
|
||||||
|
|
@ -301,21 +299,23 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||||
int i;
|
int i;
|
||||||
double norm;
|
double norm;
|
||||||
|
|
||||||
bestCorr = FLT_MIN;
|
bestCorr = -FLT_MAX;
|
||||||
bestOffs = 0;
|
bestOffs = 0;
|
||||||
|
|
||||||
// Scans for the best correlation value by testing each possible position
|
// Scans for the best correlation value by testing each possible position
|
||||||
// over the permitted range.
|
// over the permitted range.
|
||||||
bestCorr = calcCrossCorr(refPos, pMidBuffer, norm);
|
bestCorr = calcCrossCorr(refPos, pMidBuffer, norm);
|
||||||
|
bestCorr = (bestCorr + 0.1) * 0.75;
|
||||||
|
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
for (i = 1; i < seekLength; i ++)
|
for (i = 1; i < seekLength; i ++)
|
||||||
{
|
{
|
||||||
double corr;
|
double corr;
|
||||||
// Calculates correlation value for the mixing position corresponding to 'i'
|
// Calculates correlation value for the mixing position corresponding to 'i'
|
||||||
#ifdef _OPENMP
|
#if defined(_OPENMP) || defined(ST_SIMD_AVOID_UNALIGNED)
|
||||||
// in parallel OpenMP mode, can't use norm accumulator version as parallel executor won't
|
// in parallel OpenMP mode, can't use norm accumulator version as parallel executor won't
|
||||||
// iterate the loop in sequential order
|
// iterate the loop in sequential order
|
||||||
|
// in SIMD mode, avoid accumulator version to allow avoiding unaligned positions
|
||||||
corr = calcCrossCorr(refPos + channels * i, pMidBuffer, norm);
|
corr = calcCrossCorr(refPos + channels * i, pMidBuffer, norm);
|
||||||
#else
|
#else
|
||||||
// In non-parallel version call "calcCrossCorrAccumulate" that is otherwise same
|
// In non-parallel version call "calcCrossCorrAccumulate" that is otherwise same
|
||||||
|
|
@ -341,6 +341,11 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
adaptNormalizer();
|
||||||
|
#endif
|
||||||
|
|
||||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||||
clearCrossCorrState();
|
clearCrossCorrState();
|
||||||
|
|
||||||
|
|
@ -348,64 +353,157 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
// Quick seek algorithm for improved runtime-performance: First roughly scans through the
|
||||||
// routine
|
// correlation area, and then scan surroundings of two best preliminary correlation candidates
|
||||||
|
// with improved precision
|
||||||
//
|
//
|
||||||
// The best position is determined as the position where the two overlapped
|
// Based on testing:
|
||||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
// - This algorithm gives on average 99% as good match as the full algorithm
|
||||||
// value over the overlapping period
|
// - this quick seek algorithm finds the best match on ~90% of cases
|
||||||
|
// - on those 10% of cases when this algorithm doesn't find best match,
|
||||||
|
// it still finds on average ~90% match vs. the best possible match
|
||||||
int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||||
{
|
{
|
||||||
int j;
|
#define _MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||||
|
#define SCANSTEP 16
|
||||||
|
#define SCANWIND 8
|
||||||
|
|
||||||
int bestOffs;
|
int bestOffs;
|
||||||
double bestCorr, corr;
|
int i;
|
||||||
int scanCount, corrOffset, tempOffset;
|
int bestOffs2;
|
||||||
|
float bestCorr, corr;
|
||||||
|
float bestCorr2;
|
||||||
|
double norm;
|
||||||
|
|
||||||
bestCorr = FLT_MIN;
|
// note: 'float' types used in this function in case that the platform would need to use software-fp
|
||||||
bestOffs = _scanOffsets[0][0];
|
|
||||||
corrOffset = 0;
|
|
||||||
tempOffset = 0;
|
|
||||||
|
|
||||||
// Scans for the best correlation value using four-pass hierarchical search.
|
bestCorr =
|
||||||
|
bestCorr2 = -FLT_MAX;
|
||||||
|
bestOffs =
|
||||||
|
bestOffs2 = SCANWIND;
|
||||||
|
|
||||||
|
// Scans for the best correlation value by testing each possible position
|
||||||
|
// over the permitted range. Look for two best matches on the first pass to
|
||||||
|
// increase possibility of ideal match.
|
||||||
//
|
//
|
||||||
// The look-up table 'scans' has hierarchical position adjusting steps.
|
// Begin from "SCANSTEP" instead of SCANWIND to make the calculation
|
||||||
// In first pass the routine searhes for the highest correlation with
|
// catch the 'middlepoint' of seekLength vector as that's the a-priori
|
||||||
// relatively coarse steps, then rescans the neighbourhood of the highest
|
// expected best match position
|
||||||
// correlation with better resolution and so on.
|
//
|
||||||
for (scanCount = 0;scanCount < 4; scanCount ++)
|
// Roughly:
|
||||||
|
// - 15% of cases find best result directly on the first round,
|
||||||
|
// - 75% cases find better match on 2nd round around the best match from 1st round
|
||||||
|
// - 10% cases find better match on 2nd round around the 2nd-best-match from 1st round
|
||||||
|
for (i = SCANSTEP; i < seekLength - SCANWIND - 1; i += SCANSTEP)
|
||||||
{
|
{
|
||||||
j = 0;
|
// Calculates correlation value for the mixing position corresponding
|
||||||
while (_scanOffsets[scanCount][j])
|
// to 'i'
|
||||||
|
corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
|
||||||
|
// heuristic rule to slightly favour values close to mid of the seek range
|
||||||
|
float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
|
||||||
|
corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
|
||||||
|
|
||||||
|
// Checks for the highest correlation value
|
||||||
|
if (corr > bestCorr)
|
||||||
{
|
{
|
||||||
double norm;
|
// found new best match. keep the previous best as 2nd best match
|
||||||
tempOffset = corrOffset + _scanOffsets[scanCount][j];
|
bestCorr2 = bestCorr;
|
||||||
if (tempOffset >= seekLength) break;
|
bestOffs2 = bestOffs;
|
||||||
|
bestCorr = corr;
|
||||||
// Calculates correlation value for the mixing position corresponding
|
bestOffs = i;
|
||||||
// to 'tempOffset'
|
}
|
||||||
corr = (double)calcCrossCorr(refPos + channels * tempOffset, pMidBuffer, norm);
|
else if (corr > bestCorr2)
|
||||||
// heuristic rule to slightly favour values close to mid of the range
|
{
|
||||||
double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
|
// not new best, but still new 2nd best match
|
||||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
bestCorr2 = corr;
|
||||||
|
bestOffs2 = i;
|
||||||
// Checks for the highest correlation value
|
|
||||||
if (corr > bestCorr)
|
|
||||||
{
|
|
||||||
bestCorr = corr;
|
|
||||||
bestOffs = tempOffset;
|
|
||||||
}
|
|
||||||
j ++;
|
|
||||||
}
|
}
|
||||||
corrOffset = bestOffs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scans surroundings of the found best match with small stepping
|
||||||
|
int end = _MIN(bestOffs + SCANWIND + 1, seekLength);
|
||||||
|
for (i = bestOffs - SCANWIND; i < end; i++)
|
||||||
|
{
|
||||||
|
if (i == bestOffs) continue; // this offset already calculated, thus skip
|
||||||
|
|
||||||
|
// Calculates correlation value for the mixing position corresponding
|
||||||
|
// to 'i'
|
||||||
|
corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
|
||||||
|
// heuristic rule to slightly favour values close to mid of the range
|
||||||
|
float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
|
||||||
|
corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
|
||||||
|
|
||||||
|
// Checks for the highest correlation value
|
||||||
|
if (corr > bestCorr)
|
||||||
|
{
|
||||||
|
bestCorr = corr;
|
||||||
|
bestOffs = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scans surroundings of the 2nd best match with small stepping
|
||||||
|
end = _MIN(bestOffs2 + SCANWIND + 1, seekLength);
|
||||||
|
for (i = bestOffs2 - SCANWIND; i < end; i++)
|
||||||
|
{
|
||||||
|
if (i == bestOffs2) continue; // this offset already calculated, thus skip
|
||||||
|
|
||||||
|
// Calculates correlation value for the mixing position corresponding
|
||||||
|
// to 'i'
|
||||||
|
corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
|
||||||
|
// heuristic rule to slightly favour values close to mid of the range
|
||||||
|
float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
|
||||||
|
corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
|
||||||
|
|
||||||
|
// Checks for the highest correlation value
|
||||||
|
if (corr > bestCorr)
|
||||||
|
{
|
||||||
|
bestCorr = corr;
|
||||||
|
bestOffs = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||||
clearCrossCorrState();
|
clearCrossCorrState();
|
||||||
|
|
||||||
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
adaptNormalizer();
|
||||||
|
#endif
|
||||||
|
|
||||||
return bestOffs;
|
return bestOffs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// For integer algorithm: adapt normalization factor divider with music so that
|
||||||
|
/// it'll not be pessimistically restrictive that can degrade quality on quieter sections
|
||||||
|
/// yet won't cause integer overflows either
|
||||||
|
void TDStretch::adaptNormalizer()
|
||||||
|
{
|
||||||
|
// Do not adapt normalizer over too silent sequences to avoid averaging filter depleting to
|
||||||
|
// too low values during pauses in music
|
||||||
|
if ((maxnorm > 1000) || (maxnormf > 40000000))
|
||||||
|
{
|
||||||
|
//norm averaging filter
|
||||||
|
maxnormf = 0.9f * maxnormf + 0.1f * (float)maxnorm;
|
||||||
|
|
||||||
|
if ((maxnorm > 800000000) && (overlapDividerBitsNorm < 16))
|
||||||
|
{
|
||||||
|
// large values, so increase divider
|
||||||
|
overlapDividerBitsNorm++;
|
||||||
|
if (maxnorm > 1600000000) overlapDividerBitsNorm++; // extra large value => extra increase
|
||||||
|
}
|
||||||
|
else if ((maxnormf < 1000000) && (overlapDividerBitsNorm > 0))
|
||||||
|
{
|
||||||
|
// extra small values, decrease divider
|
||||||
|
overlapDividerBitsNorm--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxnorm = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// clear cross correlation routine state if necessary
|
/// clear cross correlation routine state if necessary
|
||||||
void TDStretch::clearCrossCorrState()
|
void TDStretch::clearCrossCorrState()
|
||||||
{
|
{
|
||||||
|
|
@ -417,18 +515,18 @@ void TDStretch::clearCrossCorrState()
|
||||||
void TDStretch::calcSeqParameters()
|
void TDStretch::calcSeqParameters()
|
||||||
{
|
{
|
||||||
// Adjust tempo param according to tempo, so that variating processing sequence length is used
|
// Adjust tempo param according to tempo, so that variating processing sequence length is used
|
||||||
// at varius tempo settings, between the given low...top limits
|
// at various tempo settings, between the given low...top limits
|
||||||
#define AUTOSEQ_TEMPO_LOW 0.5 // auto setting low tempo range (-50%)
|
#define AUTOSEQ_TEMPO_LOW 0.5 // auto setting low tempo range (-50%)
|
||||||
#define AUTOSEQ_TEMPO_TOP 2.0 // auto setting top tempo range (+100%)
|
#define AUTOSEQ_TEMPO_TOP 2.0 // auto setting top tempo range (+100%)
|
||||||
|
|
||||||
// sequence-ms setting values at above low & top tempo
|
// sequence-ms setting values at above low & top tempo
|
||||||
#define AUTOSEQ_AT_MIN 125.0
|
#define AUTOSEQ_AT_MIN 90.0
|
||||||
#define AUTOSEQ_AT_MAX 50.0
|
#define AUTOSEQ_AT_MAX 40.0
|
||||||
#define AUTOSEQ_K ((AUTOSEQ_AT_MAX - AUTOSEQ_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
#define AUTOSEQ_K ((AUTOSEQ_AT_MAX - AUTOSEQ_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
||||||
#define AUTOSEQ_C (AUTOSEQ_AT_MIN - (AUTOSEQ_K) * (AUTOSEQ_TEMPO_LOW))
|
#define AUTOSEQ_C (AUTOSEQ_AT_MIN - (AUTOSEQ_K) * (AUTOSEQ_TEMPO_LOW))
|
||||||
|
|
||||||
// seek-window-ms setting values at above low & top tempo
|
// seek-window-ms setting values at above low & top tempoq
|
||||||
#define AUTOSEEK_AT_MIN 25.0
|
#define AUTOSEEK_AT_MIN 20.0
|
||||||
#define AUTOSEEK_AT_MAX 15.0
|
#define AUTOSEEK_AT_MAX 15.0
|
||||||
#define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
#define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
||||||
#define AUTOSEEK_C (AUTOSEEK_AT_MIN - (AUTOSEEK_K) * (AUTOSEQ_TEMPO_LOW))
|
#define AUTOSEEK_C (AUTOSEEK_AT_MIN - (AUTOSEEK_K) * (AUTOSEQ_TEMPO_LOW))
|
||||||
|
|
@ -464,7 +562,7 @@ void TDStretch::calcSeqParameters()
|
||||||
|
|
||||||
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||||
// tempo, larger faster tempo.
|
// tempo, larger faster tempo.
|
||||||
void TDStretch::setTempo(float newTempo)
|
void TDStretch::setTempo(double newTempo)
|
||||||
{
|
{
|
||||||
int intskip;
|
int intskip;
|
||||||
|
|
||||||
|
|
@ -475,7 +573,7 @@ void TDStretch::setTempo(float newTempo)
|
||||||
|
|
||||||
// Calculate ideal skip length (according to tempo value)
|
// Calculate ideal skip length (according to tempo value)
|
||||||
nominalSkip = tempo * (seekWindowLength - overlapLength);
|
nominalSkip = tempo * (seekWindowLength - overlapLength);
|
||||||
intskip = (int)(nominalSkip + 0.5f);
|
intskip = (int)(nominalSkip + 0.5);
|
||||||
|
|
||||||
// Calculate how many samples are needed in the 'inputBuffer' to
|
// Calculate how many samples are needed in the 'inputBuffer' to
|
||||||
// process another batch of samples
|
// process another batch of samples
|
||||||
|
|
@ -488,9 +586,8 @@ void TDStretch::setTempo(float newTempo)
|
||||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void TDStretch::setChannels(int numChannels)
|
void TDStretch::setChannels(int numChannels)
|
||||||
{
|
{
|
||||||
assert(numChannels > 0);
|
if (!verifyNumberOfChannels(numChannels) ||
|
||||||
if (channels == numChannels) return;
|
(channels == numChannels)) return;
|
||||||
// assert(numChannels == 1 || numChannels == 2);
|
|
||||||
|
|
||||||
channels = numChannels;
|
channels = numChannels;
|
||||||
inputBuffer.setChannels(channels);
|
inputBuffer.setChannels(channels);
|
||||||
|
|
@ -539,7 +636,8 @@ void TDStretch::processNominalTempo()
|
||||||
// the result into 'outputBuffer'
|
// the result into 'outputBuffer'
|
||||||
void TDStretch::processSamples()
|
void TDStretch::processSamples()
|
||||||
{
|
{
|
||||||
int ovlSkip, offset;
|
int ovlSkip;
|
||||||
|
int offset = 0;
|
||||||
int temp;
|
int temp;
|
||||||
|
|
||||||
/* Removed this small optimization - can introduce a click to sound when tempo setting
|
/* Removed this small optimization - can introduce a click to sound when tempo setting
|
||||||
|
|
@ -556,35 +654,62 @@ void TDStretch::processSamples()
|
||||||
// to form a processing frame.
|
// to form a processing frame.
|
||||||
while ((int)inputBuffer.numSamples() >= sampleReq)
|
while ((int)inputBuffer.numSamples() >= sampleReq)
|
||||||
{
|
{
|
||||||
// If tempo differs from the normal ('SCALE'), scan for the best overlapping
|
if (isBeginning == false)
|
||||||
// position
|
{
|
||||||
offset = seekBestOverlapPosition(inputBuffer.ptrBegin());
|
// apart from the very beginning of the track,
|
||||||
|
// scan for the best overlapping position & do overlap-add
|
||||||
|
offset = seekBestOverlapPosition(inputBuffer.ptrBegin());
|
||||||
|
|
||||||
// Mix the samples in the 'inputBuffer' at position of 'offset' with the
|
// Mix the samples in the 'inputBuffer' at position of 'offset' with the
|
||||||
// samples in 'midBuffer' using sliding overlapping
|
// samples in 'midBuffer' using sliding overlapping
|
||||||
// ... first partially overlap with the end of the previous sequence
|
// ... first partially overlap with the end of the previous sequence
|
||||||
// (that's in 'midBuffer')
|
// (that's in 'midBuffer')
|
||||||
overlap(outputBuffer.ptrEnd((uint)overlapLength), inputBuffer.ptrBegin(), (uint)offset);
|
overlap(outputBuffer.ptrEnd((uint)overlapLength), inputBuffer.ptrBegin(), (uint)offset);
|
||||||
outputBuffer.putSamples((uint)overlapLength);
|
outputBuffer.putSamples((uint)overlapLength);
|
||||||
|
offset += overlapLength;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Adjust processing offset at beginning of track by not perform initial overlapping
|
||||||
|
// and compensating that in the 'input buffer skip' calculation
|
||||||
|
isBeginning = false;
|
||||||
|
int skip = (int)(tempo * overlapLength + 0.5 * seekLength + 0.5);
|
||||||
|
|
||||||
|
#ifdef ST_SIMD_AVOID_UNALIGNED
|
||||||
|
// in SIMD mode, round the skip amount to value corresponding to aligned memory address
|
||||||
|
if (channels == 1)
|
||||||
|
{
|
||||||
|
skip &= -4;
|
||||||
|
}
|
||||||
|
else if (channels == 2)
|
||||||
|
{
|
||||||
|
skip &= -2;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
skipFract -= skip;
|
||||||
|
if (skipFract <= -nominalSkip)
|
||||||
|
{
|
||||||
|
skipFract = -nominalSkip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ... then copy sequence samples from 'inputBuffer' to output:
|
// ... then copy sequence samples from 'inputBuffer' to output:
|
||||||
|
|
||||||
// length of sequence
|
|
||||||
temp = (seekWindowLength - 2 * overlapLength);
|
|
||||||
|
|
||||||
// crosscheck that we don't have buffer overflow...
|
// crosscheck that we don't have buffer overflow...
|
||||||
if ((int)inputBuffer.numSamples() < (offset + temp + overlapLength * 2))
|
if ((int)inputBuffer.numSamples() < (offset + seekWindowLength - overlapLength))
|
||||||
{
|
{
|
||||||
continue; // just in case, shouldn't really happen
|
continue; // just in case, shouldn't really happen
|
||||||
}
|
}
|
||||||
|
|
||||||
outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), (uint)temp);
|
// length of sequence
|
||||||
|
temp = (seekWindowLength - 2 * overlapLength);
|
||||||
|
outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * offset, (uint)temp);
|
||||||
|
|
||||||
// Copies the end of the current sequence from 'inputBuffer' to
|
// Copies the end of the current sequence from 'inputBuffer' to
|
||||||
// 'midBuffer' for being mixed with the beginning of the next
|
// 'midBuffer' for being mixed with the beginning of the next
|
||||||
// processing sequence and so on
|
// processing sequence and so on
|
||||||
assert((offset + temp + overlapLength * 2) <= (int)inputBuffer.numSamples());
|
assert((offset + temp + overlapLength) <= (int)inputBuffer.numSamples());
|
||||||
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp + overlapLength),
|
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp),
|
||||||
channels * sizeof(SAMPLETYPE) * overlapLength);
|
channels * sizeof(SAMPLETYPE) * overlapLength);
|
||||||
|
|
||||||
// Remove the processed samples from the input buffer. Update
|
// Remove the processed samples from the input buffer. Update
|
||||||
|
|
@ -680,7 +805,7 @@ TDStretch * TDStretch::newInstance()
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Integer arithmetics specific algorithm implementations.
|
// Integer arithmetic specific algorithm implementations.
|
||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
@ -706,21 +831,19 @@ void TDStretch::overlapStereo(short *poutput, const short *input) const
|
||||||
|
|
||||||
// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Multi'
|
// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Multi'
|
||||||
// version of the routine.
|
// version of the routine.
|
||||||
void TDStretch::overlapMulti(SAMPLETYPE *poutput, const SAMPLETYPE *input) const
|
void TDStretch::overlapMulti(short *poutput, const short *input) const
|
||||||
{
|
{
|
||||||
SAMPLETYPE m1=(SAMPLETYPE)0;
|
short m1;
|
||||||
SAMPLETYPE m2;
|
int i = 0;
|
||||||
int i=0;
|
|
||||||
|
|
||||||
for (m2 = (SAMPLETYPE)overlapLength; m2; m2 --)
|
for (m1 = 0; m1 < overlapLength; m1 ++)
|
||||||
{
|
{
|
||||||
|
short m2 = (short)(overlapLength - m1);
|
||||||
for (int c = 0; c < channels; c ++)
|
for (int c = 0; c < channels; c ++)
|
||||||
{
|
{
|
||||||
poutput[i] = (input[i] * m1 + pMidBuffer[i] * m2) / overlapLength;
|
poutput[i] = (input[i] * m1 + pMidBuffer[i] * m2) / overlapLength;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
m1++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -743,13 +866,15 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
||||||
// calculate overlap length so that it's power of 2 - thus it's easy to do
|
// calculate overlap length so that it's power of 2 - thus it's easy to do
|
||||||
// integer division by right-shifting. Term "-1" at end is to account for
|
// integer division by right-shifting. Term "-1" at end is to account for
|
||||||
// the extra most significatnt bit left unused in result by signed multiplication
|
// the extra most significatnt bit left unused in result by signed multiplication
|
||||||
overlapDividerBits = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
|
overlapDividerBitsPure = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
|
||||||
if (overlapDividerBits > 9) overlapDividerBits = 9;
|
if (overlapDividerBitsPure > 9) overlapDividerBitsPure = 9;
|
||||||
if (overlapDividerBits < 3) overlapDividerBits = 3;
|
if (overlapDividerBitsPure < 3) overlapDividerBitsPure = 3;
|
||||||
newOvl = (int)pow(2.0, (int)overlapDividerBits + 1); // +1 => account for -1 above
|
newOvl = (int)pow(2.0, (int)overlapDividerBitsPure + 1); // +1 => account for -1 above
|
||||||
|
|
||||||
acceptNewOverlapLength(newOvl);
|
acceptNewOverlapLength(newOvl);
|
||||||
|
|
||||||
|
overlapDividerBitsNorm = overlapDividerBitsPure;
|
||||||
|
|
||||||
// calculate sloping divider so that crosscorrelation operation won't
|
// calculate sloping divider so that crosscorrelation operation won't
|
||||||
// overflow 32-bit register. Max. sum of the crosscorrelation sum without
|
// overflow 32-bit register. Max. sum of the crosscorrelation sum without
|
||||||
// divider would be 2^30*(N^3-N)/3, where N = overlap length
|
// divider would be 2^30*(N^3-N)/3, where N = overlap length
|
||||||
|
|
@ -757,28 +882,40 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, double &norm) const
|
double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, double &norm)
|
||||||
{
|
{
|
||||||
long corr;
|
long corr;
|
||||||
long lnorm;
|
unsigned long lnorm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
#ifdef ST_SIMD_AVOID_UNALIGNED
|
||||||
|
// in SIMD mode skip 'mixingPos' positions that aren't aligned to 16-byte boundary
|
||||||
|
if (((ulongptr)mixingPos) & 15) return -1e50;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = (channels * overlapLength) & -8;
|
||||||
|
|
||||||
corr = lnorm = 0;
|
corr = lnorm = 0;
|
||||||
// Same routine for stereo and mono. For stereo, unroll loop for better
|
// Same routine for stereo and mono
|
||||||
// efficiency and gives slightly better resolution against rounding.
|
for (i = 0; i < ilength; i += 2)
|
||||||
// For mono it same routine, just unrolls loop by factor of 4
|
|
||||||
for (i = 0; i < channels * overlapLength; i += 4)
|
|
||||||
{
|
{
|
||||||
corr += (mixingPos[i] * compare[i] +
|
corr += (mixingPos[i] * compare[i] +
|
||||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
|
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm;
|
||||||
corr += (mixingPos[i + 2] * compare[i + 2] +
|
|
||||||
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBits;
|
|
||||||
lnorm += (mixingPos[i] * mixingPos[i] +
|
lnorm += (mixingPos[i] * mixingPos[i] +
|
||||||
mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
|
mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBitsNorm;
|
||||||
lnorm += (mixingPos[i + 2] * mixingPos[i + 2] +
|
// do intermediate scalings to avoid integer overflow
|
||||||
mixingPos[i + 3] * mixingPos[i + 3]) >> overlapDividerBits;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lnorm > maxnorm)
|
||||||
|
{
|
||||||
|
// modify 'maxnorm' inside critical section to avoid multi-access conflict if in OpenMP mode
|
||||||
|
#pragma omp critical
|
||||||
|
if (lnorm > maxnorm)
|
||||||
|
{
|
||||||
|
maxnorm = lnorm;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
norm = (double)lnorm;
|
norm = (double)lnorm;
|
||||||
|
|
@ -787,38 +924,42 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do
|
||||||
|
|
||||||
|
|
||||||
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||||
double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm) const
|
double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm)
|
||||||
{
|
{
|
||||||
long corr;
|
long corr;
|
||||||
long lnorm;
|
long lnorm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = (channels * overlapLength) & -8;
|
||||||
|
|
||||||
// cancel first normalizer tap from previous round
|
// cancel first normalizer tap from previous round
|
||||||
lnorm = 0;
|
lnorm = 0;
|
||||||
for (i = 1; i <= channels; i ++)
|
for (i = 1; i <= channels; i ++)
|
||||||
{
|
{
|
||||||
lnorm -= (mixingPos[-i] * mixingPos[-i]) >> overlapDividerBits;
|
lnorm -= (mixingPos[-i] * mixingPos[-i]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
corr = 0;
|
corr = 0;
|
||||||
// Same routine for stereo and mono. For stereo, unroll loop for better
|
// Same routine for stereo and mono.
|
||||||
// efficiency and gives slightly better resolution against rounding.
|
for (i = 0; i < ilength; i += 2)
|
||||||
// For mono it same routine, just unrolls loop by factor of 4
|
|
||||||
for (i = 0; i < channels * overlapLength; i += 4)
|
|
||||||
{
|
{
|
||||||
corr += (mixingPos[i] * compare[i] +
|
corr += (mixingPos[i] * compare[i] +
|
||||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
|
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm;
|
||||||
corr += (mixingPos[i + 2] * compare[i + 2] +
|
|
||||||
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBits;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update normalizer with last samples of this round
|
// update normalizer with last samples of this round
|
||||||
for (int j = 0; j < channels; j ++)
|
for (int j = 0; j < channels; j ++)
|
||||||
{
|
{
|
||||||
i --;
|
i --;
|
||||||
lnorm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBits;
|
lnorm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
norm += (double)lnorm;
|
norm += (double)lnorm;
|
||||||
|
if (norm > maxnorm)
|
||||||
|
{
|
||||||
|
maxnorm = (unsigned long)norm;
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
|
|
@ -829,7 +970,7 @@ double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *c
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Floating point arithmetics specific algorithm implementations.
|
// Floating point arithmetic specific algorithm implementations.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
|
@ -903,29 +1044,26 @@ void TDStretch::calculateOverlapLength(int overlapInMsec)
|
||||||
|
|
||||||
|
|
||||||
/// Calculate cross-correlation
|
/// Calculate cross-correlation
|
||||||
double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &anorm) const
|
double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &anorm)
|
||||||
{
|
{
|
||||||
double corr;
|
float corr;
|
||||||
double norm;
|
float norm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
#ifdef ST_SIMD_AVOID_UNALIGNED
|
||||||
|
// in SIMD mode skip 'mixingPos' positions that aren't aligned to 16-byte boundary
|
||||||
|
if (((ulongptr)mixingPos) & 15) return -1e50;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = (channels * overlapLength) & -8;
|
||||||
|
|
||||||
corr = norm = 0;
|
corr = norm = 0;
|
||||||
// Same routine for stereo and mono. For Stereo, unroll by factor of 2.
|
// Same routine for stereo and mono
|
||||||
// For mono it's same routine yet unrollsd by factor of 4.
|
for (i = 0; i < ilength; i ++)
|
||||||
for (i = 0; i < channels * overlapLength; i += 4)
|
|
||||||
{
|
{
|
||||||
corr += mixingPos[i] * compare[i] +
|
corr += mixingPos[i] * compare[i];
|
||||||
mixingPos[i + 1] * compare[i + 1];
|
norm += mixingPos[i] * mixingPos[i];
|
||||||
|
|
||||||
norm += mixingPos[i] * mixingPos[i] +
|
|
||||||
mixingPos[i + 1] * mixingPos[i + 1];
|
|
||||||
|
|
||||||
// unroll the loop for better CPU efficiency:
|
|
||||||
corr += mixingPos[i + 2] * compare[i + 2] +
|
|
||||||
mixingPos[i + 3] * compare[i + 3];
|
|
||||||
|
|
||||||
norm += mixingPos[i + 2] * mixingPos[i + 2] +
|
|
||||||
mixingPos[i + 3] * mixingPos[i + 3];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
anorm = norm;
|
anorm = norm;
|
||||||
|
|
@ -934,9 +1072,9 @@ double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, do
|
||||||
|
|
||||||
|
|
||||||
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||||
double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) const
|
double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm)
|
||||||
{
|
{
|
||||||
double corr;
|
float corr;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
corr = 0;
|
corr = 0;
|
||||||
|
|
@ -947,14 +1085,13 @@ double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *c
|
||||||
norm -= mixingPos[-i] * mixingPos[-i];
|
norm -= mixingPos[-i] * mixingPos[-i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same routine for stereo and mono. For Stereo, unroll by factor of 2.
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
// For mono it's same routine yet unrollsd by factor of 4.
|
int ilength = (channels * overlapLength) & -8;
|
||||||
for (i = 0; i < channels * overlapLength; i += 4)
|
|
||||||
|
// Same routine for stereo and mono
|
||||||
|
for (i = 0; i < ilength; i ++)
|
||||||
{
|
{
|
||||||
corr += mixingPos[i] * compare[i] +
|
corr += mixingPos[i] * compare[i];
|
||||||
mixingPos[i + 1] * compare[i + 1] +
|
|
||||||
mixingPos[i + 2] * compare[i + 2] +
|
|
||||||
mixingPos[i + 3] * compare[i + 3];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update normalizer with last samples of this round
|
// update normalizer with last samples of this round
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-04-06 15:57:21 +0000 (Sun, 06 Apr 2014) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: TDStretch.h 195 2014-04-06 15:57:21Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -112,39 +105,47 @@ class TDStretch : public FIFOProcessor
|
||||||
protected:
|
protected:
|
||||||
int channels;
|
int channels;
|
||||||
int sampleReq;
|
int sampleReq;
|
||||||
float tempo;
|
|
||||||
|
|
||||||
SAMPLETYPE *pMidBuffer;
|
|
||||||
SAMPLETYPE *pMidBufferUnaligned;
|
|
||||||
int overlapLength;
|
int overlapLength;
|
||||||
int seekLength;
|
int seekLength;
|
||||||
int seekWindowLength;
|
int seekWindowLength;
|
||||||
int overlapDividerBits;
|
int overlapDividerBitsNorm;
|
||||||
|
int overlapDividerBitsPure;
|
||||||
int slopingDivider;
|
int slopingDivider;
|
||||||
float nominalSkip;
|
|
||||||
float skipFract;
|
|
||||||
FIFOSampleBuffer outputBuffer;
|
|
||||||
FIFOSampleBuffer inputBuffer;
|
|
||||||
bool bQuickSeek;
|
|
||||||
|
|
||||||
int sampleRate;
|
int sampleRate;
|
||||||
int sequenceMs;
|
int sequenceMs;
|
||||||
int seekWindowMs;
|
int seekWindowMs;
|
||||||
int overlapMs;
|
int overlapMs;
|
||||||
|
|
||||||
|
unsigned long maxnorm;
|
||||||
|
float maxnormf;
|
||||||
|
|
||||||
|
double tempo;
|
||||||
|
double nominalSkip;
|
||||||
|
double skipFract;
|
||||||
|
|
||||||
|
bool bQuickSeek;
|
||||||
bool bAutoSeqSetting;
|
bool bAutoSeqSetting;
|
||||||
bool bAutoSeekSetting;
|
bool bAutoSeekSetting;
|
||||||
|
bool isBeginning;
|
||||||
|
|
||||||
|
SAMPLETYPE *pMidBuffer;
|
||||||
|
SAMPLETYPE *pMidBufferUnaligned;
|
||||||
|
|
||||||
|
FIFOSampleBuffer outputBuffer;
|
||||||
|
FIFOSampleBuffer inputBuffer;
|
||||||
|
|
||||||
void acceptNewOverlapLength(int newOverlapLength);
|
void acceptNewOverlapLength(int newOverlapLength);
|
||||||
|
|
||||||
virtual void clearCrossCorrState();
|
virtual void clearCrossCorrState();
|
||||||
void calculateOverlapLength(int overlapMs);
|
void calculateOverlapLength(int overlapMs);
|
||||||
|
|
||||||
virtual double calcCrossCorr(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm) const;
|
virtual double calcCrossCorr(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm);
|
||||||
virtual double calcCrossCorrAccumulate(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm) const;
|
virtual double calcCrossCorrAccumulate(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm);
|
||||||
|
|
||||||
virtual int seekBestOverlapPositionFull(const SAMPLETYPE *refPos);
|
virtual int seekBestOverlapPositionFull(const SAMPLETYPE *refPos);
|
||||||
virtual int seekBestOverlapPositionQuick(const SAMPLETYPE *refPos);
|
virtual int seekBestOverlapPositionQuick(const SAMPLETYPE *refPos);
|
||||||
int seekBestOverlapPosition(const SAMPLETYPE *refPos);
|
virtual int seekBestOverlapPosition(const SAMPLETYPE *refPos);
|
||||||
|
|
||||||
virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
||||||
virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
||||||
|
|
@ -154,6 +155,7 @@ protected:
|
||||||
void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const;
|
void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const;
|
||||||
|
|
||||||
void calcSeqParameters();
|
void calcSeqParameters();
|
||||||
|
void adaptNormalizer();
|
||||||
|
|
||||||
/// Changes the tempo of the given sound samples.
|
/// Changes the tempo of the given sound samples.
|
||||||
/// Returns amount of samples returned in the "output" buffer.
|
/// Returns amount of samples returned in the "output" buffer.
|
||||||
|
|
@ -182,7 +184,7 @@ public:
|
||||||
|
|
||||||
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||||
/// tempo, larger faster tempo.
|
/// tempo, larger faster tempo.
|
||||||
void setTempo(float newTempo);
|
void setTempo(double newTempo);
|
||||||
|
|
||||||
/// Returns nonzero if there aren't any samples available for outputting.
|
/// Returns nonzero if there aren't any samples available for outputting.
|
||||||
virtual void clear();
|
virtual void clear();
|
||||||
|
|
@ -238,8 +240,13 @@ public:
|
||||||
{
|
{
|
||||||
return seekWindowLength - overlapLength;
|
return seekWindowLength - overlapLength;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
/// return approximate initial input-output latency
|
||||||
|
int getLatency() const
|
||||||
|
{
|
||||||
|
return sampleReq;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Implementation-specific class declarations:
|
// Implementation-specific class declarations:
|
||||||
|
|
@ -249,8 +256,8 @@ public:
|
||||||
class TDStretchMMX : public TDStretch
|
class TDStretchMMX : public TDStretch
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
double calcCrossCorr(const short *mixingPos, const short *compare, double &norm) const;
|
double calcCrossCorr(const short *mixingPos, const short *compare, double &norm);
|
||||||
double calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm) const;
|
double calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm);
|
||||||
virtual void overlapStereo(short *output, const short *input) const;
|
virtual void overlapStereo(short *output, const short *input) const;
|
||||||
virtual void clearCrossCorrState();
|
virtual void clearCrossCorrState();
|
||||||
};
|
};
|
||||||
|
|
@ -262,8 +269,8 @@ public:
|
||||||
class TDStretchSSE : public TDStretch
|
class TDStretchSSE : public TDStretch
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
double calcCrossCorr(const float *mixingPos, const float *compare, double &norm) const;
|
double calcCrossCorr(const float *mixingPos, const float *compare, double &norm);
|
||||||
double calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) const;
|
double calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /// SOUNDTOUCH_ALLOW_SSE
|
#endif /// SOUNDTOUCH_ALLOW_SSE
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2008-02-10 16:26:55 +0000 (Sun, 10 Feb 2008) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: cpu_detect.h 11 2008-02-10 16:26:55Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-01-07 18:24:28 +0000 (Tue, 07 Jan 2014) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: cpu_detect_x86.cpp 183 2014-01-07 18:24:28Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -74,7 +67,6 @@ void disableExtensions(uint dwDisableMask)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Checks which instruction set extensions are supported by the CPU.
|
/// Checks which instruction set extensions are supported by the CPU.
|
||||||
uint detectCPUextensions(void)
|
uint detectCPUextensions(void)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-02-22 15:10:38 +0000 (Sun, 22 Feb 2015) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: mmx_optimized.cpp 206 2015-02-22 15:10:38Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -68,7 +61,7 @@ using namespace soundtouch;
|
||||||
|
|
||||||
|
|
||||||
// Calculates cross correlation of two buffers
|
// Calculates cross correlation of two buffers
|
||||||
double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &dnorm) const
|
double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &dnorm)
|
||||||
{
|
{
|
||||||
const __m64 *pVec1, *pVec2;
|
const __m64 *pVec1, *pVec2;
|
||||||
__m64 shifter;
|
__m64 shifter;
|
||||||
|
|
@ -79,7 +72,7 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d
|
||||||
pVec1 = (__m64*)pV1;
|
pVec1 = (__m64*)pV1;
|
||||||
pVec2 = (__m64*)pV2;
|
pVec2 = (__m64*)pV2;
|
||||||
|
|
||||||
shifter = _m_from_int(overlapDividerBits);
|
shifter = _m_from_int(overlapDividerBitsNorm);
|
||||||
normaccu = accu = _mm_setzero_si64();
|
normaccu = accu = _mm_setzero_si64();
|
||||||
|
|
||||||
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
||||||
|
|
@ -123,6 +116,16 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d
|
||||||
// Clear MMS state
|
// Clear MMS state
|
||||||
_m_empty();
|
_m_empty();
|
||||||
|
|
||||||
|
if (norm > (long)maxnorm)
|
||||||
|
{
|
||||||
|
// modify 'maxnorm' inside critical section to avoid multi-access conflict if in OpenMP mode
|
||||||
|
#pragma omp critical
|
||||||
|
if (norm > (long)maxnorm)
|
||||||
|
{
|
||||||
|
maxnorm = norm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
dnorm = (double)norm;
|
dnorm = (double)norm;
|
||||||
|
|
@ -134,7 +137,7 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d
|
||||||
|
|
||||||
|
|
||||||
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||||
double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2, double &dnorm) const
|
double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2, double &dnorm)
|
||||||
{
|
{
|
||||||
const __m64 *pVec1, *pVec2;
|
const __m64 *pVec1, *pVec2;
|
||||||
__m64 shifter;
|
__m64 shifter;
|
||||||
|
|
@ -146,13 +149,13 @@ double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2,
|
||||||
lnorm = 0;
|
lnorm = 0;
|
||||||
for (i = 1; i <= channels; i ++)
|
for (i = 1; i <= channels; i ++)
|
||||||
{
|
{
|
||||||
lnorm -= (pV1[-i] * pV1[-i]) >> overlapDividerBits;
|
lnorm -= (pV1[-i] * pV1[-i]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
pVec1 = (__m64*)pV1;
|
pVec1 = (__m64*)pV1;
|
||||||
pVec2 = (__m64*)pV2;
|
pVec2 = (__m64*)pV2;
|
||||||
|
|
||||||
shifter = _m_from_int(overlapDividerBits);
|
shifter = _m_from_int(overlapDividerBitsNorm);
|
||||||
accu = _mm_setzero_si64();
|
accu = _mm_setzero_si64();
|
||||||
|
|
||||||
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
||||||
|
|
@ -191,10 +194,15 @@ double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2,
|
||||||
pV1 = (short *)pVec1;
|
pV1 = (short *)pVec1;
|
||||||
for (int j = 1; j <= channels; j ++)
|
for (int j = 1; j <= channels; j ++)
|
||||||
{
|
{
|
||||||
lnorm += (pV1[-j] * pV1[-j]) >> overlapDividerBits;
|
lnorm += (pV1[-j] * pV1[-j]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
dnorm += (double)lnorm;
|
dnorm += (double)lnorm;
|
||||||
|
|
||||||
|
if (lnorm > (long)maxnorm)
|
||||||
|
{
|
||||||
|
maxnorm = lnorm;
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
return (double)corr / sqrt((dnorm < 1e-9) ? 1.0 : dnorm);
|
return (double)corr / sqrt((dnorm < 1e-9) ? 1.0 : dnorm);
|
||||||
|
|
@ -209,7 +217,6 @@ void TDStretchMMX::clearCrossCorrState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// MMX-optimized version of the function overlapStereo
|
// MMX-optimized version of the function overlapStereo
|
||||||
void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||||
{
|
{
|
||||||
|
|
@ -233,7 +240,7 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||||
|
|
||||||
// Overlaplength-division by shifter. "+1" is to account for "-1" deduced in
|
// Overlaplength-division by shifter. "+1" is to account for "-1" deduced in
|
||||||
// overlapDividerBits calculation earlier.
|
// overlapDividerBits calculation earlier.
|
||||||
shifter = _m_from_int(overlapDividerBits + 1);
|
shifter = _m_from_int(overlapDividerBitsPure + 1);
|
||||||
|
|
||||||
for (i = 0; i < overlapLength / 4; i ++)
|
for (i = 0; i < overlapLength / 4; i ++)
|
||||||
{
|
{
|
||||||
|
|
@ -325,7 +332,6 @@ void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uRe
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// mmx-optimized version of the filter routine for stereo sound
|
// mmx-optimized version of the filter routine for stereo sound
|
||||||
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numSamples) const
|
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numSamples) const
|
||||||
{
|
{
|
||||||
|
|
@ -382,4 +388,9 @@ uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numS
|
||||||
return (numSamples & 0xfffffffe) - length;
|
return (numSamples & 0xfffffffe) - length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// workaround to not complain about empty module
|
||||||
|
bool _dontcomplain_mmx_empty;
|
||||||
|
|
||||||
#endif // SOUNDTOUCH_ALLOW_MMX
|
#endif // SOUNDTOUCH_ALLOW_MMX
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||||
|
# vim: set filetype=python:
|
||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
@ -41,6 +42,8 @@ else:
|
||||||
# Windows need alloca renamed to _alloca
|
# Windows need alloca renamed to _alloca
|
||||||
DEFINES['alloca'] = '_alloca'
|
DEFINES['alloca'] = '_alloca'
|
||||||
|
|
||||||
|
DEFINES['BUILDING_SOUNDTOUCH'] = 1
|
||||||
|
|
||||||
# We allow warnings for third-party code that can be updated from upstream.
|
# We allow warnings for third-party code that can be updated from upstream.
|
||||||
ALLOW_COMPILER_WARNINGS = True
|
ALLOW_COMPILER_WARNINGS = True
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,6 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2015-02-21 21:24:29 +0000 (Sat, 21 Feb 2015) $
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id: sse_optimized.cpp 202 2015-02-21 21:24:29Z oparviai $
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|
@ -71,7 +64,7 @@ using namespace soundtouch;
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
// Calculates cross correlation of two buffers
|
// Calculates cross correlation of two buffers
|
||||||
double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &anorm) const
|
double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &anorm)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
const float *pVec1;
|
const float *pVec1;
|
||||||
|
|
@ -87,7 +80,7 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a
|
||||||
// Compile-time define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided
|
// Compile-time define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided
|
||||||
// for choosing if this little cheating is allowed.
|
// for choosing if this little cheating is allowed.
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION
|
#ifdef ST_SIMD_AVOID_UNALIGNED
|
||||||
// Little cheating allowed, return valid correlation only for
|
// Little cheating allowed, return valid correlation only for
|
||||||
// aligned locations, meaning every second round for stereo sound.
|
// aligned locations, meaning every second round for stereo sound.
|
||||||
|
|
||||||
|
|
@ -183,7 +176,7 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2, double &norm) const
|
double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2, double &norm)
|
||||||
{
|
{
|
||||||
// call usual calcCrossCorr function because SSE does not show big benefit of
|
// call usual calcCrossCorr function because SSE does not show big benefit of
|
||||||
// accumulating "norm" value, and also the "norm" rolling algorithm would get
|
// accumulating "norm" value, and also the "norm" rolling algorithm would get
|
||||||
|
|
|
||||||
|
|
@ -43,4 +43,6 @@ done
|
||||||
|
|
||||||
# Patch the imported files.
|
# Patch the imported files.
|
||||||
patch -p1 < moz-libsoundtouch.patch
|
patch -p1 < moz-libsoundtouch.patch
|
||||||
|
patch -p1 < mcp-disableexceptions.patch
|
||||||
|
|
||||||
|
echo "Remember to update README_MCP with the version details."
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue