| | 126 | /* Init() and ComputeChannelOperations() - |
|---|
| | 127 | * Code taken from modules/audio_filter/channel_mixer/headphone.c |
|---|
| | 128 | * converted from float into int16_t based downmix |
|---|
| | 129 | * Written by Boris Dorès <babal@via.ecp.fr> |
|---|
| | 130 | */ |
|---|
| | 131 | |
|---|
| | 132 | /***************************************************************************** |
|---|
| | 133 | * Init: initialize internal data structures |
|---|
| | 134 | * and computes the needed atomic operations |
|---|
| | 135 | *****************************************************************************/ |
|---|
| | 136 | /* x and z represent the coordinates of the virtual speaker |
|---|
| | 137 | * relatively to the center of the listener's head, measured in meters : |
|---|
| | 138 | * |
|---|
| | 139 | * left right |
|---|
| | 140 | *Z |
|---|
| | 141 | *- |
|---|
| | 142 | *a head |
|---|
| | 143 | *x |
|---|
| | 144 | *i |
|---|
| | 145 | *s |
|---|
| | 146 | * rear left rear right |
|---|
| | 147 | * |
|---|
| | 148 | * x-axis |
|---|
| | 149 | * */ |
|---|
| | 150 | static void ComputeChannelOperations( struct filter_sys_t * p_data, |
|---|
| | 151 | unsigned int i_rate, unsigned int i_next_atomic_operation, |
|---|
| | 152 | int i_source_channel_offset, double d_x, double d_z, |
|---|
| | 153 | double d_compensation_length, double d_channel_amplitude_factor ) |
|---|
| | 154 | { |
|---|
| | 155 | double d_c = 340; /*sound celerity (unit: m/s)*/ |
|---|
| | 156 | double d_compensation_delay = (d_compensation_length-0.1) / d_c * i_rate; |
|---|
| | 157 | |
|---|
| | 158 | /* Left ear */ |
|---|
| | 159 | p_data->p_atomic_operations[i_next_atomic_operation] |
|---|
| | 160 | .i_source_channel_offset = i_source_channel_offset; |
|---|
| | 161 | p_data->p_atomic_operations[i_next_atomic_operation] |
|---|
| | 162 | .i_dest_channel_offset = 0;/* left */ |
|---|
| | 163 | p_data->p_atomic_operations[i_next_atomic_operation] |
|---|
| | 164 | .i_delay = (int)( sqrt( (-0.1-d_x)*(-0.1-d_x) + (0-d_z)*(0-d_z) ) |
|---|
| | 165 | / d_c * i_rate - d_compensation_delay ); |
|---|
| | 166 | if( d_x < 0 ) |
|---|
| | 167 | { |
|---|
| | 168 | p_data->p_atomic_operations[i_next_atomic_operation] |
|---|
| | 169 | .d_amplitude_factor = d_channel_amplitude_factor * 1.1 / 2; |
|---|
| | 170 | } |
|---|
| | 171 | else if( d_x > 0 ) |
|---|
| | 172 | { |
|---|
| | 173 | p_data->p_atomic_operations[i_next_atomic_operation] |
|---|
| | 174 | .d_amplitude_factor = d_channel_amplitude_factor * 0.9 / 2; |
|---|
| | 175 | } |
|---|
| | 176 | else |
|---|
| | 177 | { |
|---|
| | 178 | p_data->p_atomic_operations[i_next_atomic_operation] |
|---|
| | 179 | .d_amplitude_factor = d_channel_amplitude_factor / 2; |
|---|
| | 180 | } |
|---|
| | 181 | |
|---|
| | 182 | /* Right ear */ |
|---|
| | 183 | p_data->p_atomic_operations[i_next_atomic_operation + 1] |
|---|
| | 184 | .i_source_channel_offset = i_source_channel_offset; |
|---|
| | 185 | p_data->p_atomic_operations[i_next_atomic_operation + 1] |
|---|
| | 186 | .i_dest_channel_offset = 1;/* right */ |
|---|
| | 187 | p_data->p_atomic_operations[i_next_atomic_operation + 1] |
|---|
| | 188 | .i_delay = (int)( sqrt( (0.1-d_x)*(0.1-d_x) + (0-d_z)*(0-d_z) ) |
|---|
| | 189 | / d_c * i_rate - d_compensation_delay ); |
|---|
| | 190 | if( d_x < 0 ) |
|---|
| | 191 | { |
|---|
| | 192 | p_data->p_atomic_operations[i_next_atomic_operation + 1] |
|---|
| | 193 | .d_amplitude_factor = d_channel_amplitude_factor * 0.9 / 2; |
|---|
| | 194 | } |
|---|
| | 195 | else if( d_x > 0 ) |
|---|
| | 196 | { |
|---|
| | 197 | p_data->p_atomic_operations[i_next_atomic_operation + 1] |
|---|
| | 198 | .d_amplitude_factor = d_channel_amplitude_factor * 1.1 / 2; |
|---|
| | 199 | } |
|---|
| | 200 | else |
|---|
| | 201 | { |
|---|
| | 202 | p_data->p_atomic_operations[i_next_atomic_operation + 1] |
|---|
| | 203 | .d_amplitude_factor = d_channel_amplitude_factor / 2; |
|---|
| | 204 | } |
|---|
| | 205 | } |
|---|
| | 206 | |
|---|
| | 207 | static int Init( vlc_object_t *p_this, struct filter_sys_t * p_data, |
|---|
| | 208 | unsigned int i_nb_channels, uint32_t i_physical_channels, |
|---|
| | 209 | unsigned int i_rate ) |
|---|
| | 210 | { |
|---|
| | 211 | double d_x = config_GetInt( p_this, "headphone-dim" ); |
|---|
| | 212 | double d_z = d_x; |
|---|
| | 213 | double d_z_rear = -d_x/3; |
|---|
| | 214 | double d_min = 0; |
|---|
| | 215 | unsigned int i_next_atomic_operation; |
|---|
| | 216 | int i_source_channel_offset; |
|---|
| | 217 | unsigned int i; |
|---|
| | 218 | |
|---|
| | 219 | if( p_data == NULL ) |
|---|
| | 220 | { |
|---|
| | 221 | msg_Dbg( p_this, "passing a null pointer as argument" ); |
|---|
| | 222 | return 0; |
|---|
| | 223 | } |
|---|
| | 224 | |
|---|
| | 225 | if( config_GetInt( p_this, "headphone-compensate" ) ) |
|---|
| | 226 | { |
|---|
| | 227 | /* minimal distance to any speaker */ |
|---|
| | 228 | if( i_physical_channels & AOUT_CHAN_REARCENTER ) |
|---|
| | 229 | { |
|---|
| | 230 | d_min = d_z_rear; |
|---|
| | 231 | } |
|---|
| | 232 | else |
|---|
| | 233 | { |
|---|
| | 234 | d_min = d_z; |
|---|
| | 235 | } |
|---|
| | 236 | } |
|---|
| | 237 | |
|---|
| | 238 | /* Number of elementary operations */ |
|---|
| | 239 | p_data->i_nb_atomic_operations = i_nb_channels * 2; |
|---|
| | 240 | if( i_physical_channels & AOUT_CHAN_CENTER ) |
|---|
| | 241 | { |
|---|
| | 242 | p_data->i_nb_atomic_operations += 2; |
|---|
| | 243 | } |
|---|
| | 244 | p_data->p_atomic_operations = malloc( sizeof(struct atomic_operation_t) |
|---|
| | 245 | * p_data->i_nb_atomic_operations ); |
|---|
| | 246 | if( p_data->p_atomic_operations == NULL ) |
|---|
| | 247 | { |
|---|
| | 248 | msg_Err( p_this, "out of memory" ); |
|---|
| | 249 | return -1; |
|---|
| | 250 | } |
|---|
| | 251 | |
|---|
| | 252 | /* For each virtual speaker, computes elementary wave propagation time |
|---|
| | 253 | * to each ear */ |
|---|
| | 254 | i_next_atomic_operation = 0; |
|---|
| | 255 | i_source_channel_offset = 0; |
|---|
| | 256 | if( i_physical_channels & AOUT_CHAN_LEFT ) |
|---|
| | 257 | { |
|---|
| | 258 | ComputeChannelOperations( p_data , i_rate |
|---|
| | 259 | , i_next_atomic_operation , i_source_channel_offset |
|---|
| | 260 | , -d_x , d_z , d_min , 2.0 / i_nb_channels ); |
|---|
| | 261 | i_next_atomic_operation += 2; |
|---|
| | 262 | i_source_channel_offset++; |
|---|
| | 263 | } |
|---|
| | 264 | if( i_physical_channels & AOUT_CHAN_RIGHT ) |
|---|
| | 265 | { |
|---|
| | 266 | ComputeChannelOperations( p_data , i_rate |
|---|
| | 267 | , i_next_atomic_operation , i_source_channel_offset |
|---|
| | 268 | , d_x , d_z , d_min , 2.0 / i_nb_channels ); |
|---|
| | 269 | i_next_atomic_operation += 2; |
|---|
| | 270 | i_source_channel_offset++; |
|---|
| | 271 | } |
|---|
| | 272 | if( i_physical_channels & AOUT_CHAN_MIDDLELEFT ) |
|---|
| | 273 | { |
|---|
| | 274 | ComputeChannelOperations( p_data , i_rate |
|---|
| | 275 | , i_next_atomic_operation , i_source_channel_offset |
|---|
| | 276 | , -d_x , 0 , d_min , 1.5 / i_nb_channels ); |
|---|
| | 277 | i_next_atomic_operation += 2; |
|---|
| | 278 | i_source_channel_offset++; |
|---|
| | 279 | } |
|---|
| | 280 | if( i_physical_channels & AOUT_CHAN_MIDDLERIGHT ) |
|---|
| | 281 | { |
|---|
| | 282 | ComputeChannelOperations( p_data , i_rate |
|---|
| | 283 | , i_next_atomic_operation , i_source_channel_offset |
|---|
| | 284 | , d_x , 0 , d_min , 1.5 / i_nb_channels ); |
|---|
| | 285 | i_next_atomic_operation += 2; |
|---|
| | 286 | i_source_channel_offset++; |
|---|
| | 287 | } |
|---|
| | 288 | if( i_physical_channels & AOUT_CHAN_REARLEFT ) |
|---|
| | 289 | { |
|---|
| | 290 | ComputeChannelOperations( p_data , i_rate |
|---|
| | 291 | , i_next_atomic_operation , i_source_channel_offset |
|---|
| | 292 | , -d_x , d_z_rear , d_min , 1.5 / i_nb_channels ); |
|---|
| | 293 | i_next_atomic_operation += 2; |
|---|
| | 294 | i_source_channel_offset++; |
|---|
| | 295 | } |
|---|
| | 296 | if( i_physical_channels & AOUT_CHAN_REARRIGHT ) |
|---|
| | 297 | { |
|---|
| | 298 | ComputeChannelOperations( p_data , i_rate |
|---|
| | 299 | , i_next_atomic_operation , i_source_channel_offset |
|---|
| | 300 | , d_x , d_z_rear , d_min , 1.5 / i_nb_channels ); |
|---|
| | 301 | i_next_atomic_operation += 2; |
|---|
| | 302 | i_source_channel_offset++; |
|---|
| | 303 | } |
|---|
| | 304 | if( i_physical_channels & AOUT_CHAN_REARCENTER ) |
|---|
| | 305 | { |
|---|
| | 306 | ComputeChannelOperations( p_data , i_rate |
|---|
| | 307 | , i_next_atomic_operation , i_source_channel_offset |
|---|
| | 308 | , 0 , -d_z , d_min , 1.5 / i_nb_channels ); |
|---|
| | 309 | i_next_atomic_operation += 2; |
|---|
| | 310 | i_source_channel_offset++; |
|---|
| | 311 | } |
|---|
| | 312 | if( i_physical_channels & AOUT_CHAN_CENTER ) |
|---|
| | 313 | { |
|---|
| | 314 | /* having two center channels increases the spatialization effect */ |
|---|
| | 315 | ComputeChannelOperations( p_data , i_rate |
|---|
| | 316 | , i_next_atomic_operation , i_source_channel_offset |
|---|
| | 317 | , d_x / 5.0 , d_z , d_min , 0.75 / i_nb_channels ); |
|---|
| | 318 | i_next_atomic_operation += 2; |
|---|
| | 319 | ComputeChannelOperations( p_data , i_rate |
|---|
| | 320 | , i_next_atomic_operation , i_source_channel_offset |
|---|
| | 321 | , -d_x / 5.0 , d_z , d_min , 0.75 / i_nb_channels ); |
|---|
| | 322 | i_next_atomic_operation += 2; |
|---|
| | 323 | i_source_channel_offset++; |
|---|
| | 324 | } |
|---|
| | 325 | if( i_physical_channels & AOUT_CHAN_LFE ) |
|---|
| | 326 | { |
|---|
| | 327 | ComputeChannelOperations( p_data , i_rate |
|---|
| | 328 | , i_next_atomic_operation , i_source_channel_offset |
|---|
| | 329 | , 0 , d_z_rear , d_min , 5.0 / i_nb_channels ); |
|---|
| | 330 | i_next_atomic_operation += 2; |
|---|
| | 331 | i_source_channel_offset++; |
|---|
| | 332 | } |
|---|
| | 333 | |
|---|
| | 334 | /* Initialize the overflow buffer |
|---|
| | 335 | * we need it because the process induce a delay in the samples */ |
|---|
| | 336 | p_data->i_overflow_buffer_size = 0; |
|---|
| | 337 | for( i = 0 ; i < p_data->i_nb_atomic_operations ; i++ ) |
|---|
| | 338 | { |
|---|
| | 339 | if( p_data->i_overflow_buffer_size |
|---|
| | 340 | < p_data->p_atomic_operations[i].i_delay * 2 * sizeof (int16_t) ) |
|---|
| | 341 | { |
|---|
| | 342 | p_data->i_overflow_buffer_size |
|---|
| | 343 | = p_data->p_atomic_operations[i].i_delay * 2 * sizeof (int16_t); |
|---|
| | 344 | } |
|---|
| | 345 | } |
|---|
| | 346 | p_data->p_overflow_buffer = malloc( p_data->i_overflow_buffer_size ); |
|---|
| | 347 | if( p_data->p_atomic_operations == NULL ) |
|---|
| | 348 | { |
|---|
| | 349 | msg_Err( p_this, "out of memory" ); |
|---|
| | 350 | return -1; |
|---|
| | 351 | } |
|---|
| | 352 | memset( p_data->p_overflow_buffer, 0 , p_data->i_overflow_buffer_size ); |
|---|
| | 353 | |
|---|
| | 354 | /* end */ |
|---|
| | 355 | return 0; |
|---|
| | 356 | } |
|---|
| | 357 | |
|---|
| | 543 | static void stereo2mono_downmix( aout_instance_t * p_aout, aout_filter_t * p_filter, |
|---|
| | 544 | aout_buffer_t * p_in_buf, aout_buffer_t * p_out_buf ) |
|---|
| | 545 | { |
|---|
| | 546 | filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys; |
|---|
| | 547 | |
|---|
| | 548 | int i_input_nb = aout_FormatNbChannels( &p_filter->input ); |
|---|
| | 549 | int i_output_nb = aout_FormatNbChannels( &p_filter->output ); |
|---|
| | 550 | |
|---|
| | 551 | int16_t * p_in = (int16_t*) p_in_buf->p_buffer; |
|---|
| | 552 | byte_t * p_out; |
|---|
| | 553 | byte_t * p_overflow; |
|---|
| | 554 | byte_t * p_slide; |
|---|
| | 555 | |
|---|
| | 556 | size_t i_overflow_size; /* in bytes */ |
|---|
| | 557 | size_t i_out_size; /* in bytes */ |
|---|
| | 558 | |
|---|
| | 559 | unsigned int i, j; |
|---|
| | 560 | |
|---|
| | 561 | int i_source_channel_offset; |
|---|
| | 562 | int i_dest_channel_offset; |
|---|
| | 563 | unsigned int i_delay; |
|---|
| | 564 | double d_amplitude_factor; |
|---|
| | 565 | |
|---|
| | 566 | /* out buffer characterisitcs */ |
|---|
| | 567 | p_out_buf->i_nb_samples = p_in_buf->i_nb_samples; |
|---|
| | 568 | p_out_buf->i_nb_bytes = p_in_buf->i_nb_bytes * i_output_nb / i_input_nb; |
|---|
| | 569 | p_out = p_out_buf->p_buffer; |
|---|
| | 570 | i_out_size = p_out_buf->i_nb_bytes; |
|---|
| | 571 | |
|---|
| | 572 | if( p_sys != NULL ) |
|---|
| | 573 | { |
|---|
| | 574 | /* Slide the overflow buffer */ |
|---|
| | 575 | p_overflow = p_sys->p_overflow_buffer; |
|---|
| | 576 | i_overflow_size = p_sys->i_overflow_buffer_size; |
|---|
| | 577 | |
|---|
| | 578 | memset( p_out, 0, i_out_size ); |
|---|
| | 579 | if ( i_out_size > i_overflow_size ) |
|---|
| | 580 | memcpy( p_out, p_overflow, i_overflow_size ); |
|---|
| | 581 | else |
|---|
| | 582 | memcpy( p_out, p_overflow, i_out_size ); |
|---|
| | 583 | |
|---|
| | 584 | p_slide = p_sys->p_overflow_buffer; |
|---|
| | 585 | while( p_slide < p_overflow + i_overflow_size ) |
|---|
| | 586 | { |
|---|
| | 587 | if( p_slide + i_out_size < p_overflow + i_overflow_size ) |
|---|
| | 588 | { |
|---|
| | 589 | memset( p_slide, 0, i_out_size ); |
|---|
| | 590 | if( p_slide + 2 * i_out_size < p_overflow + i_overflow_size ) |
|---|
| | 591 | memcpy( p_slide, p_slide + i_out_size, i_out_size ); |
|---|
| | 592 | else |
|---|
| | 593 | memcpy( p_slide, p_slide + i_out_size, |
|---|
| | 594 | p_overflow + i_overflow_size - ( p_slide + i_out_size ) ); |
|---|
| | 595 | } |
|---|
| | 596 | else |
|---|
| | 597 | { |
|---|
| | 598 | memset( p_slide, 0, p_overflow + i_overflow_size - p_slide ); |
|---|
| | 599 | } |
|---|
| | 600 | p_slide += i_out_size; |
|---|
| | 601 | } |
|---|
| | 602 | |
|---|
| | 603 | /* apply the atomic operations */ |
|---|
| | 604 | for( i = 0; i < p_sys->i_nb_atomic_operations; i++ ) |
|---|
| | 605 | { |
|---|
| | 606 | /* shorter variable names */ |
|---|
| | 607 | i_source_channel_offset |
|---|
| | 608 | = p_sys->p_atomic_operations[i].i_source_channel_offset; |
|---|
| | 609 | i_dest_channel_offset |
|---|
| | 610 | = p_sys->p_atomic_operations[i].i_dest_channel_offset; |
|---|
| | 611 | i_delay = p_sys->p_atomic_operations[i].i_delay; |
|---|
| | 612 | d_amplitude_factor |
|---|
| | 613 | = p_sys->p_atomic_operations[i].d_amplitude_factor; |
|---|
| | 614 | |
|---|
| | 615 | if( p_out_buf->i_nb_samples > i_delay ) |
|---|
| | 616 | { |
|---|
| | 617 | /* current buffer coefficients */ |
|---|
| | 618 | for( j = 0; j < p_out_buf->i_nb_samples - i_delay; j++ ) |
|---|
| | 619 | { |
|---|
| | 620 | ((int16_t*)p_out)[ (i_delay+j)*i_output_nb + i_dest_channel_offset ] |
|---|
| | 621 | += p_in[ j * i_input_nb + i_source_channel_offset ] |
|---|
| | 622 | * d_amplitude_factor; |
|---|
| | 623 | } |
|---|
| | 624 | |
|---|
| | 625 | /* overflow buffer coefficients */ |
|---|
| | 626 | for( j = 0; j < i_delay; j++ ) |
|---|
| | 627 | { |
|---|
| | 628 | ((int16_t*)p_overflow)[ j*i_output_nb + i_dest_channel_offset ] |
|---|
| | 629 | += p_in[ (p_out_buf->i_nb_samples - i_delay + j) |
|---|
| | 630 | * i_input_nb + i_source_channel_offset ] |
|---|
| | 631 | * d_amplitude_factor; |
|---|
| | 632 | } |
|---|
| | 633 | } |
|---|
| | 634 | else |
|---|
| | 635 | { |
|---|
| | 636 | /* overflow buffer coefficients only */ |
|---|
| | 637 | for( j = 0; j < p_out_buf->i_nb_samples; j++ ) |
|---|
| | 638 | { |
|---|
| | 639 | ((int16_t*)p_overflow)[ (i_delay - p_out_buf->i_nb_samples + j) |
|---|
| | 640 | * i_output_nb + i_dest_channel_offset ] |
|---|
| | 641 | += p_in[ j * i_input_nb + i_source_channel_offset ] |
|---|
| | 642 | * d_amplitude_factor; |
|---|
| | 643 | } |
|---|
| | 644 | } |
|---|
| | 645 | } |
|---|
| | 646 | } |
|---|
| | 647 | else |
|---|
| | 648 | { |
|---|
| | 649 | memset( p_out, 0, i_out_size ); |
|---|
| | 650 | } |
|---|
| | 651 | } |
|---|
| | 652 | |
|---|
| | 653 | /* Simple stereo to mono mixing. */ |
|---|