Skip to content

Commit

Permalink
Different loose mid side algorithm compatible with multithreading (#675)
Browse files Browse the repository at this point in the history
  • Loading branch information
ktmf01 authored Mar 9, 2024
1 parent 8d4ccdf commit d2b2441
Showing 1 changed file with 65 additions and 80 deletions.
145 changes: 65 additions & 80 deletions src/libFLAC/stream_encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@
*/
#undef ENABLE_RICE_PARAMETER_SEARCH

#ifdef local_abs64
#undef local_abs64
#endif
#define local_abs64(x) ((uint64_t)((x)<0? -(x) : (x)))


typedef struct {
FLAC__int32 *data[FLAC__MAX_CHANNELS];
Expand Down Expand Up @@ -413,9 +418,6 @@ typedef struct FLAC__StreamEncoderPrivate {
FLAC__real *window[FLAC__MAX_APODIZATION_FUNCTIONS]; /* the pre-computed floating-point window for each apodization function */
FLAC__real *window_unaligned[FLAC__MAX_APODIZATION_FUNCTIONS];
#endif
uint32_t loose_mid_side_stereo_frames; /* rounded number of frames the encoder will use before trying both independent and mid/side frames again */
uint32_t loose_mid_side_stereo_frame_count; /* number of frames using the current channel assignment */
FLAC__ChannelAssignment last_channel_assignment;
FLAC__StreamMetadata streaminfo; /* scratchpad for STREAMINFO as it is built */
FLAC__StreamMetadata_SeekTable *seek_table; /* pointer into encoder->protected_->metadata_ where the seek table is */
uint32_t current_sample_number;
Expand Down Expand Up @@ -737,9 +739,6 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_(
else if(!encoder->protected_->do_mid_side_stereo)
encoder->protected_->loose_mid_side_stereo = false;

if(encoder->protected_->loose_mid_side_stereo && encoder->protected_->num_threads > 2)
encoder->protected_->num_threads = 2;

if(encoder->protected_->bits_per_sample < FLAC__MIN_BITS_PER_SAMPLE || encoder->protected_->bits_per_sample > FLAC__MAX_BITS_PER_SAMPLE)
return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE;

Expand Down Expand Up @@ -921,20 +920,6 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_(
}

encoder->private_->input_capacity = 0;
#ifndef FLAC__INTEGER_ONLY_LIBRARY
encoder->private_->loose_mid_side_stereo_frames = (uint32_t)((double)encoder->protected_->sample_rate * 0.4 / (double)encoder->protected_->blocksize + 0.5);
#else
/* 26214 is the approximate fixed-point equivalent to 0.4 (0.4 * 2^16) */
/* sample rate can be up to 1048575 Hz, and thus use 20 bits, so we do the multiply&divide by hand */
FLAC__ASSERT(FLAC__MAX_SAMPLE_RATE <= 1048575);
FLAC__ASSERT(FLAC__MAX_BLOCK_SIZE <= 65535);
FLAC__ASSERT(encoder->protected_->sample_rate <= 1048575);
FLAC__ASSERT(encoder->protected_->blocksize <= 65535);
encoder->private_->loose_mid_side_stereo_frames = (uint32_t)FLAC__fixedpoint_trunc((((FLAC__uint64)(encoder->protected_->sample_rate) * (FLAC__uint64)(26214)) << 16) / (encoder->protected_->blocksize<<16) + FLAC__FP_ONE_HALF);
#endif
if(encoder->private_->loose_mid_side_stereo_frames == 0)
encoder->private_->loose_mid_side_stereo_frames = 1;
encoder->private_->loose_mid_side_stereo_frame_count = 0;
encoder->private_->current_sample_number = 0;
encoder->private_->current_frame_number = 0;

Expand Down Expand Up @@ -3518,42 +3503,35 @@ FLAC__bool process_frame_(FLAC__StreamEncoder *encoder, FLAC__bool is_last_block
}
else if(encoder->private_->num_started_threadtasks == encoder->private_->num_threadtasks) {
/* If the first task in the queue is still running, check whether there is enough work
* left in the queue. If there is, start on some */
if(encoder->protected_->loose_mid_side_stereo) {
pthread_mutex_lock(&encoder->private_->threadtask[encoder->private_->next_thread]->mutex_this_task);
if(!encoder->private_->threadtask[encoder->private_->next_thread]->task_done)
pthread_cond_wait(&encoder->private_->threadtask[encoder->private_->next_thread]->cond_task_done, &encoder->private_->threadtask[encoder->private_->next_thread]->mutex_this_task);
}
else {
/* First, check whether the mutex for the next due task is locked or free. If it is free (and thus acquired now) and
* the task is done, proceed to the next bit (writing the bitbuffer). If it is either currently locked or not yet
* processed, choose between starting on some work (if there is enough work in the queue) or waiting for the task
* to finish. Either way, release the mutex first, so it doesn't get interlocked with the work queue mutex */
int mutex_result = pthread_mutex_trylock(&encoder->private_->threadtask[encoder->private_->next_thread]->mutex_this_task);
while(mutex_result || !encoder->private_->threadtask[encoder->private_->next_thread]->task_done) {
if(!mutex_result)
pthread_mutex_unlock(&encoder->private_->threadtask[encoder->private_->next_thread]->mutex_this_task);

pthread_mutex_lock(&encoder->private_->mutex_work_queue);
if(encoder->private_->num_available_threadtasks > (encoder->protected_->num_threads - 1)) {
FLAC__StreamEncoderThreadTask * task = NULL;
task = encoder->private_->threadtask[encoder->private_->next_threadtask];
encoder->private_->num_available_threadtasks--;
encoder->private_->next_threadtask++;
if(encoder->private_->next_threadtask == encoder->private_->num_threadtasks)
encoder->private_->next_threadtask = 1;
pthread_mutex_unlock(&encoder->private_->mutex_work_queue);
pthread_mutex_lock(&task->mutex_this_task);
process_frame_thread_inner_(encoder, task);
mutex_result = pthread_mutex_trylock(&encoder->private_->threadtask[encoder->private_->next_thread]->mutex_this_task);
}
else {
pthread_mutex_unlock(&encoder->private_->mutex_work_queue);
pthread_mutex_lock(&encoder->private_->threadtask[encoder->private_->next_thread]->mutex_this_task);
while(!encoder->private_->threadtask[encoder->private_->next_thread]->task_done)
pthread_cond_wait(&encoder->private_->threadtask[encoder->private_->next_thread]->cond_task_done,&encoder->private_->threadtask[encoder->private_->next_thread]->mutex_this_task);
mutex_result = 0;
}
* left in the queue. If there is, start on some
* First, check whether the mutex for the next due task is locked or free. If it is free (and thus acquired now) and
* the task is done, proceed to the next bit (writing the bitbuffer). If it is either currently locked or not yet
* processed, choose between starting on some work (if there is enough work in the queue) or waiting for the task
* to finish. Either way, release the mutex first, so it doesn't get interlocked with the work queue mutex */
int mutex_result = pthread_mutex_trylock(&encoder->private_->threadtask[encoder->private_->next_thread]->mutex_this_task);
while(mutex_result || !encoder->private_->threadtask[encoder->private_->next_thread]->task_done) {
if(!mutex_result)
pthread_mutex_unlock(&encoder->private_->threadtask[encoder->private_->next_thread]->mutex_this_task);

pthread_mutex_lock(&encoder->private_->mutex_work_queue);
if(encoder->private_->num_available_threadtasks > (encoder->protected_->num_threads - 1)) {
FLAC__StreamEncoderThreadTask * task = NULL;
task = encoder->private_->threadtask[encoder->private_->next_threadtask];
encoder->private_->num_available_threadtasks--;
encoder->private_->next_threadtask++;
if(encoder->private_->next_threadtask == encoder->private_->num_threadtasks)
encoder->private_->next_threadtask = 1;
pthread_mutex_unlock(&encoder->private_->mutex_work_queue);
pthread_mutex_lock(&task->mutex_this_task);
process_frame_thread_inner_(encoder, task);
mutex_result = pthread_mutex_trylock(&encoder->private_->threadtask[encoder->private_->next_thread]->mutex_this_task);
}
else {
pthread_mutex_unlock(&encoder->private_->mutex_work_queue);
pthread_mutex_lock(&encoder->private_->threadtask[encoder->private_->next_thread]->mutex_this_task);
while(!encoder->private_->threadtask[encoder->private_->next_thread]->task_done)
pthread_cond_wait(&encoder->private_->threadtask[encoder->private_->next_thread]->cond_task_done,&encoder->private_->threadtask[encoder->private_->next_thread]->mutex_this_task);
mutex_result = 0;
}
}
/* Task is finished, write bitbuffer */
Expand Down Expand Up @@ -3769,14 +3747,33 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__StreamEncoderT
*/
if(encoder->protected_->do_mid_side_stereo) {
if(encoder->protected_->loose_mid_side_stereo) {
FLAC__ASSERT(encoder->protected_->num_threads < 3);
if(encoder->private_->loose_mid_side_stereo_frame_count == 0) {
uint64_t sumAbsLR = 0, sumAbsMS = 0;
uint32_t i;
if(encoder->protected_->bits_per_sample < 25) {
for(i = 1; i < encoder->protected_->blocksize; i++) {
int32_t predictionLeft = threadtask->integer_signal[0][i] - threadtask->integer_signal[0][i-1];
int32_t predictionRight = threadtask->integer_signal[1][i] - threadtask->integer_signal[1][i-1];
sumAbsLR += abs(predictionLeft) + abs(predictionRight);
sumAbsMS += abs((predictionLeft + predictionRight) >> 1) + abs(predictionLeft - predictionRight);
}
}
else { /* bps 25 or higher */
for(i = 1; i < encoder->protected_->blocksize; i++) {
int64_t predictionLeft = (int64_t)threadtask->integer_signal[0][i] - (int64_t)threadtask->integer_signal[0][i-1];
int64_t predictionRight = (int64_t)threadtask->integer_signal[1][i] - (int64_t)threadtask->integer_signal[1][i-1];
sumAbsLR += local_abs64(predictionLeft) + local_abs64(predictionRight);
sumAbsMS += local_abs64((predictionLeft + predictionRight) >> 1) + local_abs64(predictionLeft - predictionRight);
}
}
if(sumAbsLR < sumAbsMS) {
do_independent = true;
do_mid_side = true;
do_mid_side = false;
frame_header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT;
}
else {
do_independent = (encoder->private_->last_channel_assignment == FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT);
do_mid_side = !do_independent;
do_independent = false;
do_mid_side = true;
frame_header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_MID_SIDE;
}
}
else {
Expand Down Expand Up @@ -3908,17 +3905,14 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__StreamEncoderT
/*
* Compose the frame bitbuffer
*/
if(do_mid_side) {
if((do_independent && do_mid_side) || encoder->protected_->loose_mid_side_stereo) {
uint32_t left_bps = 0, right_bps = 0; /* initialized only to prevent superfluous compiler warning */
FLAC__Subframe *left_subframe = 0, *right_subframe = 0; /* initialized only to prevent superfluous compiler warning */
FLAC__ChannelAssignment channel_assignment;

FLAC__ASSERT(encoder->protected_->channels == 2);

if(encoder->protected_->loose_mid_side_stereo && encoder->private_->loose_mid_side_stereo_frame_count > 0) {
channel_assignment = (encoder->private_->last_channel_assignment == FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT? FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT : FLAC__CHANNEL_ASSIGNMENT_MID_SIDE);
}
else {
if(!encoder->protected_->loose_mid_side_stereo) {
uint32_t bits[4]; /* WATCHOUT - indexed by FLAC__ChannelAssignment */
uint32_t min_bits;
int ca;
Expand All @@ -3927,7 +3921,6 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__StreamEncoderT
FLAC__ASSERT(FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE == 1);
FLAC__ASSERT(FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE == 2);
FLAC__ASSERT(FLAC__CHANNEL_ASSIGNMENT_MID_SIDE == 3);
FLAC__ASSERT(do_independent && do_mid_side);

/* We have to figure out which channel assignent results in the smallest frame */
bits[FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT] = threadtask->best_subframe_bits [0] + threadtask->best_subframe_bits [1];
Expand All @@ -3940,23 +3933,21 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__StreamEncoderT

/* When doing loose mid-side stereo, ignore left-side
* and right-side options */
ca = encoder->protected_->loose_mid_side_stereo ? 3 : 1;
for( ; ca <= 3; ca++) {
for(ca = 1; ca <= 3; ca++) {
if(bits[ca] < min_bits) {
min_bits = bits[ca];
channel_assignment = (FLAC__ChannelAssignment)ca;
}
}
frame_header.channel_assignment = channel_assignment;
}

frame_header.channel_assignment = channel_assignment;

if(!FLAC__frame_add_header(&frame_header, threadtask->frame)) {
encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
return false;
}

switch(channel_assignment) {
switch(frame_header.channel_assignment) {
case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT:
left_subframe = &threadtask->subframe_workspace [0][threadtask->best_subframe [0]];
right_subframe = &threadtask->subframe_workspace [1][threadtask->best_subframe [1]];
Expand All @@ -3977,7 +3968,7 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__StreamEncoderT
FLAC__ASSERT(0);
}

switch(channel_assignment) {
switch(frame_header.channel_assignment) {
case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT:
left_bps = threadtask->subframe_bps [0];
right_bps = threadtask->subframe_bps [1];
Expand Down Expand Up @@ -4005,6 +3996,7 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__StreamEncoderT
return false;
}
else {
FLAC__ASSERT(do_independent);
if(!FLAC__frame_add_header(&frame_header, threadtask->frame)) {
encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
return false;
Expand All @@ -4018,13 +4010,6 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__StreamEncoderT
}
}

if(encoder->protected_->loose_mid_side_stereo) {
encoder->private_->loose_mid_side_stereo_frame_count++;
if(encoder->private_->loose_mid_side_stereo_frame_count >= encoder->private_->loose_mid_side_stereo_frames)
encoder->private_->loose_mid_side_stereo_frame_count = 0;
encoder->private_->last_channel_assignment = frame_header.channel_assignment;
}

return true;
}

Expand Down

0 comments on commit d2b2441

Please sign in to comment.