コード例 #1
0
int create_resampler(uint32_t inSampleRate,
                    uint32_t outSampleRate,
                    uint32_t channelCount,
                    uint32_t quality,
                    struct resampler_buffer_provider* provider,
                    struct resampler_itfe **resampler)
{
    int error;
    struct resampler *rsmp;

    ALOGV("create_resampler() In SR %d Out SR %d channels %d",
         inSampleRate, outSampleRate, channelCount);

    if (resampler == NULL) {
        return -EINVAL;
    }

    *resampler = NULL;

    if (quality <= RESAMPLER_QUALITY_MIN || quality >= RESAMPLER_QUALITY_MAX) {
        return -EINVAL;
    }

    rsmp = (struct resampler *)calloc(1, sizeof(struct resampler));

    rsmp->speex_resampler = speex_resampler_init(channelCount,
                                      inSampleRate,
                                      outSampleRate,
                                      quality,
                                      &error);
    if (rsmp->speex_resampler == NULL) {
        ALOGW("ReSampler: Cannot create speex resampler: %s", speex_resampler_strerror(error));
        free(rsmp);
        return -ENODEV;
    }

    rsmp->itfe.reset = resampler_reset;
    rsmp->itfe.resample_from_provider = resampler_resample_from_provider;
    rsmp->itfe.resample_from_input = resampler_resample_from_input;
    rsmp->itfe.delay_ns = resampler_delay_ns;

    rsmp->provider = provider;
    rsmp->in_sample_rate = inSampleRate;
    rsmp->out_sample_rate = outSampleRate;
    rsmp->channel_count = channelCount;
    rsmp->in_buf = NULL;
    rsmp->in_buf_size = 0;

    resampler_reset(&rsmp->itfe);

    int frames = speex_resampler_get_input_latency(rsmp->speex_resampler);
    rsmp->speex_delay_ns = (int32_t)((1000000000 * (int64_t)frames) / rsmp->in_sample_rate);
    frames = speex_resampler_get_output_latency(rsmp->speex_resampler);
    rsmp->speex_delay_ns += (int32_t)((1000000000 * (int64_t)frames) / rsmp->out_sample_rate);

    *resampler = &rsmp->itfe;
    ALOGV("create_resampler() DONE rsmp %p &rsmp->itfe %p speex %p",
         rsmp, &rsmp->itfe, rsmp->speex_resampler);
    return 0;
}
コード例 #2
0
ファイル: speex.c プロジェクト: 0xheart0/vlc
static block_t *Resample (filter_t *filter, block_t *in)
{
    SpeexResamplerState *st = (SpeexResamplerState *)filter->p_sys;

    const size_t framesize = filter->fmt_out.audio.i_bytes_per_frame;
    const unsigned irate = filter->fmt_in.audio.i_rate;
    const unsigned orate = filter->fmt_out.audio.i_rate;

    spx_uint32_t ilen = in->i_nb_samples;
    spx_uint32_t olen = ((ilen + 2) * orate * UINT64_C(11))
                      / (irate * UINT64_C(10));

    block_t *out = block_Alloc (olen * framesize);
    if (unlikely(out == NULL))
        goto error;

    speex_resampler_set_rate (st, irate, orate);

    int err;
    if (filter->fmt_in.audio.i_format == VLC_CODEC_FL32)
        err = speex_resampler_process_interleaved_float (st,
            (float *)in->p_buffer, &ilen, (float *)out->p_buffer, &olen);
    else
        err = speex_resampler_process_interleaved_int (st,
            (int16_t *)in->p_buffer, &ilen, (int16_t *)out->p_buffer, &olen);
    if (err != 0)
    {
        msg_Err (filter, "cannot resample: %s",
                 speex_resampler_strerror (err));
        block_Release (out);
        out = NULL;
        goto error;
    }

    if (ilen < in->i_nb_samples)
        msg_Err (filter, "lost %"PRIu32" of %u input frames",
                 in->i_nb_samples - ilen, in->i_nb_samples);

    out->i_buffer = olen * framesize;
    out->i_nb_samples = olen;
    out->i_pts = in->i_pts;
    out->i_length = olen * CLOCK_FREQ / filter->fmt_out.audio.i_rate;
error:
    block_Release (in);
    return out;
}
コード例 #3
0
ファイル: speex.c プロジェクト: RodrigoNieves/vlc
static int Open (vlc_object_t *obj)
{
    filter_t *filter = (filter_t *)obj;

    /* Will change rate */
    if (filter->fmt_in.audio.i_rate == filter->fmt_out.audio.i_rate
    /* Cannot convert format */
     || filter->fmt_in.audio.i_format != filter->fmt_out.audio.i_format
    /* Cannot remix */
     || filter->fmt_in.audio.i_physical_channels
                                  != filter->fmt_out.audio.i_physical_channels
     || filter->fmt_in.audio.i_original_channels
                                  != filter->fmt_out.audio.i_original_channels)
        return VLC_EGENERIC;

    switch (filter->fmt_in.audio.i_format)
    {
        case VLC_CODEC_FL32: break;
        case VLC_CODEC_S16N: break;
        default:             return VLC_EGENERIC;
    }

    SpeexResamplerState *st;

    unsigned channels = aout_FormatNbChannels (&filter->fmt_in.audio);
    unsigned q = var_InheritInteger (obj, "speex-resampler-quality");
    if (unlikely(q > 10))
        q = 3;

    int err;
    st = speex_resampler_init(channels, filter->fmt_in.audio.i_rate,
                              filter->fmt_out.audio.i_rate, q, &err);
    if (unlikely(st == NULL))
    {
        msg_Err (obj, "cannot initialize resampler: %s",
                 speex_resampler_strerror (err));
        return VLC_ENOMEM;
    }

    filter->p_sys = (filter_sys_t *)st;
    filter->pf_audio_filter = Resample;
    return VLC_SUCCESS;
}
コード例 #4
0
void BE_ST_InitAudio(void)
{
	g_sdlAudioSubsystemUp = false;
	g_sdlEmulatedOPLChipReady = false;
	int inSampleRate = BE_Cross_GetSelectedGameVerSampleRate();
	bool doDigitized = (inSampleRate != 0);
	if (!doDigitized)
		inSampleRate = OPL_SAMPLE_RATE;

	if (g_refKeenCfg.sndSubSystem)
	{
		if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
		{
			BE_Cross_LogMessage(BE_LOG_MSG_WARNING, "SDL audio system initialization failed,\n%s\n", SDL_GetError());
		}
		else
		{
			g_sdlAudioSpec.freq = g_refKeenCfg.sndSampleRate;
#ifdef MIXER_SAMPLE_FORMAT_FLOAT
			g_sdlAudioSpec.format = AUDIO_F32SYS;
#elif (defined MIXER_SAMPLE_FORMAT_SINT16)
			g_sdlAudioSpec.format = AUDIO_S16SYS;
#endif
			g_sdlAudioSpec.channels = 1;
			// Should be some power-of-two roughly proportional to the sample rate; Using 1024 for 48000Hz.
			for (g_sdlAudioSpec.samples = 1; g_sdlAudioSpec.samples < g_refKeenCfg.sndSampleRate/64; g_sdlAudioSpec.samples *= 2)
			{
			}

			if (doDigitized)
				g_sdlAudioSpec.callback = (g_refKeenCfg.sndSampleRate == inSampleRate) ? BEL_ST_Simple_DigiCallBack : BEL_ST_Resampling_DigiCallBack;
			else
				g_sdlAudioSpec.callback = ((g_refKeenCfg.sndSampleRate == inSampleRate) || !g_refKeenCfg.oplEmulation) ? BEL_ST_Simple_EmuCallBack : BEL_ST_Resampling_EmuCallBack;

			g_sdlAudioSpec.userdata = NULL;
			if (SDL_OpenAudio(&g_sdlAudioSpec, NULL))
			{
				BE_Cross_LogMessage(BE_LOG_MSG_WARNING, "Cannot open SDL audio device,\n%s\n", SDL_GetError());
				SDL_QuitSubSystem(SDL_INIT_AUDIO);
			}
			else
			{
#ifdef REFKEEN_CONFIG_THREADS
				g_sdlCallbackMutex = SDL_CreateMutex();
				if (!g_sdlCallbackMutex)
				{
					BE_Cross_LogMessage(BE_LOG_MSG_ERROR, "Cannot create recursive mutex for SDL audio callback,\n%s\nClosing SDL audio subsystem\n", SDL_GetError());
					SDL_CloseAudio();
					SDL_QuitSubSystem(SDL_INIT_AUDIO);
				}
				else
#endif
				{
					BE_Cross_LogMessage(BE_LOG_MSG_NORMAL, "Audio subsystem initialized, requested spec: freq %d, format %u, channels %d, samples %u\n", (int)g_sdlAudioSpec.freq, (unsigned int)g_sdlAudioSpec.format, (int)g_sdlAudioSpec.channels, (unsigned int)g_sdlAudioSpec.samples);
					g_sdlAudioSubsystemUp = true;
				}
			}
		}
	}
	// If the audio subsystem is off, let us simulate a byte rate
	// of 1000Hz (same as SDL_GetTicks() time units)
	if (!g_sdlAudioSubsystemUp)
	{
		g_sdlAudioSpec.freq = doDigitized ? inSampleRate : (NUM_OF_BYTES_FOR_SOUND_CALLBACK_WITH_DISABLED_SUBSYSTEM / sizeof(BE_ST_SndSample_T));
		g_sdlAudioSpec.callback = doDigitized ? BEL_ST_Resampling_DigiCallBack : BEL_ST_Resampling_EmuCallBack;
		return;
	}

	if (g_refKeenCfg.oplEmulation)
	{
		YM3812Init(1, 3579545, OPL_SAMPLE_RATE);
		g_sdlEmulatedOPLChipReady = true;
	}

	if ((doDigitized || g_sdlEmulatedOPLChipReady) && (g_sdlAudioSpec.freq != inSampleRate))
	{
		// Should allocate this first, for g_sdlSrcData.data_in
		g_sdlMiscOutNumOfSamples = 2*g_sdlAudioSpec.samples;
		g_sdlMiscOutSamples = (BE_ST_SndSample_T *)malloc(sizeof(BE_ST_SndSample_T) * g_sdlMiscOutNumOfSamples); 
		if (g_sdlMiscOutSamples == NULL)
			BE_ST_ExitWithErrorMsg("BE_ST_InitAudio: Out of memory! (Failed to allocate g_sdlMiscOutSamples.)");

#ifndef REFKEEN_RESAMPLER_NONE
		if (g_refKeenCfg.useResampler)
		{
#if (!defined REFKEEN_RESAMPLER_LIBRESAMPLE) && (!defined REFKEEN_RESAMPLER_LIBAVCODEC)
			char errMsg[160];
#endif

#if (defined REFKEEN_RESAMPLER_LIBSWRESAMPLE)
			g_sdlSwrContext = swr_alloc_set_opts(
				NULL,                // allocating a new context
				AV_CH_LAYOUT_MONO,   // out channels layout
				AV_SAMPLE_FMT_S16,   // out format
				g_sdlAudioSpec.freq, // out rate
				AV_CH_LAYOUT_MONO,   // in channels layout
				AV_SAMPLE_FMT_S16,   // in format
				inSampleRate,        // in rate
				0,
				NULL
			);
			if (g_sdlSwrContext == NULL)
				BE_ST_ExitWithErrorMsg("BE_ST_InitAudio: swr_alloc_set_opts failed!");
			int error = swr_init(g_sdlSwrContext);
			if (error != 0)
			{
				// av_err2str requires libavutil/libavutil-ffmpeg, so don't convert code to string
				snprintf(errMsg, sizeof(errMsg), "BE_ST_InitAudio: swr_init failed! Error code: %d", error);
				BE_ST_ExitWithErrorMsg(errMsg);
			}
#elif (defined REFKEEN_RESAMPLER_LIBAVRESAMPLE)
			g_sdlAvAudioResampleContext = avresample_alloc_context();
			if (g_sdlAvAudioResampleContext == NULL)
				BE_ST_ExitWithErrorMsg("BE_ST_InitAudio: avresample_alloc_context failed!");
			av_opt_set_int(g_sdlAvAudioResampleContext, "in_channel_layout",  AV_CH_LAYOUT_MONO,   0);
			av_opt_set_int(g_sdlAvAudioResampleContext, "out_channel_layout", AV_CH_LAYOUT_MONO,   0);
			av_opt_set_int(g_sdlAvAudioResampleContext, "in_sample_rate",     inSampleRate,        0);
			av_opt_set_int(g_sdlAvAudioResampleContext, "out_sample_rate",    g_sdlAudioSpec.freq, 0);
			av_opt_set_int(g_sdlAvAudioResampleContext, "in_sample_fmt",      AV_SAMPLE_FMT_S16,   0);
			av_opt_set_int(g_sdlAvAudioResampleContext, "out_sample_fmt",     AV_SAMPLE_FMT_S16,   0);
			int error = avresample_open(g_sdlAvAudioResampleContext);
			if (error != 0)
			{
				// av_err2str requires libavutil/libavutil-ffmpeg, so don't convert code to string
				snprintf(errMsg, sizeof(errMsg), "BE_ST_InitAudio: swr_init failed! Error code: %d", error);
				BE_ST_ExitWithErrorMsg(errMsg);
			}
#elif (defined REFKEEN_RESAMPLER_LIBAVCODEC)
			avcodec_register_all();
			g_sdlAvResampleContext = av_resample_init(
				g_sdlAudioSpec.freq,	// out rate
				inSampleRate,	// in rate
				16,	// filter length
				10,	// phase count
				0,	// linear FIR filter
				1.0	// cutoff frequency
			);
			if (g_sdlAvResampleContext == NULL)
				BE_ST_ExitWithErrorMsg("BE_ST_InitAudio: av_resample_init failed!");
#elif (defined REFKEEN_RESAMPLER_LIBRESAMPLE)
			g_sdlResampleFactor = (double)g_sdlAudioSpec.freq/inSampleRate;
			g_sdlResampleHandle = resample_open(0, g_sdlResampleFactor, g_sdlResampleFactor);
			if (g_sdlResampleHandle == NULL)
				BE_ST_ExitWithErrorMsg("BE_ST_InitAudio: resample_open failed!");
#elif (defined REFKEEN_RESAMPLER_LIBSOXR)
			soxr_io_spec_t io_spec = soxr_io_spec(SOXR_INT16, SOXR_INT16);
			soxr_quality_spec_t q_spec = soxr_quality_spec(SOXR_LQ, 0); // Default quality spec adds an audible latency for resampling to 8000Hz
			soxr_error_t error;
			g_sdlSoxr = soxr_create(
				inSampleRate, // in rate
				g_sdlAudioSpec.freq, // out rate
				1, // channels
				&error,
				&io_spec,
				&q_spec,
				NULL // runtime spec
			);
			if (g_sdlSoxr == NULL)
			{
				snprintf(errMsg, sizeof(errMsg), "BE_ST_InitAudio: soxr_create failed!\nError: %s", soxr_strerror(error));
				BE_ST_ExitWithErrorMsg(errMsg);
			}
#elif (defined REFKEEN_RESAMPLER_LIBSPEEXDSP)
			int error;
			g_sdlSpeexResamplerState = speex_resampler_init(
				1, // channels
				inSampleRate, // in rate
				g_sdlAudioSpec.freq, // out rate
				0, // quality in the range 0-10 (10 is higher)
				&error
			);
			if (g_sdlSpeexResamplerState == NULL)
			{
				snprintf(errMsg, sizeof(errMsg), "BE_ST_InitAudio: speex_resampler_init failed! Error code: %d\nError: %s", error, speex_resampler_strerror(error));
				BE_ST_ExitWithErrorMsg(errMsg);
			}
#elif (defined REFKEEN_RESAMPLER_LIBSAMPLERATE)
			int error;
			g_sdlSrcResampler = src_new(SRC_SINC_FASTEST, 1, &error);
			if (g_sdlSrcResampler == NULL)
			{
				snprintf(errMsg, sizeof(errMsg), "BE_ST_InitAudio: src_new failed!\nError code: %d", error);
				BE_ST_ExitWithErrorMsg(errMsg);
			}
			g_sdlSrcData.data_in = doDigitized ? g_sdlMiscOutSamples : g_sdlALOutSamples;
			g_sdlSrcData.src_ratio = (double)g_sdlAudioSpec.freq / inSampleRate;
#endif
		}
		else
#endif // REFKEEN_RESAMPLER_NONE
		{
			// The sum of all entries should be g_sdlAudioSpec.freq,
			// "uniformly" distributed over g_sdlALSampleRateConvTable
			g_sdlSampleRateConvTable = (int *)malloc(sizeof(int) * inSampleRate);
			if (g_sdlSampleRateConvTable == NULL)
				BE_ST_ExitWithErrorMsg("BE_ST_InitAudio: Failed to allocate memory for sample rate conversion!");
			g_sdlSampleRateConvTableSize = inSampleRate;
			for (int i = 0; i < inSampleRate; ++i)
			{
				// Using uint64_t cause an overflow is possible
				g_sdlSampleRateConvTable[i] = ((uint64_t)(i+1)*(uint64_t)g_sdlAudioSpec.freq/inSampleRate)-(uint64_t)i*(uint64_t)g_sdlAudioSpec.freq/inSampleRate;
			}
			g_sdlSampleRateConvCurrIndex = 0;
			g_sdlSampleRateConvCounter = 0;
		}
	}
}
コード例 #5
0
int main(int argc, char **argv)
{
   int c;
   int option_index = 0;
   char *inFile, *outFile;
   FILE *fin, *fout=NULL, *frange=NULL;
   float *output;
   int frame_size=0;
   OpusMSDecoder *st=NULL;
   opus_int64 packet_count=0;
   int total_links=0;
   int stream_init = 0;
   int quiet = 0;
   ogg_int64_t page_granule=0;
   ogg_int64_t link_out=0;
   struct option long_options[] =
   {
      {"help", no_argument, NULL, 0},
      {"quiet", no_argument, NULL, 0},
      {"version", no_argument, NULL, 0},
      {"version-short", no_argument, NULL, 0},
      {"rate", required_argument, NULL, 0},
      {"gain", required_argument, NULL, 0},
      {"no-dither", no_argument, NULL, 0},
      {"packet-loss", required_argument, NULL, 0},
      {"save-range", required_argument, NULL, 0},
      {0, 0, 0, 0}
   };
   ogg_sync_state oy;
   ogg_page       og;
   ogg_packet     op;
   ogg_stream_state os;
   int close_in=0;
   int eos=0;
   ogg_int64_t audio_size=0;
   double last_coded_seconds=0;
   float loss_percent=-1;
   float manual_gain=0;
   int channels=-1;
   int mapping_family;
   int rate=0;
   int wav_format=0;
   int preskip=0;
   int gran_offset=0;
   int has_opus_stream=0;
   ogg_int32_t opus_serialno;
   int dither=1;
   shapestate shapemem;
   SpeexResamplerState *resampler=NULL;
   float gain=1;
   int streams=0;
   size_t last_spin=0;
#ifdef WIN_UNICODE
   int argc_utf8;
   char **argv_utf8;
#endif

   if(query_cpu_support()){
     fprintf(stderr,"\n\n** WARNING: This program with compiled with SSE%s\n",query_cpu_support()>1?"2":"");
     fprintf(stderr,"            but this CPU claims to lack these instructions. **\n\n");
   }

#ifdef WIN_UNICODE
   (void)argc;
   (void)argv;

   init_console_utf8();
   init_commandline_arguments_utf8(&argc_utf8, &argv_utf8);
#endif

   output=0;
   shapemem.a_buf=0;
   shapemem.b_buf=0;
   shapemem.mute=960;
   shapemem.fs=0;

   /*Process options*/
   while(1)
   {
      c = getopt_long (argc_utf8, argv_utf8, "hV",
                       long_options, &option_index);
      if (c==-1)
         break;

      switch(c)
      {
      case 0:
         if (strcmp(long_options[option_index].name,"help")==0)
         {
            usage();
            quit(0);
         } else if (strcmp(long_options[option_index].name,"quiet")==0)
         {
            quiet = 1;
         } else if (strcmp(long_options[option_index].name,"version")==0)
         {
            version();
            quit(0);
         } else if (strcmp(long_options[option_index].name,"version-short")==0)
         {
            version_short();
            quit(0);
         } else if (strcmp(long_options[option_index].name,"no-dither")==0)
         {
            dither=0;
         } else if (strcmp(long_options[option_index].name,"rate")==0)
         {
            rate=atoi (optarg);
         } else if (strcmp(long_options[option_index].name,"gain")==0)
         {
            manual_gain=atof (optarg);
         }else if(strcmp(long_options[option_index].name,"save-range")==0){
          frange=fopen_utf8(optarg,"w");
          if(frange==NULL){
            perror(optarg);
            fprintf(stderr,"Could not open save-range file: %s\n",optarg);
            fprintf(stderr,"Must provide a writable file name.\n");
            quit(1);
          }
         } else if (strcmp(long_options[option_index].name,"packet-loss")==0)
         {
            loss_percent = atof(optarg);
         }
         break;
      case 'h':
         usage();
         quit(0);
         break;
      case 'V':
         version();
         quit(0);
         break;
      case '?':
         usage();
         quit(1);
         break;
      }
   }
   if (argc_utf8-optind!=2 && argc_utf8-optind!=1)
   {
      usage();
      quit(1);
   }
   inFile=argv_utf8[optind];

   /*Output to a file or playback?*/
   if (argc_utf8-optind==2){
     /*If we're outputting to a file, should we apply a wav header?*/
     int i;
     char *ext;
     outFile=argv_utf8[optind+1];
     ext=".wav";
     i=strlen(outFile)-4;
     wav_format=i>=0;
     while(wav_format&&ext&&outFile[i]) {
       wav_format&=tolower(outFile[i++])==*ext++;
     }
   }else {
     outFile="";
     wav_format=0;
     /*If playing to audio out, default the rate to 48000
       instead of the original rate. The original rate is
       only important for minimizing surprise about the rate
       of output files and preserving length, which aren't
       relevant for playback. Many audio devices sound
       better at 48kHz and not resampling also saves CPU.*/
     if(rate==0)rate=48000;
   }

   /*Open input file*/
   if (strcmp(inFile, "-")==0)
   {
#if defined WIN32 || defined _WIN32
      _setmode(_fileno(stdin), _O_BINARY);
#endif
      fin=stdin;
   }
   else
   {
      fin = fopen_utf8(inFile, "rb");
      if (!fin)
      {
         perror(inFile);
         quit(1);
      }
      close_in=1;
   }

   /* .opus files use the Ogg container to provide framing and timekeeping.
    * http://tools.ietf.org/html/draft-terriberry-oggopus
    * The easiest way to decode the Ogg container is to use libogg, so
    *  thats what we do here.
    * Using libogg is fairly straight forward-- you take your stream of bytes
    *  and feed them to ogg_sync_ and it periodically returns Ogg pages, you
    *  check if the pages belong to the stream you're decoding then you give
    *  them to libogg and it gives you packets. You decode the packets. The
    *  pages also provide timing information.*/
   ogg_sync_init(&oy);

   /*Main decoding loop*/
   while (1)
   {
      char *data;
      int i, nb_read;
      /*Get the ogg buffer for writing*/
      data = ogg_sync_buffer(&oy, 200);
      /*Read bitstream from input file*/
      nb_read = fread(data, sizeof(char), 200, fin);
      ogg_sync_wrote(&oy, nb_read);

      /*Loop for all complete pages we got (most likely only one)*/
      while (ogg_sync_pageout(&oy, &og)==1)
      {
         if (stream_init == 0) {
            ogg_stream_init(&os, ogg_page_serialno(&og));
            stream_init = 1;
         }
         if (ogg_page_serialno(&og) != os.serialno) {
            /* so all streams are read. */
            ogg_stream_reset_serialno(&os, ogg_page_serialno(&og));
         }
         /*Add page to the bitstream*/
         ogg_stream_pagein(&os, &og);
         page_granule = ogg_page_granulepos(&og);
         /*Extract all available packets*/
         while (ogg_stream_packetout(&os, &op) == 1)
         {
            /*OggOpus streams are identified by a magic string in the initial
              stream header.*/
            if (op.b_o_s && op.bytes>=8 && !memcmp(op.packet, "OpusHead", 8)) {
               if(!has_opus_stream)
               {
                 opus_serialno = os.serialno;
                 has_opus_stream = 1;
                 link_out = 0;
                 packet_count = 0;
                 eos = 0;
                 total_links++;
               } else {
                 fprintf(stderr,"Warning: ignoring opus stream %" I64FORMAT "\n",(long long)os.serialno);
               }
            }
            if (!has_opus_stream || os.serialno != opus_serialno)
               break;
            /*If first packet in a logical stream, process the Opus header*/
            if (packet_count==0)
            {
               st = process_header(&op, &rate, &mapping_family, &channels, &preskip, &gain, manual_gain, &streams, wav_format, quiet);
               if (!st)
                  quit(1);

               /*Remember how many samples at the front we were told to skip
                 so that we can adjust the timestamp counting.*/
               gran_offset=preskip;

               /*Setup the memory for the dithered output*/
               if(!shapemem.a_buf)
               {
                  shapemem.a_buf=calloc(channels,sizeof(float)*4);
                  shapemem.b_buf=calloc(channels,sizeof(float)*4);
                  shapemem.fs=rate;
               }
               if(!output)output=malloc(sizeof(float)*MAX_FRAME_SIZE*channels);

               /*Normal players should just play at 48000 or their maximum rate,
                 as described in the OggOpus spec.  But for commandline tools
                 like opusdec it can be desirable to exactly preserve the original
                 sampling rate and duration, so we have a resampler here.*/
               if (rate != 48000)
               {
                  int err;
                  resampler = speex_resampler_init(channels, 48000, rate, 5, &err);
                  if (err!=0)
                     fprintf(stderr, "resampler error: %s\n", speex_resampler_strerror(err));
                  speex_resampler_skip_zeros(resampler);
               }
               if(!fout)fout=out_file_open(outFile, &wav_format, rate, mapping_family, &channels);
            } else if (packet_count==1)
            {
               if (!quiet)
                  print_comments((char*)op.packet, op.bytes);
            } else {
               int ret;
               opus_int64 maxout;
               opus_int64 outsamp;
               int lost=0;
               if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent)
                  lost=1;

               /*End of stream condition*/
               if (op.e_o_s && os.serialno == opus_serialno)eos=1; /* don't care for anything except opus eos */

               /*Are we simulating loss for this packet?*/
               if (!lost){
                  /*Decode Opus packet*/
                  ret = opus_multistream_decode_float(st, (unsigned char*)op.packet, op.bytes, output, MAX_FRAME_SIZE, 0);
               } else {
                  /*Extract the original duration.
                    Normally you wouldn't have it for a lost packet, but normally the
                    transports used on lossy channels will effectively tell you.
                    This avoids opusdec squaking when the decoded samples and
                    granpos mismatches.*/
                  opus_int32 lost_size;
                  lost_size = MAX_FRAME_SIZE;
                  if(op.bytes>0){
                    opus_int32 spp;
                    spp=opus_packet_get_nb_frames(op.packet,op.bytes);
                    if(spp>0){
                      spp*=opus_packet_get_samples_per_frame(op.packet,48000/*decoding_rate*/);
                      if(spp>0)lost_size=spp;
                    }
                  }
                  /*Invoke packet loss concealment.*/
                  ret = opus_multistream_decode_float(st, NULL, 0, output, lost_size, 0);
               }

               if(!quiet){
                  /*Display a progress spinner while decoding.*/
                  static const char spinner[]="|/-\\";
                  double coded_seconds = (double)audio_size/(channels*rate*sizeof(short));
                  if(coded_seconds>=last_coded_seconds+1){
                     fprintf(stderr,"\r[%c] %02d:%02d:%02d", spinner[last_spin&3],
                             (int)(coded_seconds/3600),(int)(coded_seconds/60)%60,
                             (int)(coded_seconds)%60);
                     fflush(stderr);
                     last_spin++;
                     last_coded_seconds=coded_seconds;
                  }
               }

               /*If the decoder returned less than zero, we have an error.*/
               if (ret<0)
               {
                  fprintf (stderr, "Decoding error: %s\n", opus_strerror(ret));
                  break;
               }
               frame_size = ret;

               /*If we're collecting --save-range debugging data, collect it now.*/
               if(frange!=NULL){
                 OpusDecoder *od;
                 opus_uint32 rngs[256];
                 for(i=0;i<streams;i++){
                   ret=opus_multistream_decoder_ctl(st,OPUS_MULTISTREAM_GET_DECODER_STATE(i,&od));
                   ret=opus_decoder_ctl(od,OPUS_GET_FINAL_RANGE(&rngs[i]));
                 }
                 save_range(frange,frame_size*(48000/48000/*decoding_rate*/),op.packet,op.bytes,
                            rngs,streams);
               }

               /*Apply header gain, if we're not using an opus library new
                 enough to do this internally.*/
               if (gain!=0){
                 for (i=0;i<frame_size*channels;i++)
                    output[i] *= gain;
               }

               /*This handles making sure that our output duration respects
                 the final end-trim by not letting the output sample count
                 get ahead of the granpos indicated value.*/
               maxout=((page_granule-gran_offset)*rate/48000)-link_out;
               outsamp=audio_write(output, channels, frame_size, fout, resampler, &preskip, dither?&shapemem:0, strlen(outFile)!=0,0>maxout?0:maxout);
               link_out+=outsamp;
               audio_size+=sizeof(short)*outsamp*channels;
            }
            packet_count++;
         }
         /*We're done, drain the resampler if we were using it.*/
         if(eos && resampler)
         {
            float *zeros;
            int drain;

            zeros=(float *)calloc(100*channels,sizeof(float));
            drain = speex_resampler_get_input_latency(resampler);
            do {
               opus_int64 outsamp;
               int tmp = drain;
               if (tmp > 100)
                  tmp = 100;
               outsamp=audio_write(zeros, channels, tmp, fout, resampler, NULL, &shapemem, strlen(outFile)!=0, ((page_granule-gran_offset)*rate/48000)-link_out);
               link_out+=outsamp;
               audio_size+=sizeof(short)*outsamp*channels;
               drain -= tmp;
            } while (drain>0);
            free(zeros);
            speex_resampler_destroy(resampler);
            resampler=NULL;
         }
         if(eos)
         {
            has_opus_stream=0;
            if(st)opus_multistream_decoder_destroy(st);
            st=NULL;
         }
      }
      if (feof(fin)) {
         if(!quiet) {
           fprintf(stderr, "\rDecoding complete.        \n");
           fflush(stderr);
         }
         break;
      }
   }

   /*If we were writing wav, go set the duration.*/
   if (strlen(outFile)!=0 && fout && wav_format>=0 && audio_size<0x7FFFFFFF)
   {
      if (fseek(fout,4,SEEK_SET)==0)
      {
         int tmp;
         tmp = le_int(audio_size+20+wav_format);
         if(fwrite(&tmp,4,1,fout)!=1)fprintf(stderr,"Error writing end length.\n");
         if (fseek(fout,16+wav_format,SEEK_CUR)==0)
         {
            tmp = le_int(audio_size);
            if(fwrite(&tmp,4,1,fout)!=1)fprintf(stderr,"Error writing header length.\n");
         } else
         {
            fprintf (stderr, "First seek worked, second didn't\n");
         }
      } else {
         fprintf (stderr, "Cannot seek on wave file, size will be incorrect\n");
      }
   }

   /*Did we make it to the end without recovering ANY opus logical streams?*/
   if(!total_links)fprintf (stderr, "This doesn't look like a Opus file\n");

   if (stream_init)
      ogg_stream_clear(&os);
   ogg_sync_clear(&oy);

#if defined WIN32 || defined _WIN32
   if (strlen(outFile)==0)
      WIN_Audio_close ();
#endif

   if(shapemem.a_buf)free(shapemem.a_buf);
   if(shapemem.b_buf)free(shapemem.b_buf);

   if(output)free(output);

   if(frange)fclose(frange);

   if (close_in)
      fclose(fin);
   if (fout != NULL)
      fclose(fout);

#ifdef WIN_UNICODE
   free_commandline_arguments_utf8(&argc_utf8, &argv_utf8);
   uninit_console_utf8();
#endif

   return 0;
}
コード例 #6
0
ファイル: dsp.c プロジェクト: upswimsdn/libventrilo3
int
_v3_audio_send(v3_handle v3h, uint32_t rate, uint8_t channels, const void *pcm, uint32_t pcmlen) {
    _v3_connection *v3c;
    const v3_codec *codec;
    uint8_t pcmbuf[65536];
    uint32_t pcmbuflen = 0;
    uint8_t databuf[2048];
    uint32_t databuflen = 0;
    uint32_t framesize;
    uint8_t *ptr;
    uint32_t *len;
    uint32_t rd;
    float *volume[1];
    int ret = V3_OK;

    _v3_enter(v3h, __func__);

    channels = (channels == 2) ? 2 : 1;
    v3c = _v3_handles[v3h];
    codec = v3_codec_channel_get(v3h, v3c->luser.channel);
    framesize = codec->framesize * channels;

    if (!v3_codec_valid(codec)) {
        _v3_error(v3h, "invalid or unsupported codec");
        _v3_leave(v3h, __func__);
        return V3_FAILURE;
    }
    if (rate != codec->rate) {
#ifdef HAVE_SPEEXDSP
        int err = 0;
        uint32_t in_len = pcmlen;
        uint32_t out_len = sizeof(pcmbuf);

        if (!v3c->resampler.state ||
            v3c->resampler.in_rate != rate ||
            v3c->resampler.out_rate != codec->rate ||
            v3c->resampler.channels != channels) {
            if (v3c->resampler.state) {
                speex_resampler_destroy(v3c->resampler.state);
            }
            v3c->resampler.state = speex_resampler_init(
                    (v3c->resampler.channels = channels),
                    (v3c->resampler.in_rate = rate),
                    (v3c->resampler.out_rate = codec->rate),
                    SPEEX_RESAMPLER_QUALITY_VOIP,
                    &err);
        }
        in_len  /= sizeof(int16_t) * channels;
        out_len /= sizeof(int16_t) * channels;
        if (err || (err = speex_resampler_process_interleaved_int(
                v3c->resampler.state,
                pcm,
                &in_len,
                (void *)pcmbuf,
                &out_len))) {
            _v3_error(v3h, "resampler error: %i: %s", err, speex_resampler_strerror(err));
            _v3_leave(v3h, __func__);
            return V3_FAILURE;
        }
        pcmbuflen = out_len * sizeof(int16_t) * channels;
#else
        _v3_error(v3h, "resampler needed for output rate (in: %uHz != out: %uHz)", rate, codec->rate);
        _v3_leave(v3h, __func__);
        return V3_FAILURE;
#endif
    } else {
        if (pcmlen > sizeof(pcmbuf)) {
            _v3_error(v3h, "pcm length larger than buffer size (%u > %lu)", pcmlen, sizeof(pcmbuf));
            _v3_leave(v3h, __func__);
            return V3_FAILURE;
        }
        memcpy(pcmbuf, pcm, pcmlen);
        pcmbuflen = pcmlen;
    }
    if (channels == 2) {
        switch (codec->index) {
#ifdef HAVE_OPUS
          case 1:
          case 2:
            break;
#endif
          default:
            {
                int16_t *sample = (int16_t *)pcmbuf;
                uint32_t ctr;

                pcmbuflen /= 2;
                for (ctr = 0; ctr < pcmbuflen; ++ctr) {
                    sample[ctr] = sample[ctr*2] / 2 + sample[ctr*2+1] / 2;
                }
                channels = 1;
            }
            break;
        }
    }
    while (*(len = (v3c->pcmqueued) ? &v3c->pcmqueued : &pcmbuflen) / framesize >= codec->frames) {
        ptr = (v3c->pcmqueued) ? v3c->pcmq : pcmbuf;
        rd = framesize * codec->frames;
        *volume = &v3c->luser.volume;
        _v3_audio_amplify(v3h, (void *)ptr, rd, volume, sizeof(volume) / sizeof(*volume));
        databuflen = sizeof(databuf);
        if ((ret = _v3_audio_encode(
                v3h,
                ptr,
                rd,
                codec->index,
                codec->format,
                &v3c->encoder,
                databuf,
                &databuflen,
                channels)) == V3_OK) {
            ret = _v3_msg_audio_put(
                    v3h,
                    V3_AUDIO_DATA,
                    codec->index,
                    codec->format,
                    pcmlen,
                    databuf,
                    databuflen);
        }
        memmove(ptr, ptr + rd, (v3c->pcmqueued ? sizeof(v3c->pcmq) : sizeof(pcmbuf)) - rd);
        *len -= rd;
    }
    if (pcmbuflen) {
        rd = sizeof(v3c->pcmq) - v3c->pcmqueued;
        rd = (pcmbuflen > rd) ? rd : pcmbuflen;
        memcpy(v3c->pcmq + v3c->pcmqueued, pcmbuf, rd);
        v3c->pcmqueued += rd;
    }

    _v3_leave(v3h, __func__);
    return ret;
}
コード例 #7
0
ファイル: speexdspResampler.c プロジェクト: taktod/ttLibC
/*
 * sample_rate resample.
 * @param resampler  resampler object.
 * @param prev_frame reuse frame.
 * @param src_pcms16 source pcms16 data.
 * @return resampled pcms16 data.
 */
ttLibC_PcmS16 *ttLibC_SpeexdspResampler_resample(ttLibC_SpeexdspResampler *resampler, ttLibC_PcmS16 *prev_frame, ttLibC_PcmS16 *src_pcms16) {
	ttLibC_SpeexdspResampler_ *resampler_ = (ttLibC_SpeexdspResampler_ *)resampler;
	if(resampler_ == NULL) {
		return NULL;
	}
	if(src_pcms16 == NULL) {
		return NULL;
	}
	switch(src_pcms16->type) {
	case PcmS16Type_bigEndian:
	case PcmS16Type_bigEndian_planar:
		resampler_->inherit_super.error = ttLibC_updateError(Target_On_Resampler, Error_InvalidOperation);
		return NULL;
	default:
		return NULL;
	case PcmS16Type_littleEndian:
	case PcmS16Type_littleEndian_planar:
		break;
	}
	ttLibC_PcmS16 *pcms16 = prev_frame;
	uint32_t out_sample_num = (src_pcms16->inherit_super.sample_num
			* resampler_->inherit_super.output_sample_rate
			/ resampler_->inherit_super.input_sample_rate + 1);
	uint32_t in_sample_num  = src_pcms16->inherit_super.sample_num;
	// estimate result data size.
	size_t data_size = out_sample_num * sizeof(int16_t) * resampler_->inherit_super.channel_num;
	uint8_t *data = NULL;
	bool alloc_flag = false;
	if(pcms16 != NULL) {
		if(!pcms16->inherit_super.inherit_super.is_non_copy) {
			if(pcms16->inherit_super.inherit_super.data_size >= data_size) {
				// reuse frame have enough buffer.
				data = pcms16->inherit_super.inherit_super.data;
				data_size = pcms16->inherit_super.inherit_super.data_size;
			}
			else {
				ttLibC_free(pcms16->inherit_super.inherit_super.data);
			}
		}
		pcms16->inherit_super.inherit_super.is_non_copy = true;
	}
	if(data == NULL) {
		data = ttLibC_malloc(data_size);
		if(data == NULL) {
			resampler_->inherit_super.error = ttLibC_updateError(Target_On_Resampler, Error_MemoryAllocate);
			return NULL;
		}
		alloc_flag = true;
	}
	int res;
	switch(src_pcms16->type) {
	default:
		ERR_PRINT("no way to be here..");
		if(alloc_flag) {
			ttLibC_free(data);
		}
		return NULL;
	case PcmS16Type_littleEndian:
		res = speex_resampler_process_interleaved_int(resampler_->resampler, (const int16_t *)src_pcms16->l_data, &in_sample_num, (int16_t *)data, &out_sample_num);
		break;
	case PcmS16Type_littleEndian_planar:
		res = speex_resampler_process_int(resampler_->resampler, resampler_->inherit_super.channel_num, (const int16_t *)src_pcms16->l_data, &in_sample_num, (int16_t *)data, &out_sample_num);
		break;
	}
	if(res != 0) {
		ERR_PRINT("failed to resampler: %s", speex_resampler_strerror(res));
		if(alloc_flag) {
			ttLibC_free(data);
		}
		resampler_->inherit_super.error = ttLibC_updateError(Target_On_Resampler, Error_LibraryError);
		return NULL;
	}
	uint64_t pts = src_pcms16->inherit_super.inherit_super.pts * resampler_->inherit_super.output_sample_rate / resampler_->inherit_super.input_sample_rate;
	uint8_t *l_data = NULL;
	uint32_t l_stride = 0;
	uint8_t *r_data = NULL;
	uint32_t r_stride = 0;
	switch(src_pcms16->type) {
	case PcmS16Type_bigEndian:
	case PcmS16Type_littleEndian:
		l_data = data;
		l_stride = out_sample_num * 2 * resampler_->inherit_super.channel_num;
		break;
	case PcmS16Type_bigEndian_planar:
	case PcmS16Type_littleEndian_planar:
		l_data = data;
		l_stride = out_sample_num * 2;
		if(resampler_->inherit_super.channel_num == 2) {
			r_data = data + l_stride;
			r_stride = l_stride;
		}
		break;
	}
	pcms16 = ttLibC_PcmS16_make(
			pcms16,
			src_pcms16->type,
			resampler_->inherit_super.output_sample_rate,
			out_sample_num,
			resampler_->inherit_super.channel_num,
			data,
			data_size,
			l_data,
			l_stride,
			r_data,
			r_stride,
			true,
			pts,
			resampler_->inherit_super.output_sample_rate);
	if(pcms16 == NULL) {
		if(alloc_flag) {
			ttLibC_free(data);
		}
		resampler_->inherit_super.error = ttLibC_updateError(Target_On_Resampler, Error_MemoryAllocate);
		return NULL;
	}
	pcms16->inherit_super.inherit_super.is_non_copy = false;
	pcms16->inherit_super.inherit_super.buffer_size = pcms16->inherit_super.sample_num * pcms16->inherit_super.channel_num * sizeof(int16_t);
	return pcms16;
}
コード例 #8
0
ファイル: audio_resampler.cpp プロジェクト: EasyRPG/Player
int AudioResampler::FillBufferDifferentRate(uint8_t* buffer, int length) {
	const int input_samplesize = GetSamplesizeForFormat(input_format);
	const int output_samplesize = GetSamplesizeForFormat(output_format);
	//The buffer size has to be a multiple of a frame
	const int buffer_size=sizeof(internal_buffer) - sizeof(internal_buffer)%(nr_of_channels*((input_samplesize>output_samplesize) ? input_samplesize : output_samplesize));
	
	int total_output_frames = length / (output_samplesize*nr_of_channels);
	int amount_of_samples_to_read = 0;
	int amount_of_samples_read = 0;
	
	uint8_t * advanced_input_buffer = internal_buffer;
	int unused_frames = 0;
	int empty_buffer_space = 0;
	int error = 0;
	
	#ifdef HAVE_LIBSPEEXDSP
		spx_uint32_t numerator = 0;
		spx_uint32_t denominator = 0;
	#endif

	while (total_output_frames > 0) {
		//Calculate how much frames of the last cycle are unused - to reuse them
		unused_frames = conversion_data.input_frames - conversion_data.input_frames_used;
		empty_buffer_space = buffer_size / output_samplesize - unused_frames*nr_of_channels;
		
		advanced_input_buffer = internal_buffer;

		//If there is still unused data in the input_buffer order it to the front
		for (int i = 0; i < unused_frames*nr_of_channels*output_samplesize; i++) {
			*advanced_input_buffer = *(advanced_input_buffer + empty_buffer_space*output_samplesize);
			advanced_input_buffer++;
		}
		//advanced_input_buffer is now offset to the first frame of new data!

		//ensure that the input buffer is not able to overrun
		amount_of_samples_to_read = (input_samplesize > output_samplesize) ? (empty_buffer_space*output_samplesize) / input_samplesize : empty_buffer_space;

		//Read as many frames as needed to refill the buffer (filled after the conversion to float)
		if (amount_of_samples_to_read != 0) {
			switch (output_format) {
				case AudioDecoder::Format::F32: amount_of_samples_read = DecodeAndConvertFloat(wrapped_decoder.get(), advanced_input_buffer, amount_of_samples_to_read, input_samplesize, input_format); break;
			#ifdef HAVE_LIBSPEEXDSP
				case AudioDecoder::Format::S16:  amount_of_samples_read = DecodeAndConvertInt16(wrapped_decoder.get(), advanced_input_buffer, amount_of_samples_to_read, input_samplesize, input_format); break;
			#endif
				default: error_message = "internal error: output_format is not convertable"; return ERROR;
			}
			if (amount_of_samples_read < 0) {
				error_message = wrapped_decoder->GetError();
				return amount_of_samples_read; //error occured
			}
		}
		//Now we have a prepared full buffer of converted values

		//Prepare the source data
		conversion_data.input_frames = amount_of_samples_read / nr_of_channels + unused_frames;
		conversion_data.output_frames = total_output_frames;

		#if defined(HAVE_LIBSPEEXDSP)
			conversion_data.input_frames_used = conversion_data.input_frames;
			conversion_data.output_frames_gen = conversion_data.output_frames;

			//libspeexdsp defines a sample rate conversion with a fraction (input/output)
			numerator = input_rate*pitch;
			denominator = output_rate * STANDARD_PITCH;
			if (pitch_handled_by_decoder) {
				numerator = input_rate;
				denominator = output_rate;
			}
			if (conversion_data.ratio_num != numerator || conversion_data.ratio_denom != denominator) {
				speex_resampler_set_rate_frac(conversion_state, numerator, denominator, input_rate, output_rate);
				conversion_data.ratio_num = numerator;
				conversion_data.ratio_denom = denominator;
			}
			
			//A pitfall from libspeexdsp if the output buffer is defined to big - everything stutters -achieved good values with the same size as the input buffer for a maximum
			conversion_data.output_frames_gen=(conversion_data.input_frames<conversion_data.output_frames_gen) ? conversion_data.input_frames :conversion_data.output_frames_gen;
			
			switch (output_format) {
			case Format::F32:
				error = speex_resampler_process_interleaved_float(conversion_state, (float*)internal_buffer, &conversion_data.input_frames_used, (float*)buffer, &conversion_data.output_frames_gen);
				break;
			case Format::S16:
				error = speex_resampler_process_interleaved_int(conversion_state, (spx_int16_t*)internal_buffer, &conversion_data.input_frames_used, (spx_int16_t*)buffer, &conversion_data.output_frames_gen);
				break;
			default: error_message = "internal error: output_format is not convertable"; return ERROR;
			}
			
			if (error != 0) {
				error_message = speex_resampler_strerror(error);
				return ERROR;
			}
		#elif defined(HAVE_LIBSAMPLERATE)
			conversion_data.data_in = (float*)internal_buffer;
			conversion_data.data_out = (float*)buffer;
			if (pitch_handled_by_decoder) {
				conversion_data.src_ratio = (output_rate*1.0) / input_rate;
			}
			else {
				conversion_data.src_ratio = (output_rate*STANDARD_PITCH *1.0) / (input_rate*pitch*1.0);
			}
			conversion_data.end_of_input = (wrapped_decoder->IsFinished()) ? 1 : 0;

			//Now let libsamplerate filter the data
			error = src_process(conversion_state, &conversion_data);

			if (error != 0) {
				error_message = src_strerror(error);
				return ERROR;
			}
		#endif

		total_output_frames -= conversion_data.output_frames_gen;
		buffer += conversion_data.output_frames_gen*nr_of_channels*output_samplesize;

		if ((conversion_data.input_frames == 0 && conversion_data.output_frames_gen <= conversion_data.output_frames) || conversion_data.output_frames_gen == 0) {
			finished = true;
			//There is nothing left to convert - return how much samples (in bytes) are converted! 
			return length - total_output_frames*(output_samplesize*nr_of_channels);
		}
	}
	return length;
}
コード例 #9
0
ファイル: exceptions.cpp プロジェクト: zaolij/libaudioverse
const char* SpeexError::what() const {
	return speex_resampler_strerror(code);
}
コード例 #10
0
/** @internal @This handles data.
 *
 * @param upipe description structure of the pipe
 * @param uref uref structure
 * @param upump_p reference to pump that generated the buffer
 * @return false if the input must be blocked
 */
static bool upipe_speexdsp_handle(struct upipe *upipe, struct uref *uref,
                             struct upump **upump_p)
{
    struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe);

    struct urational drift_rate;
    if (!ubase_check(uref_clock_get_rate(uref, &drift_rate)))
        drift_rate = (struct urational){ 1, 1 };

    /* reinitialize resampler when drift rate changes */
    if (urational_cmp(&drift_rate, &upipe_speexdsp->drift_rate)) {
        upipe_speexdsp->drift_rate = drift_rate;
        spx_uint32_t ratio_num = drift_rate.den;
        spx_uint32_t ratio_den = drift_rate.num;
        spx_uint32_t in_rate = upipe_speexdsp->rate * ratio_num / ratio_den;
        spx_uint32_t out_rate = upipe_speexdsp->rate;
        int err = speex_resampler_set_rate_frac(upipe_speexdsp->ctx,
                ratio_num, ratio_den, in_rate, out_rate);
        if (err) {
            upipe_err_va(upipe, "Couldn't resample from %u to %u: %s",
                in_rate, out_rate, speex_resampler_strerror(err));
        } else {
            upipe_dbg_va(upipe, "Resampling from %u to %u",
                in_rate, out_rate);
        }
    }

    size_t size;
    if (!ubase_check(uref_sound_size(uref, &size, NULL /* sample_size */))) {
        uref_free(uref);
        return true;
    }

    struct ubuf *ubuf = ubuf_sound_alloc(upipe_speexdsp->ubuf_mgr, size + 10);
    if (!ubuf)
        return false;

    const void *in;
    uref_sound_read_void(uref, 0, -1, &in, 1);

    void *out;
    ubuf_sound_write_void(ubuf, 0, -1, &out, 1);

    spx_uint32_t in_len = size;         /* input size */
    spx_uint32_t out_len = size + 10;   /* available output size */

    int err;

    if (upipe_speexdsp->f32)
        err = speex_resampler_process_interleaved_float(upipe_speexdsp->ctx,
                in, &in_len, out, &out_len);
    else
        err = speex_resampler_process_interleaved_int(upipe_speexdsp->ctx,
                in, &in_len, out, &out_len);

    if (err) {
        upipe_err_va(upipe, "Could not resample: %s",
                speex_resampler_strerror(err));
    }

    uref_sound_unmap(uref, 0, -1, 1);
    ubuf_sound_unmap(ubuf, 0, -1, 1);

    if (err) {
        ubuf_free(ubuf);
    } else {
        ubuf_sound_resize(ubuf, 0, out_len);
        uref_attach_ubuf(uref, ubuf);
    }

    upipe_speexdsp_output(upipe, uref, upump_p);
    return true;
}

/** @internal @This receives incoming uref.
 *
 * @param upipe description structure of the pipe
 * @param uref uref structure describing the picture
 * @param upump_p reference to pump that generated the buffer
 */
static void upipe_speexdsp_input(struct upipe *upipe, struct uref *uref,
                            struct upump **upump_p)
{
    if (!upipe_speexdsp_check_input(upipe)) {
        upipe_speexdsp_hold_input(upipe, uref);
        upipe_speexdsp_block_input(upipe, upump_p);
    } else if (!upipe_speexdsp_handle(upipe, uref, upump_p)) {
        upipe_speexdsp_hold_input(upipe, uref);
        upipe_speexdsp_block_input(upipe, upump_p);
        /* Increment upipe refcount to avoid disappearing before all packets
         * have been sent. */
        upipe_use(upipe);
    }
}

/** @internal @This receives a provided ubuf manager.
 *
 * @param upipe description structure of the pipe
 * @param flow_format amended flow format
 * @return an error code
 */
static int upipe_speexdsp_check(struct upipe *upipe, struct uref *flow_format)
{
    struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe);
    if (flow_format != NULL)
        upipe_speexdsp_store_flow_def(upipe, flow_format);

    if (upipe_speexdsp->flow_def == NULL)
        return UBASE_ERR_NONE;

    bool was_buffered = !upipe_speexdsp_check_input(upipe);
    upipe_speexdsp_output_input(upipe);
    upipe_speexdsp_unblock_input(upipe);
    if (was_buffered && upipe_speexdsp_check_input(upipe)) {
        /* All packets have been output, release again the pipe that has been
         * used in @ref upipe_speexdsp_input. */
        upipe_release(upipe);
    }
    return UBASE_ERR_NONE;
}

/** @internal @This sets the input flow definition.
 *
 * @param upipe description structure of the pipe
 * @param flow_def flow definition packet
 * @return an error code
 */
static int upipe_speexdsp_set_flow_def(struct upipe *upipe, struct uref *flow_def)
{
    struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe);

    if (flow_def == NULL)
        return UBASE_ERR_INVALID;

    const char *def;
    UBASE_RETURN(uref_flow_get_def(flow_def, &def))

    if (unlikely(ubase_ncmp(def, "sound.f32.") &&
                ubase_ncmp(def, "sound.s16.")))
        return UBASE_ERR_INVALID;

    uint8_t in_planes;
    if (unlikely(!ubase_check(uref_sound_flow_get_planes(flow_def,
                                                         &in_planes))))
        return UBASE_ERR_INVALID;

    if (in_planes != 1) {
        upipe_err(upipe, "only interleaved audio is supported");
        return UBASE_ERR_INVALID;
    }

    if (!ubase_check(uref_sound_flow_get_rate(flow_def,
                    &upipe_speexdsp->rate))) {
        upipe_err(upipe, "no sound rate defined");
        uref_dump(flow_def, upipe->uprobe);
        return UBASE_ERR_INVALID;
    }

    uint8_t channels;
    if (unlikely(!ubase_check(uref_sound_flow_get_channels(flow_def,
                        &channels))))
        return UBASE_ERR_INVALID;

    flow_def = uref_dup(flow_def);
    if (unlikely(flow_def == NULL)) {
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
        return UBASE_ERR_ALLOC;
    }

    upipe_speexdsp_require_ubuf_mgr(upipe, flow_def);

    if (upipe_speexdsp->ctx)
        speex_resampler_destroy(upipe_speexdsp->ctx);

    upipe_speexdsp->f32 = !ubase_ncmp(def, "sound.f32.");

    int err;
    upipe_speexdsp->ctx = speex_resampler_init(channels,
                upipe_speexdsp->rate, upipe_speexdsp->rate,
                upipe_speexdsp->quality, &err);
    if (!upipe_speexdsp->ctx) {
        upipe_err_va(upipe, "Could not create resampler: %s",
                speex_resampler_strerror(err));
        return UBASE_ERR_INVALID;
    }

    return UBASE_ERR_NONE;
}

/** @internal @This provides a flow format suggestion.
 *
 * @param upipe description structure of the pipe
 * @param request description structure of the request
 * @return an error code
 */
static int upipe_speexdsp_provide_flow_format(struct upipe *upipe,
                                          struct urequest *request)
{
    const char *def;
    UBASE_RETURN(uref_flow_get_def(request->uref, &def))
    uint8_t channels;
    UBASE_RETURN(uref_sound_flow_get_channels(request->uref, &channels))
    uint8_t planes;
    UBASE_RETURN(uref_sound_flow_get_planes(request->uref, &planes))
    uint8_t sample_size;
    UBASE_RETURN(uref_sound_flow_get_sample_size(request->uref, &sample_size))

    struct uref *flow = uref_dup(request->uref);
    UBASE_ALLOC_RETURN(flow);

    uref_sound_flow_clear_format(flow);
    uref_sound_flow_set_planes(flow, 0);
    uref_sound_flow_set_channels(flow, channels);
    uref_sound_flow_add_plane(flow, "all");
    if (ubase_ncmp(def, "sound.s16.")) {
        uref_flow_set_def(flow, "sound.f32."); /* prefer f32 over s16 */
        uref_sound_flow_set_sample_size(flow, 4 * channels);
    } else {
        uref_flow_set_def(flow, def);
        uref_sound_flow_set_sample_size(flow, (planes > 1) ? sample_size :
                sample_size / channels);
    }

    return urequest_provide_flow_format(request, flow);
}

/** @internal @This processes control commands on a speexdsp pipe.
 *
 * @param upipe description structure of the pipe
 * @param command type of command to process
 * @param args arguments of the command
 * @return an error code
 */
static int upipe_speexdsp_control(struct upipe *upipe, int command, va_list args)
{
    struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe);

    switch (command) {
        /* generic commands */
        case UPIPE_REGISTER_REQUEST: {
            struct urequest *request = va_arg(args, struct urequest *);
            if (request->type == UREQUEST_FLOW_FORMAT)
                return upipe_speexdsp_provide_flow_format(upipe, request);
            if (request->type == UREQUEST_UBUF_MGR)
                return upipe_throw_provide_request(upipe, request);
            return upipe_speexdsp_alloc_output_proxy(upipe, request);
        }
        case UPIPE_UNREGISTER_REQUEST: {
            struct urequest *request = va_arg(args, struct urequest *);
            if (request->type == UREQUEST_FLOW_FORMAT ||
                request->type == UREQUEST_UBUF_MGR)
                return UBASE_ERR_NONE;
            return upipe_speexdsp_free_output_proxy(upipe, request);
        }

        case UPIPE_GET_OUTPUT: {
            struct upipe **p = va_arg(args, struct upipe **);
            return upipe_speexdsp_get_output(upipe, p);
        }
        case UPIPE_SET_OUTPUT: {
            struct upipe *output = va_arg(args, struct upipe *);
            return upipe_speexdsp_set_output(upipe, output);
        }
        case UPIPE_GET_FLOW_DEF: {
            struct uref **p = va_arg(args, struct uref **);
            return upipe_speexdsp_get_flow_def(upipe, p);
        }
        case UPIPE_SET_FLOW_DEF: {
            struct uref *flow = va_arg(args, struct uref *);
            return upipe_speexdsp_set_flow_def(upipe, flow);
        }
        case UPIPE_SET_OPTION: {
            const char *option = va_arg(args, const char *);
            const char *value  = va_arg(args, const char *);
            if (strcmp(option, "quality"))
                return UBASE_ERR_INVALID;
            if (upipe_speexdsp->ctx)
                return UBASE_ERR_BUSY;
            int quality = atoi(value);
            if (quality > SPEEX_RESAMPLER_QUALITY_MAX) {
                quality = SPEEX_RESAMPLER_QUALITY_MAX;
                upipe_err_va(upipe, "Clamping quality to %d",
                        SPEEX_RESAMPLER_QUALITY_MAX);
            } else if (quality < SPEEX_RESAMPLER_QUALITY_MIN) {
                quality = SPEEX_RESAMPLER_QUALITY_MIN;
                upipe_err_va(upipe, "Clamping quality to %d",
                        SPEEX_RESAMPLER_QUALITY_MIN);
            }
            upipe_speexdsp->quality = quality;
            return UBASE_ERR_NONE;
        }

        default:
            return UBASE_ERR_UNHANDLED;
    }
}

/** @internal @This allocates a speexdsp pipe.
 *
 * @param mgr common management structure
 * @param uprobe structure used to raise events
 * @param signature signature of the pipe allocator
 * @param args optional arguments
 * @return pointer to upipe or NULL in case of allocation error
 */
static struct upipe *upipe_speexdsp_alloc(struct upipe_mgr *mgr,
                                     struct uprobe *uprobe,
                                     uint32_t signature, va_list args)
{
    struct upipe *upipe = upipe_speexdsp_alloc_void(mgr, uprobe, signature,
                                               args);
    if (unlikely(upipe == NULL))
        return NULL;

    struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe);

    upipe_speexdsp->ctx = NULL;
    upipe_speexdsp->drift_rate = (struct urational){ 0, 0 };
    upipe_speexdsp->quality = SPEEX_RESAMPLER_QUALITY_MAX;

    upipe_speexdsp_init_urefcount(upipe);
    upipe_speexdsp_init_ubuf_mgr(upipe);
    upipe_speexdsp_init_output(upipe);
    upipe_speexdsp_init_flow_def(upipe);
    upipe_speexdsp_init_input(upipe);

    upipe_throw_ready(upipe);
    return upipe;
}

/** @This frees a upipe.
 *
 * @param upipe description structure of the pipe
 */
static void upipe_speexdsp_free(struct upipe *upipe)
{
    struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe);
    if (likely(upipe_speexdsp->ctx))
        speex_resampler_destroy(upipe_speexdsp->ctx);

    upipe_throw_dead(upipe);
    upipe_speexdsp_clean_input(upipe);
    upipe_speexdsp_clean_output(upipe);
    upipe_speexdsp_clean_flow_def(upipe);
    upipe_speexdsp_clean_ubuf_mgr(upipe);
    upipe_speexdsp_clean_urefcount(upipe);
    upipe_speexdsp_free_void(upipe);
}

/** module manager static descriptor */
static struct upipe_mgr upipe_speexdsp_mgr = {
    .refcount = NULL,
    .signature = UPIPE_SPEEXDSP_SIGNATURE,

    .upipe_alloc = upipe_speexdsp_alloc,
    .upipe_input = upipe_speexdsp_input,
    .upipe_control = upipe_speexdsp_control,

    .upipe_mgr_control = NULL
};

/** @This returns the management structure for speexdsp pipes
 *
 * @return pointer to manager
 */
struct upipe_mgr *upipe_speexdsp_mgr_alloc(void)
{
    return &upipe_speexdsp_mgr;
}