static int Open( vlc_object_t *p_obj, bool b_change_ratio ) { filter_t *p_filter = (filter_t *)p_obj; /* Cannot remix */ if( p_filter->fmt_in.audio.i_channels != p_filter->fmt_out.audio.i_channels ) return VLC_EGENERIC; /* Get SoXR input/output format */ soxr_datatype_t i_itype, i_otype; if( !SoXR_GetFormat( p_filter->fmt_in.audio.i_format, &i_itype ) || !SoXR_GetFormat( p_filter->fmt_out.audio.i_format, &i_otype ) ) return VLC_EGENERIC; filter_sys_t *p_sys = calloc( 1, sizeof( filter_sys_t ) ); if( unlikely( p_sys == NULL ) ) return VLC_ENOMEM; /* Setup SoXR */ int64_t i_vlc_q = var_InheritInteger( p_obj, "soxr-resampler-quality" ); if( i_vlc_q < 0 ) i_vlc_q = 0; else if( i_vlc_q > MAX_SOXR_QUALITY ) i_vlc_q = MAX_SOXR_QUALITY; const unsigned long i_recipe = soxr_resampler_quality_list[i_vlc_q]; const unsigned i_channels = p_filter->fmt_in.audio.i_channels; const double f_ratio = p_filter->fmt_out.audio.i_rate / (double) p_filter->fmt_in.audio.i_rate; p_sys->f_fixed_ratio = f_ratio; soxr_error_t error; /* IO spec */ soxr_io_spec_t io_spec = soxr_io_spec( i_itype, i_otype ); /* Quality spec */ soxr_quality_spec_t q_spec = soxr_quality_spec( i_recipe, 0 ); /* Create SoXR */ p_sys->soxr = soxr_create( 1, f_ratio, i_channels, &error, &io_spec, &q_spec, NULL ); if( error ) { msg_Err( p_filter, "soxr_create failed: %s", soxr_strerror( error ) ); free( p_sys ); return VLC_EGENERIC; } /* Create a 'variable-rate' SoXR if needed: it is slower than the fixed * one, but it will be only used when the input rate is changing (to catch * up a delay). */ if( b_change_ratio ) { q_spec = soxr_quality_spec( SOXR_LQ, SOXR_VR ); p_sys->vr_soxr = soxr_create( 1, f_ratio, i_channels, &error, &io_spec, &q_spec, NULL ); if( error ) { msg_Err( p_filter, "soxr_create failed: %s", soxr_strerror( error ) ); soxr_delete( p_sys->soxr ); free( p_sys ); return VLC_EGENERIC; } soxr_set_io_ratio( p_sys->vr_soxr, 1 / f_ratio, 0 ); } msg_Dbg( p_filter, "Using SoX Resampler with '%s' engine and '%s' quality " "to convert %4.4s/%dHz to %4.4s/%dHz.", soxr_engine( p_sys->soxr ), soxr_resampler_quality_vlctext[i_vlc_q], (const char *)&p_filter->fmt_in.audio.i_format, p_filter->fmt_in.audio.i_rate, (const char *)&p_filter->fmt_out.audio.i_format, p_filter->fmt_out.audio.i_rate ); p_filter->p_sys = p_sys; p_filter->pf_audio_filter = Resample; p_filter->pf_flush = Flush; p_filter->pf_audio_drain = Drain; return VLC_SUCCESS; }
static block_t * Resample( filter_t *p_filter, block_t *p_in ) { filter_sys_t *p_sys = p_filter->p_sys; const vlc_tick_t i_pts = p_in->i_pts; if( p_sys->vr_soxr ) { /* "audio resampler" with variable ratio: use the fixed resampler when * the ratio is the same than the fixed one, otherwise use the variable * resampler. */ soxr_t soxr; block_t *p_flushed_out = NULL, *p_out = NULL; const double f_ratio = p_filter->fmt_out.audio.i_rate / (double) p_filter->fmt_in.audio.i_rate; const size_t i_olen = SoXR_GetOutLen( p_in->i_nb_samples, f_ratio ); if( f_ratio != p_sys->f_fixed_ratio ) { /* using variable resampler */ soxr_set_io_ratio( p_sys->vr_soxr, 1 / f_ratio, i_olen ); soxr = p_sys->vr_soxr; } else if( f_ratio == 1.0f ) { /* not using any resampler */ soxr = NULL; p_out = p_in; } else { /* using fixed resampler */ soxr = p_sys->soxr; } /* If the new soxr is different than the last one, flush it */ if( p_sys->last_soxr && soxr != p_sys->last_soxr && p_sys->i_last_olen ) { p_flushed_out = SoXR_Resample( p_filter, p_sys->last_soxr, NULL, p_sys->i_last_olen ); if( soxr ) msg_Dbg( p_filter, "Using '%s' engine", soxr_engine( soxr ) ); } if( soxr ) { assert( !p_out ); p_out = SoXR_Resample( p_filter, soxr, p_in, i_olen ); if( !p_out ) goto error; } if( p_flushed_out ) { /* Prepend the flushed output data to p_out */ const unsigned i_nb_samples = p_flushed_out->i_nb_samples + p_out->i_nb_samples; block_ChainAppend( &p_flushed_out, p_out ); p_out = block_ChainGather( p_flushed_out ); if( !p_out ) goto error; p_out->i_nb_samples = i_nb_samples; } p_out->i_pts = i_pts; return p_out; } else { /* "audio converter" with fixed ratio */ const size_t i_olen = SoXR_GetOutLen( p_in->i_nb_samples, p_sys->f_fixed_ratio ); block_t *p_out = SoXR_Resample( p_filter, p_sys->soxr, p_in, i_olen ); if( p_out ) p_out->i_pts = i_pts; return p_out; } error: block_Release( p_in ); return NULL; }
int main(int n, char const * arg[]) { char const * const arg0 = n? --n, *arg++ : "", * engine = ""; double const irate = n? --n, atof(*arg++) : 96000.; double const orate = n? --n, atof(*arg++) : 44100.; unsigned const chans = n? --n, (unsigned)atoi(*arg++) : 1; soxr_datatype_t const itype = n? --n, (soxr_datatype_t)atoi(*arg++) : 0; unsigned const ospec = n? --n, (soxr_datatype_t)atoi(*arg++) : 0; unsigned long const q_recipe= n? --n, strtoul(*arg++, 0, 16) : SOXR_HQ; unsigned long const q_flags = n? --n, strtoul(*arg++, 0, 16) : 0; double const passband_end = n? --n, atof(*arg++) : 0; double const stopband_begin = n? --n, atof(*arg++) : 0; double const phase_response = n? --n, atof(*arg++) : -1; int const use_threads = n? --n, atoi(*arg++) : 1; soxr_datatype_t const otype = ospec & 3; soxr_quality_spec_t q_spec = soxr_quality_spec(q_recipe, q_flags); soxr_io_spec_t io_spec = soxr_io_spec(itype, otype); soxr_runtime_spec_t const runtime_spec = soxr_runtime_spec(!use_threads); /* Allocate resampling input and output buffers in proportion to the input * and output rates: */ #define buf_total_len 15000 /* In samples per channel. */ size_t const osize = soxr_datatype_size(otype) * chans; size_t const isize = soxr_datatype_size(itype) * chans; size_t const olen0= (size_t)(orate * buf_total_len / (irate + orate) + .5); size_t const olen = min(max(olen0, 1), buf_total_len - 1); size_t const ilen = buf_total_len - olen; void * const obuf = malloc(osize * olen); void * const ibuf = malloc(isize * ilen); size_t odone = 0, clips = 0, omax = 0, i; soxr_error_t error; soxr_t soxr; int32_t seed = 0; char const * e = getenv("SOXR_THROUGHPUT_GAIN"); double gain = e? atof(e) : .5; /* Overrides (if given): */ if (passband_end > 0) q_spec.passband_end = passband_end / 100; if (stopband_begin > 0) q_spec.stopband_begin = stopband_begin / 100; if (phase_response >=0) q_spec.phase_response = phase_response; io_spec.flags = ospec & ~7u; /* Create a stream resampler: */ soxr = soxr_create( irate, orate, chans, /* Input rate, output rate, # of channels. */ &error, /* To report any error during creation. */ &io_spec, &q_spec, &runtime_spec); #define ranqd1(x) ((x) = 1664525 * (x) + 1013904223) /* int32_t x */ #define dranqd1(x) (ranqd1(x) * (1. / (65536. * 32768.))) /* [-1,1) */ #define RAND (dranqd1(seed) * gain) #define DURATION_MSECS 125 #define NUM_ATTEMPTS 8 if (!error) { /* If all is well, run the resampler: */ engine = soxr_engine(soxr); switch (itype & 3) { case 0: for (i=0;i<ilen*chans; ((float *)ibuf)[i]=(float )RAND, ++i); break; case 1: for (i=0;i<ilen*chans; ((double *)ibuf)[i]=(double )RAND, ++i); break; case 2: for (i=0;i<ilen*chans; ((int32_t *)ibuf)[i]=rint32(65536.*32768*RAND), ++i); break; case 3: for (i=0;i<ilen*chans; ((int16_t *)ibuf)[i]=rint16( 1.*32768*RAND), ++i); break; } /* Resample in blocks: */ for (i=0; i<NUM_ATTEMPTS; ++i) { size_t itotal = 0, ototal = 0; timerStart(DURATION_MSECS); do { size_t const ilen1 = odone < olen? ilen : 0; error = soxr_process(soxr, ibuf, ilen1, NULL, obuf, olen, &odone); itotal += ilen1; ototal += odone; } while (!error && timerRunning()); omax = max(omax, ototal); } } /* Tidy up: */ clips = *soxr_num_clips(soxr); /* Can occur only with integer output. */ soxr_delete(soxr); free(obuf), free(ibuf); /* Diagnostics: */ fprintf(stderr, "%-26s %s; %lu clips; I/O: %s (%-5s) %.2f Ms/s\n", arg0, soxr_strerror(error), (long unsigned)clips, ferror(stdin) || ferror(stdout)? strerror(errno) : "no error", engine, 1e-6 * k / DURATION_MSECS * chans * (double)omax); return !!error; }