Similar to libswscale this does resampling and format convertion, just for audio instead of video. changing sampling rate, sample formats, channel layouts and sample packing all in one with a very simple public interface. Signed-off-by: Michael Niedermayer <michaelni@gmx.at>tags/n0.9
| @@ -33,6 +33,7 @@ FFLIBS-$(CONFIG_AVFILTER) += avfilter | |||
| FFLIBS-$(CONFIG_AVFORMAT) += avformat | |||
| FFLIBS-$(CONFIG_AVCODEC) += avcodec | |||
| FFLIBS-$(CONFIG_POSTPROC) += postproc | |||
| FFLIBS-$(CONFIG_SWRESAMPLE)+= swresample | |||
| FFLIBS-$(CONFIG_SWSCALE) += swscale | |||
| FFLIBS := avutil | |||
| @@ -20,7 +20,7 @@ $(foreach VAR,$(SILENT),$(eval override $(VAR) = @$($(VAR)))) | |||
| $(eval INSTALL = @$(call ECHO,INSTALL,$$(^:$(SRC_DIR)/%=%)); $(INSTALL)) | |||
| endif | |||
| ALLFFLIBS = avcodec avdevice avfilter avformat avutil postproc swscale | |||
| ALLFFLIBS = avcodec avdevice avfilter avformat avutil postproc swscale swresample | |||
| # NASM requires -I path terminated with / | |||
| IFLAGS := -I. -I$(SRC_PATH)/ | |||
| @@ -89,6 +89,7 @@ Configuration options: | |||
| --disable-avdevice disable libavdevice build | |||
| --disable-avcodec disable libavcodec build | |||
| --disable-avformat disable libavformat build | |||
| --disable-swresample disable libswresample build | |||
| --disable-swscale disable libswscale build | |||
| --disable-postproc disable libpostproc build | |||
| --disable-avfilter disable video filter support [no] | |||
| @@ -1037,6 +1038,7 @@ CONFIG_LIST=" | |||
| small | |||
| sram | |||
| static | |||
| swresample | |||
| swscale | |||
| swscale_alpha | |||
| thumb | |||
| @@ -1603,7 +1605,7 @@ avformat_deps="avcodec" | |||
| postproc_deps="gpl" | |||
| # programs | |||
| ffmpeg_deps="avcodec avformat swscale" | |||
| ffmpeg_deps="avcodec avformat swscale swresample" | |||
| ffmpeg_select="buffer_filter buffersink_filter" | |||
| avconv_deps="avcodec avformat swscale" | |||
| avconv_select="buffer_filter" | |||
| @@ -1766,6 +1768,7 @@ enable postproc | |||
| enable protocols | |||
| enable static | |||
| enable stripping | |||
| enable swresample | |||
| enable swscale | |||
| enable swscale_alpha | |||
| @@ -3143,7 +3146,7 @@ enabled extra_warnings && check_cflags -Winline | |||
| # add some linker flags | |||
| check_ldflags -Wl,--warn-common | |||
| check_ldflags -Wl,-rpath-link=libpostproc:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil | |||
| check_ldflags -Wl,-rpath-link=libpostproc:libswresample:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil | |||
| test_ldflags -Wl,-Bsymbolic && append SHFLAGS -Wl,-Bsymbolic | |||
| echo "X{};" > $TMPV | |||
| @@ -45,6 +45,7 @@ | |||
| #include "libavutil/avstring.h" | |||
| #include "libavutil/libm.h" | |||
| #include "libavformat/os_support.h" | |||
| #include "libswresample/swresample.h" | |||
| #include "libavformat/ffm.h" // not public API | |||
| @@ -229,15 +230,14 @@ typedef struct OutputStream { | |||
| /* audio only */ | |||
| int audio_resample; | |||
| ReSampleContext *resample; /* for audio resampling */ | |||
| int resample_sample_fmt; | |||
| int resample_channels; | |||
| int resample_sample_rate; | |||
| int reformat_pair; | |||
| AVAudioConvert *reformat_ctx; | |||
| AVFifoBuffer *fifo; /* for compression: one audio fifo per codec */ | |||
| FILE *logfile; | |||
| struct SwrContext *swr; | |||
| #if CONFIG_AVFILTER | |||
| AVFilterContext *output_video_filter; | |||
| AVFilterContext *input_video_filter; | |||
| @@ -843,14 +843,15 @@ need_realloc: | |||
| exit_program(1); | |||
| } | |||
| if (enc->channels != dec->channels) | |||
| if (enc->channels != dec->channels | |||
| || enc->sample_fmt != dec->sample_fmt) | |||
| ost->audio_resample = 1; | |||
| resample_changed = ost->resample_sample_fmt != dec->sample_fmt || | |||
| ost->resample_channels != dec->channels || | |||
| ost->resample_sample_rate != dec->sample_rate; | |||
| if ((ost->audio_resample && !ost->resample) || resample_changed) { | |||
| if ((ost->audio_resample && !ost->swr) || resample_changed) { | |||
| if (resample_changed) { | |||
| av_log(NULL, AV_LOG_INFO, "Input stream #%d.%d frame changed from rate:%d fmt:%s ch:%d to rate:%d fmt:%s ch:%d\n", | |||
| ist->file_index, ist->st->index, | |||
| @@ -859,24 +860,29 @@ need_realloc: | |||
| ost->resample_sample_fmt = dec->sample_fmt; | |||
| ost->resample_channels = dec->channels; | |||
| ost->resample_sample_rate = dec->sample_rate; | |||
| if (ost->resample) | |||
| audio_resample_close(ost->resample); | |||
| swr_free(&ost->swr); | |||
| } | |||
| /* if audio_sync_method is >1 the resampler is needed for audio drift compensation */ | |||
| if (audio_sync_method <= 1 && | |||
| ost->resample_sample_fmt == enc->sample_fmt && | |||
| ost->resample_channels == enc->channels && | |||
| ost->resample_sample_rate == enc->sample_rate) { | |||
| ost->resample = NULL; | |||
| //ost->swr = NULL; | |||
| ost->audio_resample = 0; | |||
| } else { | |||
| if (dec->sample_fmt != AV_SAMPLE_FMT_S16) | |||
| fprintf(stderr, "Warning, using s16 intermediate sample format for resampling\n"); | |||
| ost->resample = av_audio_resample_init(enc->channels, dec->channels, | |||
| enc->sample_rate, dec->sample_rate, | |||
| enc->sample_fmt, dec->sample_fmt, | |||
| 16, 10, 0, 0.8); | |||
| if (!ost->resample) { | |||
| ost->swr = swr_alloc2(ost->swr, | |||
| enc->channel_layout, enc->sample_fmt, enc->sample_rate, | |||
| dec->channel_layout, dec->sample_fmt, dec->sample_rate, | |||
| 0, NULL); | |||
| av_set_int(ost->swr, "ich", dec->channels); | |||
| av_set_int(ost->swr, "och", enc->channels); | |||
| if(audio_sync_method>1) av_set_int(ost->swr, "flags", SWR_FLAG_RESAMPLE); | |||
| if(ost->swr && swr_init(ost->swr) < 0){ | |||
| fprintf(stderr, "swr_init() failed\n"); | |||
| swr_free(&ost->swr); | |||
| } | |||
| if (!ost->swr) { | |||
| fprintf(stderr, "Can not resample %d channels @ %d Hz to %d channels @ %d Hz\n", | |||
| dec->channels, dec->sample_rate, | |||
| enc->channels, enc->sample_rate); | |||
| @@ -885,21 +891,7 @@ need_realloc: | |||
| } | |||
| } | |||
| #define MAKE_SFMT_PAIR(a,b) ((a)+AV_SAMPLE_FMT_NB*(b)) | |||
| if (!ost->audio_resample && dec->sample_fmt!=enc->sample_fmt && | |||
| MAKE_SFMT_PAIR(enc->sample_fmt,dec->sample_fmt)!=ost->reformat_pair) { | |||
| if (ost->reformat_ctx) | |||
| av_audio_convert_free(ost->reformat_ctx); | |||
| ost->reformat_ctx = av_audio_convert_alloc(enc->sample_fmt, 1, | |||
| dec->sample_fmt, 1, NULL, 0); | |||
| if (!ost->reformat_ctx) { | |||
| fprintf(stderr, "Cannot convert %s sample format to %s sample format\n", | |||
| av_get_sample_fmt_name(dec->sample_fmt), | |||
| av_get_sample_fmt_name(enc->sample_fmt)); | |||
| exit_program(1); | |||
| } | |||
| ost->reformat_pair=MAKE_SFMT_PAIR(enc->sample_fmt,dec->sample_fmt); | |||
| } | |||
| av_assert0(ost->audio_resample || dec->sample_fmt==enc->sample_fmt); | |||
| if(audio_sync_method){ | |||
| double delta = get_sync_ipts(ost) * enc->sample_rate - ost->sync_opts | |||
| @@ -941,7 +933,7 @@ need_realloc: | |||
| if(verbose > 2) | |||
| fprintf(stderr, "compensating audio timestamp drift:%f compensation:%d in:%d\n", delta, comp, enc->sample_rate); | |||
| // fprintf(stderr, "drift:%f len:%d opts:%"PRId64" ipts:%"PRId64" fifo:%d\n", delta, -1, ost->sync_opts, (int64_t)(get_sync_ipts(ost) * enc->sample_rate), av_fifo_size(ost->fifo)/(ost->st->codec->channels * 2)); | |||
| av_resample_compensate(*(struct AVResampleContext**)ost->resample, comp, enc->sample_rate); | |||
| swr_compensate(ost->swr, comp, enc->sample_rate); | |||
| } | |||
| } | |||
| }else | |||
| @@ -950,30 +942,15 @@ need_realloc: | |||
| if (ost->audio_resample) { | |||
| buftmp = audio_buf; | |||
| size_out = audio_resample(ost->resample, | |||
| (short *)buftmp, (short *)buf, | |||
| size / (dec->channels * isize)); | |||
| size_out = swr_convert(ost->swr, ( uint8_t*[]){buftmp}, audio_buf_size / (enc->channels * osize), | |||
| (const uint8_t*[]){buf }, size / (dec->channels * isize)); | |||
| size_out = size_out * enc->channels * osize; | |||
| } else { | |||
| buftmp = buf; | |||
| size_out = size; | |||
| } | |||
| if (!ost->audio_resample && dec->sample_fmt!=enc->sample_fmt) { | |||
| const void *ibuf[6]= {buftmp}; | |||
| void *obuf[6]= {audio_buf}; | |||
| int istride[6]= {isize}; | |||
| int ostride[6]= {osize}; | |||
| int len= size_out/istride[0]; | |||
| if (av_audio_convert(ost->reformat_ctx, obuf, ostride, ibuf, istride, len)<0) { | |||
| printf("av_audio_convert() failed\n"); | |||
| if (exit_on_error) | |||
| exit_program(1); | |||
| return; | |||
| } | |||
| buftmp = audio_buf; | |||
| size_out = len*osize; | |||
| } | |||
| av_assert0(ost->audio_resample || dec->sample_fmt==enc->sample_fmt); | |||
| /* now encode as many frames as possible */ | |||
| if (enc->frame_size > 1) { | |||
| @@ -2133,7 +2110,6 @@ static int transcode_init(OutputFile *output_files, int nb_output_files, | |||
| if (!ost->fifo) { | |||
| return AVERROR(ENOMEM); | |||
| } | |||
| ost->reformat_pair = MAKE_SFMT_PAIR(AV_SAMPLE_FMT_NONE,AV_SAMPLE_FMT_NONE); | |||
| if (!codec->sample_rate) { | |||
| codec->sample_rate = icodec->sample_rate; | |||
| } | |||
| @@ -2149,6 +2125,8 @@ static int transcode_init(OutputFile *output_files, int nb_output_files, | |||
| if (av_get_channel_layout_nb_channels(codec->channel_layout) != codec->channels) | |||
| codec->channel_layout = 0; | |||
| ost->audio_resample = codec->sample_rate != icodec->sample_rate || audio_sync_method > 1; | |||
| ost->audio_resample |= codec->sample_fmt != icodec->sample_fmt | |||
| || codec->channel_layout != icodec->channel_layout; | |||
| icodec->request_channels = codec->channels; | |||
| ist->decoding_needed = 1; | |||
| ost->encoding_needed = 1; | |||
| @@ -2679,10 +2657,7 @@ static int transcode(OutputFile *output_files, int nb_output_files, | |||
| av_free(ost->forced_kf_pts); | |||
| if (ost->video_resample) | |||
| sws_freeContext(ost->img_resample_ctx); | |||
| if (ost->resample) | |||
| audio_resample_close(ost->resample); | |||
| if (ost->reformat_ctx) | |||
| av_audio_convert_free(ost->reformat_ctx); | |||
| swr_free(&ost->swr); | |||
| av_dict_free(&ost->opts); | |||
| } | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| include $(SUBDIR)../config.mak | |||
| NAME = swresample | |||
| FFLIBS = avutil | |||
| HEADERS = swresample.h | |||
| OBJS = swresample.o audioconvert.o resample2.o rematrix.o | |||
| TESTPROGS = swresample_test | |||
| include $(SUBDIR)../subdir.mak | |||
| @@ -0,0 +1,113 @@ | |||
| /* | |||
| * audio conversion | |||
| * Copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> | |||
| * | |||
| * This file is part of FFmpeg. | |||
| * | |||
| * FFmpeg is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * FFmpeg is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with FFmpeg; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| /** | |||
| * @file | |||
| * audio conversion | |||
| * @author Michael Niedermayer <michaelni@gmx.at> | |||
| */ | |||
| #include "libavutil/avstring.h" | |||
| #include "libavutil/avassert.h" | |||
| #include "libavutil/libm.h" | |||
| #include "libavutil/samplefmt.h" | |||
| #include "audioconvert.h" | |||
| struct AVAudioConvert { | |||
| int channels; | |||
| int fmt_pair; | |||
| }; | |||
| AVAudioConvert *swr_audio_convert_alloc(enum AVSampleFormat out_fmt, | |||
| enum AVSampleFormat in_fmt, | |||
| int channels, int flags) | |||
| { | |||
| AVAudioConvert *ctx; | |||
| ctx = av_malloc(sizeof(AVAudioConvert)); | |||
| if (!ctx) | |||
| return NULL; | |||
| ctx->channels = channels; | |||
| ctx->fmt_pair = out_fmt + AV_SAMPLE_FMT_NB*in_fmt; | |||
| return ctx; | |||
| } | |||
| void swr_audio_convert_free(AVAudioConvert **ctx) | |||
| { | |||
| av_freep(ctx); | |||
| } | |||
| int swr_audio_convert(AVAudioConvert *ctx, AudioData *out, AudioData*in, int len) | |||
| { | |||
| int ch; | |||
| av_assert0(ctx->channels == out->ch_count); | |||
| //FIXME optimize common cases | |||
| for(ch=0; ch<ctx->channels; ch++){ | |||
| const int is= (in ->planar ? 1 : in->ch_count) * in->bps; | |||
| const int os= (out->planar ? 1 :out->ch_count) *out->bps; | |||
| const uint8_t *pi= in ->ch[ch]; | |||
| uint8_t *po= out->ch[ch]; | |||
| uint8_t *end= po + os*len; | |||
| if(!po) | |||
| continue; | |||
| #define CONV(ofmt, otype, ifmt, expr)\ | |||
| if(ctx->fmt_pair == ofmt + AV_SAMPLE_FMT_NB*ifmt){\ | |||
| do{\ | |||
| *(otype*)po = expr; pi += is; po += os;\ | |||
| }while(po < end);\ | |||
| } | |||
| //FIXME put things below under ifdefs so we do not waste space for cases no codec will need | |||
| //FIXME rounding ? | |||
| CONV(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_U8 , *(const uint8_t*)pi) | |||
| else CONV(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_U8 , (*(const uint8_t*)pi - 0x80)<<8) | |||
| else CONV(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_U8 , (*(const uint8_t*)pi - 0x80)<<24) | |||
| else CONV(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_U8 , (*(const uint8_t*)pi - 0x80)*(1.0 / (1<<7))) | |||
| else CONV(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_U8 , (*(const uint8_t*)pi - 0x80)*(1.0 / (1<<7))) | |||
| else CONV(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_S16, (*(const int16_t*)pi>>8) + 0x80) | |||
| else CONV(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_S16, *(const int16_t*)pi) | |||
| else CONV(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_S16, *(const int16_t*)pi<<16) | |||
| else CONV(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_S16, *(const int16_t*)pi*(1.0 / (1<<15))) | |||
| else CONV(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_S16, *(const int16_t*)pi*(1.0 / (1<<15))) | |||
| else CONV(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_S32, (*(const int32_t*)pi>>24) + 0x80) | |||
| else CONV(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_S32, *(const int32_t*)pi>>16) | |||
| else CONV(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_S32, *(const int32_t*)pi) | |||
| else CONV(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_S32, *(const int32_t*)pi*(1.0 / (1U<<31))) | |||
| else CONV(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_S32, *(const int32_t*)pi*(1.0 / (1U<<31))) | |||
| else CONV(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_FLT, av_clip_uint8( lrintf(*(const float*)pi * (1<<7)) + 0x80)) | |||
| else CONV(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, av_clip_int16( lrintf(*(const float*)pi * (1<<15)))) | |||
| else CONV(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, av_clipl_int32(llrintf(*(const float*)pi * (1U<<31)))) | |||
| else CONV(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_FLT, *(const float*)pi) | |||
| else CONV(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_FLT, *(const float*)pi) | |||
| else CONV(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_DBL, av_clip_uint8( lrint(*(const double*)pi * (1<<7)) + 0x80)) | |||
| else CONV(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, av_clip_int16( lrint(*(const double*)pi * (1<<15)))) | |||
| else CONV(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, av_clipl_int32(llrint(*(const double*)pi * (1U<<31)))) | |||
| else CONV(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_DBL, *(const double*)pi) | |||
| else CONV(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_DBL, *(const double*)pi) | |||
| else return -1; | |||
| } | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,65 @@ | |||
| /* | |||
| * audio conversion | |||
| * Copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> | |||
| * Copyright (c) 2008 Peter Ross | |||
| * | |||
| * This file is part of FFmpeg. | |||
| * | |||
| * FFmpeg is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * FFmpeg is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with FFmpeg; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #ifndef SWR_AUDIOCONVERT_H | |||
| #define SWR_AUDIOCONVERT_H | |||
| /** | |||
| * @file | |||
| * Audio format conversion routines | |||
| */ | |||
| #include "swresample_internal.h" | |||
| #include "libavutil/cpu.h" | |||
| #include "libavutil/audioconvert.h" | |||
| struct AVAudioConvert; | |||
| typedef struct AVAudioConvert AVAudioConvert; | |||
| /** | |||
| * Create an audio sample format converter context | |||
| * @param out_fmt Output sample format | |||
| * @param in_fmt Input sample format | |||
| * @param channels Number of channels | |||
| * @param flags See AV_CPU_FLAG_xx | |||
| * @return NULL on error | |||
| */ | |||
| AVAudioConvert *swr_audio_convert_alloc(enum AVSampleFormat out_fmt, | |||
| enum AVSampleFormat in_fmt, | |||
| int channels, int flags); | |||
| /** | |||
| * Free audio sample format converter context. | |||
| * and set the pointer to NULL | |||
| */ | |||
| void swr_audio_convert_free(AVAudioConvert **ctx); | |||
| /** | |||
| * Convert between audio sample formats | |||
| * @param[in] out array of output buffers for each channel. set to NULL to ignore processing of the given channel. | |||
| * @param[in] in array of input buffers for each channel | |||
| * @param len length of audio frame size (measured in samples) | |||
| */ | |||
| int swr_audio_convert(AVAudioConvert *ctx, AudioData *out, AudioData *in, int len); | |||
| #endif /* AVCODEC_AUDIOCONVERT_H */ | |||
| @@ -0,0 +1,271 @@ | |||
| /* | |||
| * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at) | |||
| * | |||
| * This file is part of libswresample | |||
| * | |||
| * libswresample is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * libswresample is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with libswresample; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #include "swresample_internal.h" | |||
| #include "libavutil/audioconvert.h" | |||
| #include "libavutil/avassert.h" | |||
| #define SAMPLE float | |||
| #define RENAME(x) x ## _float | |||
| #include "rematrix_template.c" | |||
| #undef SAMPLE | |||
| #undef RENAME | |||
| #define SAMPLE int16_t | |||
| #define RENAME(x) x ## _s16 | |||
| #include "rematrix_template.c" | |||
| #define FRONT_LEFT 0 | |||
| #define FRONT_RIGHT 1 | |||
| #define FRONT_CENTER 2 | |||
| #define LOW_FREQUENCY 3 | |||
| #define BACK_LEFT 4 | |||
| #define BACK_RIGHT 5 | |||
| #define FRONT_LEFT_OF_CENTER 6 | |||
| #define FRONT_RIGHT_OF_CENTER 7 | |||
| #define BACK_CENTER 8 | |||
| #define SIDE_LEFT 9 | |||
| #define SIDE_RIGHT 10 | |||
| #define TOP_CENTER 11 | |||
| #define TOP_FRONT_LEFT 12 | |||
| #define TOP_FRONT_CENTER 13 | |||
| #define TOP_FRONT_RIGHT 14 | |||
| #define TOP_BACK_LEFT 15 | |||
| #define TOP_BACK_CENTER 16 | |||
| #define TOP_BACK_RIGHT 17 | |||
| static int even(int64_t layout){ | |||
| if(!layout) return 1; | |||
| if(layout&(layout-1)) return 1; | |||
| return 0; | |||
| } | |||
| static int sane_layout(int64_t layout){ | |||
| if(!(layout & AV_CH_LAYOUT_SURROUND)) // at least 1 front speaker | |||
| return 0; | |||
| if(!even(layout & (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT))) // no asymetric front | |||
| return 0; | |||
| if(!even(layout & (AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT))) // no asymetric side | |||
| return 0; | |||
| if(!even(layout & (AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT))) | |||
| return 0; | |||
| if(!even(layout & (AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER))) | |||
| return 0; | |||
| if(av_get_channel_layout_nb_channels(layout) >= SWR_CH_MAX) | |||
| return 0; | |||
| return 1; | |||
| } | |||
| int swr_rematrix_init(SwrContext *s){ | |||
| int i, j, in_i, out_i; | |||
| float matrix[64][64]={0}; | |||
| int64_t unaccounted= s->in_ch_layout & ~s->out_ch_layout; | |||
| float maxcoef=0; | |||
| for(i=0; i<64; i++){ | |||
| if(s->in_ch_layout & s->out_ch_layout & (1LL<<i)) | |||
| matrix[i][i]= 1.0; | |||
| } | |||
| if(!sane_layout(s->in_ch_layout)){ | |||
| av_log(s, AV_LOG_ERROR, "Input channel layout isnt supported\n"); | |||
| return AVERROR(EINVAL); | |||
| } | |||
| if(!sane_layout(s->out_ch_layout)){ | |||
| av_log(s, AV_LOG_ERROR, "Output channel layout isnt supported\n"); | |||
| return AVERROR(EINVAL); | |||
| } | |||
| //FIXME implement dolby surround | |||
| //FIXME implement full ac3 | |||
| if(unaccounted & AV_CH_FRONT_CENTER){ | |||
| if((s->out_ch_layout & AV_CH_LAYOUT_STEREO) == AV_CH_LAYOUT_STEREO){ | |||
| matrix[ FRONT_LEFT][FRONT_CENTER]+= sqrt(0.5); | |||
| matrix[FRONT_RIGHT][FRONT_CENTER]+= sqrt(0.5); | |||
| }else | |||
| av_assert0(0); | |||
| } | |||
| if(unaccounted & AV_CH_LAYOUT_STEREO){ | |||
| if(s->out_ch_layout & AV_CH_FRONT_CENTER){ | |||
| matrix[FRONT_CENTER][ FRONT_LEFT]+= sqrt(0.5); | |||
| matrix[FRONT_CENTER][FRONT_RIGHT]+= sqrt(0.5); | |||
| if(s->in_ch_layout & AV_CH_FRONT_CENTER) | |||
| matrix[FRONT_CENTER][ FRONT_CENTER] = s->clev*sqrt(2); | |||
| }else | |||
| av_assert0(0); | |||
| } | |||
| if(unaccounted & AV_CH_BACK_CENTER){ | |||
| if(s->out_ch_layout & AV_CH_BACK_LEFT){ | |||
| matrix[ BACK_LEFT][BACK_CENTER]+= sqrt(0.5); | |||
| matrix[BACK_RIGHT][BACK_CENTER]+= sqrt(0.5); | |||
| }else if(s->out_ch_layout & AV_CH_SIDE_LEFT){ | |||
| matrix[ SIDE_LEFT][BACK_CENTER]+= sqrt(0.5); | |||
| matrix[SIDE_RIGHT][BACK_CENTER]+= sqrt(0.5); | |||
| }else if(s->out_ch_layout & AV_CH_FRONT_LEFT){ | |||
| matrix[ FRONT_LEFT][BACK_CENTER]+= s->slev*sqrt(0.5); | |||
| matrix[FRONT_RIGHT][BACK_CENTER]+= s->slev*sqrt(0.5); | |||
| }else if(s->out_ch_layout & AV_CH_FRONT_CENTER){ | |||
| matrix[ FRONT_CENTER][BACK_CENTER]+= s->slev*sqrt(0.5); | |||
| }else | |||
| av_assert0(0); | |||
| } | |||
| if(unaccounted & AV_CH_BACK_LEFT){ | |||
| if(s->out_ch_layout & AV_CH_BACK_CENTER){ | |||
| matrix[BACK_CENTER][ BACK_LEFT]+= sqrt(0.5); | |||
| matrix[BACK_CENTER][BACK_RIGHT]+= sqrt(0.5); | |||
| }else if(s->out_ch_layout & AV_CH_SIDE_LEFT){ | |||
| if(s->in_ch_layout & AV_CH_SIDE_LEFT){ | |||
| matrix[ SIDE_LEFT][ BACK_LEFT]+= sqrt(0.5); | |||
| matrix[SIDE_RIGHT][BACK_RIGHT]+= sqrt(0.5); | |||
| }else{ | |||
| matrix[ SIDE_LEFT][ BACK_LEFT]+= 1.0; | |||
| matrix[SIDE_RIGHT][BACK_RIGHT]+= 1.0; | |||
| } | |||
| }else if(s->out_ch_layout & AV_CH_FRONT_LEFT){ | |||
| matrix[ FRONT_LEFT][ BACK_LEFT]+= s->slev; | |||
| matrix[FRONT_RIGHT][BACK_RIGHT]+= s->slev; | |||
| }else if(s->out_ch_layout & AV_CH_FRONT_CENTER){ | |||
| matrix[ FRONT_CENTER][BACK_LEFT ]+= s->slev*sqrt(0.5); | |||
| matrix[ FRONT_CENTER][BACK_RIGHT]+= s->slev*sqrt(0.5); | |||
| }else | |||
| av_assert0(0); | |||
| } | |||
| if(unaccounted & AV_CH_SIDE_LEFT){ | |||
| if(s->out_ch_layout & AV_CH_BACK_LEFT){ | |||
| matrix[ BACK_LEFT][ SIDE_LEFT]+= 1.0; | |||
| matrix[BACK_RIGHT][SIDE_RIGHT]+= 1.0; | |||
| }else if(s->out_ch_layout & AV_CH_BACK_CENTER){ | |||
| matrix[BACK_CENTER][ SIDE_LEFT]+= sqrt(0.5); | |||
| matrix[BACK_CENTER][SIDE_RIGHT]+= sqrt(0.5); | |||
| }else if(s->out_ch_layout & AV_CH_FRONT_LEFT){ | |||
| matrix[ FRONT_LEFT][ SIDE_LEFT]+= s->slev; | |||
| matrix[FRONT_RIGHT][SIDE_RIGHT]+= s->slev; | |||
| }else if(s->out_ch_layout & AV_CH_FRONT_CENTER){ | |||
| matrix[ FRONT_CENTER][SIDE_LEFT ]+= s->slev*sqrt(0.5); | |||
| matrix[ FRONT_CENTER][SIDE_RIGHT]+= s->slev*sqrt(0.5); | |||
| }else | |||
| av_assert0(0); | |||
| } | |||
| if(unaccounted & AV_CH_FRONT_LEFT_OF_CENTER){ | |||
| if(s->out_ch_layout & AV_CH_FRONT_LEFT){ | |||
| matrix[ FRONT_LEFT][ FRONT_LEFT_OF_CENTER]+= 1.0; | |||
| matrix[FRONT_RIGHT][FRONT_RIGHT_OF_CENTER]+= 1.0; | |||
| }else if(s->out_ch_layout & AV_CH_FRONT_CENTER){ | |||
| matrix[ FRONT_CENTER][ FRONT_LEFT_OF_CENTER]+= sqrt(0.5); | |||
| matrix[ FRONT_CENTER][FRONT_RIGHT_OF_CENTER]+= sqrt(0.5); | |||
| }else | |||
| av_assert0(0); | |||
| } | |||
| //FIXME quantize for integeres | |||
| for(out_i=i=0; i<64; i++){ | |||
| double sum=0; | |||
| int in_i=0; | |||
| int ch_in=0; | |||
| for(j=0; j<64; j++){ | |||
| s->matrix[out_i][in_i]= matrix[i][j]; | |||
| if(matrix[i][j]){ | |||
| s->matrix_ch[out_i][++ch_in]= in_i; | |||
| sum += fabs(matrix[i][j]); | |||
| } | |||
| if(s->in_ch_layout & (1ULL<<j)) | |||
| in_i++; | |||
| } | |||
| s->matrix_ch[out_i][0]= ch_in; | |||
| maxcoef= FFMAX(maxcoef, sum); | |||
| if(s->out_ch_layout & (1ULL<<i)) | |||
| out_i++; | |||
| } | |||
| if(( s->out_sample_fmt < AV_SAMPLE_FMT_FLT | |||
| || s->int_sample_fmt < AV_SAMPLE_FMT_FLT) && maxcoef > 1.0){ | |||
| for(i=0; i<SWR_CH_MAX; i++) | |||
| for(j=0; j<SWR_CH_MAX; j++) | |||
| s->matrix[i][j] /= maxcoef; | |||
| } | |||
| for(i=0; i<av_get_channel_layout_nb_channels(s->out_ch_layout); i++){ | |||
| for(j=0; j<av_get_channel_layout_nb_channels(s->in_ch_layout); j++){ | |||
| av_log(NULL, AV_LOG_ERROR, "%f ", s->matrix[i][j]); | |||
| } | |||
| av_log(NULL, AV_LOG_ERROR, "\n"); | |||
| } | |||
| return 0; | |||
| } | |||
| int swr_rematrix(SwrContext *s, AudioData *out, AudioData *in, int len, int mustcopy){ | |||
| int out_i, in_i, i, j; | |||
| av_assert0(out->ch_count == av_get_channel_layout_nb_channels(s->out_ch_layout)); | |||
| av_assert0(in ->ch_count == av_get_channel_layout_nb_channels(s-> in_ch_layout)); | |||
| for(out_i=0; out_i<out->ch_count; out_i++){ | |||
| switch(s->matrix_ch[out_i][0]){ | |||
| case 1: | |||
| in_i= s->matrix_ch[out_i][1]; | |||
| if(mustcopy || s->matrix[out_i][in_i]!=1.0){ | |||
| if(s->int_sample_fmt == AV_SAMPLE_FMT_FLT){ | |||
| copy_float(out->ch[out_i], in->ch[in_i], s->matrix[out_i][in_i], len); | |||
| }else | |||
| copy_s16 (out->ch[out_i], in->ch[in_i], s->matrix[out_i][in_i], len); | |||
| }else{ | |||
| out->ch[out_i]= in->ch[in_i]; | |||
| } | |||
| break; | |||
| case 2: | |||
| if(s->int_sample_fmt == AV_SAMPLE_FMT_FLT){ | |||
| sum2_float(out->ch[out_i], in->ch[ s->matrix_ch[out_i][1] ], in->ch[ s->matrix_ch[out_i][2] ], | |||
| s->matrix[out_i][ s->matrix_ch[out_i][1] ], s->matrix[out_i][ s->matrix_ch[out_i][2] ], | |||
| len); | |||
| }else{ | |||
| sum2_s16 (out->ch[out_i], in->ch[ s->matrix_ch[out_i][1] ], in->ch[ s->matrix_ch[out_i][2] ], | |||
| s->matrix[out_i][ s->matrix_ch[out_i][1] ], s->matrix[out_i][ s->matrix_ch[out_i][2] ], | |||
| len); | |||
| } | |||
| break; | |||
| default: | |||
| if(s->int_sample_fmt == AV_SAMPLE_FMT_FLT){ | |||
| for(i=0; i<len; i++){ | |||
| float v=0; | |||
| for(j=0; j<s->matrix_ch[out_i][0]; j++){ | |||
| in_i= s->matrix_ch[out_i][1+j]; | |||
| v+= ((float*)in->ch[in_i])[i] * s->matrix[out_i][in_i]; | |||
| } | |||
| ((float*)out->ch[out_i])[i]= v; | |||
| } | |||
| }else{ | |||
| for(i=0; i<len; i++){ | |||
| int v=0; | |||
| for(j=0; j<s->matrix_ch[out_i][0]; j++){ | |||
| in_i= s->matrix_ch[out_i][1+j]; | |||
| v+= ((int16_t*)in->ch[in_i])[i] * s->matrix[out_i][in_i]; //FIXME use int16 coeffs | |||
| } | |||
| ((int16_t*)out->ch[out_i])[i]= v; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| /* | |||
| * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at) | |||
| * | |||
| * This file is part of libswresample | |||
| * | |||
| * libswresample is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * libswresample is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with libswresample; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| static void RENAME(sum2)(SAMPLE *out, const SAMPLE *in1, const SAMPLE *in2, float coeff1, float coeff2, int len){ | |||
| int i; | |||
| for(i=0; i<len; i++) | |||
| out[i] = coeff1*in1[i] + coeff2*in2[i]; //FIXME better int16 | |||
| } | |||
| static void RENAME(copy)(SAMPLE *out, const SAMPLE *in, float coeff, int len){ | |||
| if(coeff == 1.0){ | |||
| memcpy(out, in, sizeof(SAMPLE)*len); | |||
| }else{ | |||
| int i; | |||
| for(i=0; i<len; i++) | |||
| out[i] = coeff*in[i]; //FIXME better int16 | |||
| } | |||
| } | |||
| @@ -0,0 +1,352 @@ | |||
| /* | |||
| * audio resampling | |||
| * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at> | |||
| * | |||
| * This file is part of FFmpeg. | |||
| * | |||
| * FFmpeg is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * FFmpeg is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with FFmpeg; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| /** | |||
| * @file | |||
| * audio resampling | |||
| * @author Michael Niedermayer <michaelni@gmx.at> | |||
| */ | |||
| #include "libavutil/log.h" | |||
| #include "swresample_internal.h" | |||
| #ifndef CONFIG_RESAMPLE_HP | |||
| #define FILTER_SHIFT 15 | |||
| #define FELEM int16_t | |||
| #define FELEM2 int32_t | |||
| #define FELEML int64_t | |||
| #define FELEM_MAX INT16_MAX | |||
| #define FELEM_MIN INT16_MIN | |||
| #define WINDOW_TYPE 9 | |||
| #elif !defined(CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE) | |||
| #define FILTER_SHIFT 30 | |||
| #define FELEM int32_t | |||
| #define FELEM2 int64_t | |||
| #define FELEML int64_t | |||
| #define FELEM_MAX INT32_MAX | |||
| #define FELEM_MIN INT32_MIN | |||
| #define WINDOW_TYPE 12 | |||
| #else | |||
| #define FILTER_SHIFT 0 | |||
| #define FELEM double | |||
| #define FELEM2 double | |||
| #define FELEML double | |||
| #define WINDOW_TYPE 24 | |||
| #endif | |||
| typedef struct AVResampleContext{ | |||
| const AVClass *av_class; | |||
| FELEM *filter_bank; | |||
| int filter_length; | |||
| int ideal_dst_incr; | |||
| int dst_incr; | |||
| int index; | |||
| int frac; | |||
| int src_incr; | |||
| int compensation_distance; | |||
| int phase_shift; | |||
| int phase_mask; | |||
| int linear; | |||
| double factor; | |||
| }AVResampleContext; | |||
| /** | |||
| * 0th order modified bessel function of the first kind. | |||
| */ | |||
| static double bessel(double x){ | |||
| double v=1; | |||
| double lastv=0; | |||
| double t=1; | |||
| int i; | |||
| static const double inv[100]={ | |||
| 1.0/( 1* 1), 1.0/( 2* 2), 1.0/( 3* 3), 1.0/( 4* 4), 1.0/( 5* 5), 1.0/( 6* 6), 1.0/( 7* 7), 1.0/( 8* 8), 1.0/( 9* 9), 1.0/(10*10), | |||
| 1.0/(11*11), 1.0/(12*12), 1.0/(13*13), 1.0/(14*14), 1.0/(15*15), 1.0/(16*16), 1.0/(17*17), 1.0/(18*18), 1.0/(19*19), 1.0/(20*20), | |||
| 1.0/(21*21), 1.0/(22*22), 1.0/(23*23), 1.0/(24*24), 1.0/(25*25), 1.0/(26*26), 1.0/(27*27), 1.0/(28*28), 1.0/(29*29), 1.0/(30*30), | |||
| 1.0/(31*31), 1.0/(32*32), 1.0/(33*33), 1.0/(34*34), 1.0/(35*35), 1.0/(36*36), 1.0/(37*37), 1.0/(38*38), 1.0/(39*39), 1.0/(40*40), | |||
| 1.0/(41*41), 1.0/(42*42), 1.0/(43*43), 1.0/(44*44), 1.0/(45*45), 1.0/(46*46), 1.0/(47*47), 1.0/(48*48), 1.0/(49*49), 1.0/(50*50), | |||
| 1.0/(51*51), 1.0/(52*52), 1.0/(53*53), 1.0/(54*54), 1.0/(55*55), 1.0/(56*56), 1.0/(57*57), 1.0/(58*58), 1.0/(59*59), 1.0/(60*60), | |||
| 1.0/(61*61), 1.0/(62*62), 1.0/(63*63), 1.0/(64*64), 1.0/(65*65), 1.0/(66*66), 1.0/(67*67), 1.0/(68*68), 1.0/(69*69), 1.0/(70*70), | |||
| 1.0/(71*71), 1.0/(72*72), 1.0/(73*73), 1.0/(74*74), 1.0/(75*75), 1.0/(76*76), 1.0/(77*77), 1.0/(78*78), 1.0/(79*79), 1.0/(80*80), | |||
| 1.0/(81*81), 1.0/(82*82), 1.0/(83*83), 1.0/(84*84), 1.0/(85*85), 1.0/(86*86), 1.0/(87*87), 1.0/(88*88), 1.0/(89*89), 1.0/(90*90), | |||
| 1.0/(91*91), 1.0/(92*92), 1.0/(93*93), 1.0/(94*94), 1.0/(95*95), 1.0/(96*96), 1.0/(97*97), 1.0/(98*98), 1.0/(99*99), 1.0/(10000) | |||
| }; | |||
| x= x*x/4; | |||
| for(i=0; v != lastv; i++){ | |||
| lastv=v; | |||
| t *= x*inv[i]; | |||
| v += t; | |||
| } | |||
| return v; | |||
| } | |||
| /** | |||
| * builds a polyphase filterbank. | |||
| * @param factor resampling factor | |||
| * @param scale wanted sum of coefficients for each filter | |||
| * @param type 0->cubic, 1->blackman nuttall windowed sinc, 2..16->kaiser windowed sinc beta=2..16 | |||
| * @return 0 on success, negative on error | |||
| */ | |||
| static int build_filter(FELEM *filter, double factor, int tap_count, int phase_count, int scale, int type){ | |||
| int ph, i; | |||
| double x, y, w; | |||
| double *tab = av_malloc(tap_count * sizeof(*tab)); | |||
| const int center= (tap_count-1)/2; | |||
| if (!tab) | |||
| return AVERROR(ENOMEM); | |||
| /* if upsampling, only need to interpolate, no filter */ | |||
| if (factor > 1.0) | |||
| factor = 1.0; | |||
| for(ph=0;ph<phase_count;ph++) { | |||
| double norm = 0; | |||
| for(i=0;i<tap_count;i++) { | |||
| x = M_PI * ((double)(i - center) - (double)ph / phase_count) * factor; | |||
| if (x == 0) y = 1.0; | |||
| else y = sin(x) / x; | |||
| switch(type){ | |||
| case 0:{ | |||
| const float d= -0.5; //first order derivative = -0.5 | |||
| x = fabs(((double)(i - center) - (double)ph / phase_count) * factor); | |||
| if(x<1.0) y= 1 - 3*x*x + 2*x*x*x + d*( -x*x + x*x*x); | |||
| else y= d*(-4 + 8*x - 5*x*x + x*x*x); | |||
| break;} | |||
| case 1: | |||
| w = 2.0*x / (factor*tap_count) + M_PI; | |||
| y *= 0.3635819 - 0.4891775 * cos(w) + 0.1365995 * cos(2*w) - 0.0106411 * cos(3*w); | |||
| break; | |||
| default: | |||
| w = 2.0*x / (factor*tap_count*M_PI); | |||
| y *= bessel(type*sqrt(FFMAX(1-w*w, 0))); | |||
| break; | |||
| } | |||
| tab[i] = y; | |||
| norm += y; | |||
| } | |||
| /* normalize so that an uniform color remains the same */ | |||
| for(i=0;i<tap_count;i++) { | |||
| #ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE | |||
| filter[ph * tap_count + i] = tab[i] / norm; | |||
| #else | |||
| filter[ph * tap_count + i] = av_clip(lrintf(tab[i] * scale / norm), FELEM_MIN, FELEM_MAX); | |||
| #endif | |||
| } | |||
| } | |||
| #if 0 | |||
| { | |||
| #define LEN 1024 | |||
| int j,k; | |||
| double sine[LEN + tap_count]; | |||
| double filtered[LEN]; | |||
| double maxff=-2, minff=2, maxsf=-2, minsf=2; | |||
| for(i=0; i<LEN; i++){ | |||
| double ss=0, sf=0, ff=0; | |||
| for(j=0; j<LEN+tap_count; j++) | |||
| sine[j]= cos(i*j*M_PI/LEN); | |||
| for(j=0; j<LEN; j++){ | |||
| double sum=0; | |||
| ph=0; | |||
| for(k=0; k<tap_count; k++) | |||
| sum += filter[ph * tap_count + k] * sine[k+j]; | |||
| filtered[j]= sum / (1<<FILTER_SHIFT); | |||
| ss+= sine[j + center] * sine[j + center]; | |||
| ff+= filtered[j] * filtered[j]; | |||
| sf+= sine[j + center] * filtered[j]; | |||
| } | |||
| ss= sqrt(2*ss/LEN); | |||
| ff= sqrt(2*ff/LEN); | |||
| sf= 2*sf/LEN; | |||
| maxff= FFMAX(maxff, ff); | |||
| minff= FFMIN(minff, ff); | |||
| maxsf= FFMAX(maxsf, sf); | |||
| minsf= FFMIN(minsf, sf); | |||
| if(i%11==0){ | |||
| av_log(NULL, AV_LOG_ERROR, "i:%4d ss:%f ff:%13.6e-%13.6e sf:%13.6e-%13.6e\n", i, ss, maxff, minff, maxsf, minsf); | |||
| minff=minsf= 2; | |||
| maxff=maxsf= -2; | |||
| } | |||
| } | |||
| } | |||
| #endif | |||
| av_free(tab); | |||
| return 0; | |||
| } | |||
| AVResampleContext *swr_resample_init(AVResampleContext *c, int out_rate, int in_rate, int filter_size, int phase_shift, int linear, double cutoff){ | |||
| double factor= FFMIN(out_rate * cutoff / in_rate, 1.0); | |||
| int phase_count= 1<<phase_shift; | |||
| if(!c || c->phase_shift!=phase_shift || c->linear!=linear || c->factor != factor | |||
| || c->filter_length!=FFMAX((int)ceil(filter_size/factor), 1)){ | |||
| c= av_mallocz(sizeof(AVResampleContext)); | |||
| if (!c) | |||
| return NULL; | |||
| c->phase_shift= phase_shift; | |||
| c->phase_mask= phase_count-1; | |||
| c->linear= linear; | |||
| c->factor= factor; | |||
| c->filter_length= FFMAX((int)ceil(filter_size/factor), 1); | |||
| c->filter_bank= av_mallocz(c->filter_length*(phase_count+1)*sizeof(FELEM)); | |||
| if (!c->filter_bank) | |||
| goto error; | |||
| if (build_filter(c->filter_bank, factor, c->filter_length, phase_count, 1<<FILTER_SHIFT, WINDOW_TYPE)) | |||
| goto error; | |||
| memcpy(&c->filter_bank[c->filter_length*phase_count+1], c->filter_bank, (c->filter_length-1)*sizeof(FELEM)); | |||
| c->filter_bank[c->filter_length*phase_count]= c->filter_bank[c->filter_length - 1]; | |||
| } | |||
| c->compensation_distance= 0; | |||
| c->src_incr= out_rate; | |||
| c->ideal_dst_incr= c->dst_incr= in_rate * phase_count; | |||
| c->index= -phase_count*((c->filter_length-1)/2); | |||
| c->frac= 0; | |||
| return c; | |||
| error: | |||
| av_free(c->filter_bank); | |||
| av_free(c); | |||
| return NULL; | |||
| } | |||
| void swr_resample_free(AVResampleContext **c){ | |||
| if(!*c) | |||
| return; | |||
| av_freep(&(*c)->filter_bank); | |||
| av_freep(c); | |||
| } | |||
| void swr_compensate(struct SwrContext *s, int sample_delta, int compensation_distance){ | |||
| AVResampleContext *c= s->resample; | |||
| // sample_delta += (c->ideal_dst_incr - c->dst_incr)*(int64_t)c->compensation_distance / c->ideal_dst_incr; | |||
| c->compensation_distance= compensation_distance; | |||
| c->dst_incr = c->ideal_dst_incr - c->ideal_dst_incr * (int64_t)sample_delta / compensation_distance; | |||
| } | |||
| int swr_resample(AVResampleContext *c, short *dst, short *src, int *consumed, int src_size, int dst_size, int update_ctx){ | |||
| int dst_index, i; | |||
| int index= c->index; | |||
| int frac= c->frac; | |||
| int dst_incr_frac= c->dst_incr % c->src_incr; | |||
| int dst_incr= c->dst_incr / c->src_incr; | |||
| int compensation_distance= c->compensation_distance; | |||
| if(compensation_distance == 0 && c->filter_length == 1 && c->phase_shift==0){ | |||
| int64_t index2= ((int64_t)index)<<32; | |||
| int64_t incr= (1LL<<32) * c->dst_incr / c->src_incr; | |||
| dst_size= FFMIN(dst_size, (src_size-1-index) * (int64_t)c->src_incr / c->dst_incr); | |||
| for(dst_index=0; dst_index < dst_size; dst_index++){ | |||
| dst[dst_index] = src[index2>>32]; | |||
| index2 += incr; | |||
| } | |||
| frac += dst_index * dst_incr_frac; | |||
| index += dst_index * dst_incr; | |||
| index += frac / c->src_incr; | |||
| frac %= c->src_incr; | |||
| }else{ | |||
| for(dst_index=0; dst_index < dst_size; dst_index++){ | |||
| FELEM *filter= c->filter_bank + c->filter_length*(index & c->phase_mask); | |||
| int sample_index= index >> c->phase_shift; | |||
| FELEM2 val=0; | |||
| if(sample_index < 0){ | |||
| for(i=0; i<c->filter_length; i++) | |||
| val += src[FFABS(sample_index + i) % src_size] * filter[i]; | |||
| }else if(sample_index + c->filter_length > src_size){ | |||
| break; | |||
| }else if(c->linear){ | |||
| FELEM2 v2=0; | |||
| for(i=0; i<c->filter_length; i++){ | |||
| val += src[sample_index + i] * (FELEM2)filter[i]; | |||
| v2 += src[sample_index + i] * (FELEM2)filter[i + c->filter_length]; | |||
| } | |||
| val+=(v2-val)*(FELEML)frac / c->src_incr; | |||
| }else{ | |||
| for(i=0; i<c->filter_length; i++){ | |||
| val += src[sample_index + i] * (FELEM2)filter[i]; | |||
| } | |||
| } | |||
| #ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE | |||
| dst[dst_index] = av_clip_int16(lrintf(val)); | |||
| #else | |||
| val = (val + (1<<(FILTER_SHIFT-1)))>>FILTER_SHIFT; | |||
| dst[dst_index] = (unsigned)(val + 32768) > 65535 ? (val>>31) ^ 32767 : val; | |||
| #endif | |||
| frac += dst_incr_frac; | |||
| index += dst_incr; | |||
| if(frac >= c->src_incr){ | |||
| frac -= c->src_incr; | |||
| index++; | |||
| } | |||
| if(dst_index + 1 == compensation_distance){ | |||
| compensation_distance= 0; | |||
| dst_incr_frac= c->ideal_dst_incr % c->src_incr; | |||
| dst_incr= c->ideal_dst_incr / c->src_incr; | |||
| } | |||
| } | |||
| } | |||
| *consumed= FFMAX(index, 0) >> c->phase_shift; | |||
| if(index>=0) index &= c->phase_mask; | |||
| if(compensation_distance){ | |||
| compensation_distance -= dst_index; | |||
| assert(compensation_distance > 0); | |||
| } | |||
| if(update_ctx){ | |||
| c->frac= frac; | |||
| c->index= index; | |||
| c->dst_incr= dst_incr_frac + c->src_incr*dst_incr; | |||
| c->compensation_distance= compensation_distance; | |||
| } | |||
| #if 0 | |||
| if(update_ctx && !c->compensation_distance){ | |||
| #undef rand | |||
| av_resample_compensate(c, rand() % (8000*2) - 8000, 8000*2); | |||
| av_log(NULL, AV_LOG_DEBUG, "%d %d %d\n", c->dst_incr, c->ideal_dst_incr, c->compensation_distance); | |||
| } | |||
| #endif | |||
| return dst_index; | |||
| } | |||
| int swr_multiple_resample(AVResampleContext *c, AudioData *dst, int dst_size, AudioData *src, int src_size, int *consumed){ | |||
| int i, ret= -1; | |||
| for(i=0; i<dst->ch_count; i++){ | |||
| ret= swr_resample(c, dst->ch[i], src->ch[i], consumed, src_size, dst_size, i+1==dst->ch_count); | |||
| } | |||
| return ret; | |||
| } | |||
| @@ -0,0 +1,432 @@ | |||
| /* | |||
| * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at) | |||
| * | |||
| * This file is part of libswresample | |||
| * | |||
| * libswresample is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * libswresample is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with libswresample; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #include "libavutil/opt.h" | |||
| #include "swresample_internal.h" | |||
| #include "audioconvert.h" | |||
| #include "libavutil/avassert.h" | |||
| #define C30DB M_SQRT2 | |||
| #define C15DB 1.189207115 | |||
| #define C__0DB 1.0 | |||
| #define C_15DB 0.840896415 | |||
| #define C_30DB M_SQRT1_2 | |||
| #define C_45DB 0.594603558 | |||
| #define C_60DB 0.5 | |||
| //TODO split options array out? | |||
| #define OFFSET(x) offsetof(SwrContext,x) | |||
| static const AVOption options[]={ | |||
| {"ich", "input channel count", OFFSET( in.ch_count ), FF_OPT_TYPE_INT, {.dbl=2}, 1, SWR_CH_MAX, 0}, | |||
| {"och", "output channel count", OFFSET(out.ch_count ), FF_OPT_TYPE_INT, {.dbl=2}, 1, SWR_CH_MAX, 0}, | |||
| {"isr", "input sample rate" , OFFSET( in_sample_rate), FF_OPT_TYPE_INT, {.dbl=48000}, 1, INT_MAX, 0}, | |||
| {"osr", "output sample rate" , OFFSET(out_sample_rate), FF_OPT_TYPE_INT, {.dbl=48000}, 1, INT_MAX, 0}, | |||
| {"ip" , "input planar" , OFFSET( in.planar ), FF_OPT_TYPE_INT, {.dbl=0}, 0, 1, 0}, | |||
| {"op" , "output planar" , OFFSET(out.planar ), FF_OPT_TYPE_INT, {.dbl=0}, 0, 1, 0}, | |||
| {"isf", "input sample format", OFFSET( in_sample_fmt ), FF_OPT_TYPE_INT, {.dbl=AV_SAMPLE_FMT_S16}, 0, AV_SAMPLE_FMT_NB-1, 0}, | |||
| {"osf", "output sample format", OFFSET(out_sample_fmt ), FF_OPT_TYPE_INT, {.dbl=AV_SAMPLE_FMT_S16}, 0, AV_SAMPLE_FMT_NB-1, 0}, | |||
| {"tsf", "internal sample format", OFFSET(int_sample_fmt ), FF_OPT_TYPE_INT, {.dbl=AV_SAMPLE_FMT_NONE}, -1, AV_SAMPLE_FMT_FLT, 0}, | |||
| {"icl", "input channel layout" , OFFSET( in_ch_layout), FF_OPT_TYPE_INT64, {.dbl=0}, 0, INT64_MAX, 0, "channel_layout"}, | |||
| {"ocl", "output channel layout", OFFSET(out_ch_layout), FF_OPT_TYPE_INT64, {.dbl=0}, 0, INT64_MAX, 0, "channel_layout"}, | |||
| {"clev", "center mix level" , OFFSET(clev) , FF_OPT_TYPE_FLOAT, {.dbl=C_30DB}, 0, 4, 0}, | |||
| {"slev", "sourround mix level" , OFFSET(slev) , FF_OPT_TYPE_FLOAT, {.dbl=C_30DB}, 0, 4, 0}, | |||
| {"flags", NULL , OFFSET(flags) , FF_OPT_TYPE_FLAGS, {.dbl=0}, 0, UINT_MAX, 0, "flags"}, | |||
| {"res", "force resampling", 0, FF_OPT_TYPE_CONST, {.dbl=SWR_FLAG_RESAMPLE}, INT_MIN, INT_MAX, 0, "flags"}, | |||
| {0} | |||
| }; | |||
| static const char* context_to_name(void* ptr) { | |||
| return "SWR"; | |||
| } | |||
| static const AVClass av_class = { "SwrContext", context_to_name, options, LIBAVUTIL_VERSION_INT, OFFSET(log_level_offset), OFFSET(log_ctx) }; | |||
| static int resample(SwrContext *s, AudioData *out_param, int out_count, | |||
| const AudioData * in_param, int in_count); | |||
| SwrContext *swr_alloc(void){ | |||
| SwrContext *s= av_mallocz(sizeof(SwrContext)); | |||
| if(s){ | |||
| s->av_class= &av_class; | |||
| av_opt_set_defaults2(s, 0, 0); | |||
| } | |||
| return s; | |||
| } | |||
| SwrContext *swr_alloc2(struct SwrContext *s, int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate, | |||
| int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate, | |||
| int log_offset, void *log_ctx){ | |||
| if(!s) s= swr_alloc(); | |||
| if(!s) return NULL; | |||
| s->log_level_offset= log_offset; | |||
| s->log_ctx= log_ctx; | |||
| av_set_int(s, "ocl", out_ch_layout); | |||
| av_set_int(s, "osf", out_sample_fmt); | |||
| av_set_int(s, "osr", out_sample_rate); | |||
| av_set_int(s, "icl", in_ch_layout); | |||
| av_set_int(s, "isf", in_sample_fmt); | |||
| av_set_int(s, "isr", in_sample_rate); | |||
| s-> in.ch_count= av_get_channel_layout_nb_channels(s-> in_ch_layout); | |||
| s->out.ch_count= av_get_channel_layout_nb_channels(s->out_ch_layout); | |||
| s->int_sample_fmt = AV_SAMPLE_FMT_S16; | |||
| return s; | |||
| } | |||
| static void free_temp(AudioData *a){ | |||
| av_free(a->data); | |||
| memset(a, 0, sizeof(*a)); | |||
| } | |||
| void swr_free(SwrContext **ss){ | |||
| SwrContext *s= *ss; | |||
| if(s){ | |||
| free_temp(&s->postin); | |||
| free_temp(&s->midbuf); | |||
| free_temp(&s->preout); | |||
| free_temp(&s->in_buffer); | |||
| swr_audio_convert_free(&s-> in_convert); | |||
| swr_audio_convert_free(&s->out_convert); | |||
| swr_resample_free(&s->resample); | |||
| } | |||
| av_freep(ss); | |||
| } | |||
| static int64_t guess_layout(int ch){ | |||
| switch(ch){ | |||
| case 1: return AV_CH_LAYOUT_MONO; | |||
| case 2: return AV_CH_LAYOUT_STEREO; | |||
| case 5: return AV_CH_LAYOUT_5POINT0; | |||
| case 6: return AV_CH_LAYOUT_5POINT1; | |||
| case 7: return AV_CH_LAYOUT_7POINT0; | |||
| case 8: return AV_CH_LAYOUT_7POINT1; | |||
| default: return 0; | |||
| } | |||
| } | |||
| int swr_init(SwrContext *s){ | |||
| s->in_buffer_index= 0; | |||
| s->in_buffer_count= 0; | |||
| s->resample_in_constraint= 0; | |||
| free_temp(&s->postin); | |||
| free_temp(&s->midbuf); | |||
| free_temp(&s->preout); | |||
| free_temp(&s->in_buffer); | |||
| swr_audio_convert_free(&s-> in_convert); | |||
| swr_audio_convert_free(&s->out_convert); | |||
| //We assume AVOptions checked the various values and the defaults where allowed | |||
| if( s->int_sample_fmt != AV_SAMPLE_FMT_S16 | |||
| &&s->int_sample_fmt != AV_SAMPLE_FMT_FLT){ | |||
| av_log(s, AV_LOG_ERROR, "Requested sample format %s is not supported internally, only float & S16 is supported\n", av_get_sample_fmt_name(s->int_sample_fmt)); | |||
| return AVERROR(EINVAL); | |||
| } | |||
| //FIXME should we allow/support using FLT on material that doesnt need it ? | |||
| if(s->in_sample_fmt <= AV_SAMPLE_FMT_S16 || s->int_sample_fmt==AV_SAMPLE_FMT_S16){ | |||
| s->int_sample_fmt= AV_SAMPLE_FMT_S16; | |||
| }else | |||
| s->int_sample_fmt= AV_SAMPLE_FMT_FLT; | |||
| if (s->out_sample_rate!=s->in_sample_rate || (s->flags & SWR_FLAG_RESAMPLE)){ | |||
| s->resample = swr_resample_init(s->resample, s->out_sample_rate, s->in_sample_rate, 16, 10, 0, 0.8); | |||
| }else | |||
| swr_resample_free(&s->resample); | |||
| if(s->int_sample_fmt != AV_SAMPLE_FMT_S16 && s->resample){ | |||
| av_log(s, AV_LOG_ERROR, "Resampling only supported with internal s16 currently\n"); //FIXME | |||
| return -1; | |||
| } | |||
| if(!s-> in_ch_layout) | |||
| s-> in_ch_layout= guess_layout(s->in.ch_count); | |||
| if(!s->out_ch_layout) | |||
| s->out_ch_layout= guess_layout(s->out.ch_count); | |||
| s->rematrix= s->out_ch_layout !=s->in_ch_layout; | |||
| #define RSC 1 //FIXME finetune | |||
| if(!s-> in.ch_count) | |||
| s-> in.ch_count= av_get_channel_layout_nb_channels(s-> in_ch_layout); | |||
| if(!s->out.ch_count) | |||
| s->out.ch_count= av_get_channel_layout_nb_channels(s->out_ch_layout); | |||
| av_assert0(s-> in.ch_count); | |||
| av_assert0(s->out.ch_count); | |||
| s->resample_first= RSC*s->out.ch_count/s->in.ch_count - RSC < s->out_sample_rate/(float)s-> in_sample_rate - 1.0; | |||
| s-> in.bps= av_get_bits_per_sample_fmt(s-> in_sample_fmt)/8; | |||
| s->int_bps= av_get_bits_per_sample_fmt(s->int_sample_fmt)/8; | |||
| s->out.bps= av_get_bits_per_sample_fmt(s->out_sample_fmt)/8; | |||
| s->in_convert = swr_audio_convert_alloc(s->int_sample_fmt, | |||
| s-> in_sample_fmt, s-> in.ch_count, 0); | |||
| s->out_convert= swr_audio_convert_alloc(s->out_sample_fmt, | |||
| s->int_sample_fmt, s->out.ch_count, 0); | |||
| s->postin= s->in; | |||
| s->preout= s->out; | |||
| s->midbuf= s->in; | |||
| s->in_buffer= s->in; | |||
| if(!s->resample_first){ | |||
| s->midbuf.ch_count= s->out.ch_count; | |||
| s->in_buffer.ch_count = s->out.ch_count; | |||
| } | |||
| s->in_buffer.bps = s->postin.bps = s->midbuf.bps = s->preout.bps = s->int_bps; | |||
| s->in_buffer.planar = s->postin.planar = s->midbuf.planar = s->preout.planar = 1; | |||
| if(s->rematrix && swr_rematrix_init(s)<0) | |||
| return -1; | |||
| return 0; | |||
| } | |||
| static int realloc_audio(AudioData *a, int count){ | |||
| int i, countb; | |||
| AudioData old; | |||
| if(a->count >= count) | |||
| return 0; | |||
| count*=2; | |||
| countb= FFALIGN(count*a->bps, 32); | |||
| old= *a; | |||
| av_assert0(a->planar); | |||
| av_assert0(a->bps); | |||
| av_assert0(a->ch_count); | |||
| a->data= av_malloc(countb*a->ch_count); | |||
| if(!a->data) | |||
| return AVERROR(ENOMEM); | |||
| for(i=0; i<a->ch_count; i++){ | |||
| a->ch[i]= a->data + i*(a->planar ? countb : a->bps); | |||
| if(a->planar) memcpy(a->ch[i], old.ch[i], a->count*a->bps); | |||
| } | |||
| av_free(old.data); | |||
| a->count= count; | |||
| return 1; | |||
| } | |||
| static void copy(AudioData *out, AudioData *in, | |||
| int count){ | |||
| av_assert0(out->planar == in->planar); | |||
| av_assert0(out->bps == in->bps); | |||
| av_assert0(out->ch_count == in->ch_count); | |||
| if(out->planar){ | |||
| int ch; | |||
| for(ch=0; ch<out->ch_count; ch++) | |||
| memcpy(out->ch[ch], in->ch[ch], count*out->bps); | |||
| }else | |||
| memcpy(out->ch[0], in->ch[0], count*out->ch_count*out->bps); | |||
| } | |||
| int swr_convert(struct SwrContext *s, uint8_t *out_arg[SWR_CH_MAX], int out_count, | |||
| const uint8_t *in_arg [SWR_CH_MAX], int in_count){ | |||
| AudioData *postin, *midbuf, *preout; | |||
| int ret, i/*, in_max*/; | |||
| AudioData * in= &s->in; | |||
| AudioData *out= &s->out; | |||
| AudioData preout_tmp, midbuf_tmp; | |||
| if(!s->resample){ | |||
| if(in_count > out_count) | |||
| return -1; | |||
| out_count = in_count; | |||
| } | |||
| av_assert0(in ->planar == 0); | |||
| av_assert0(out->planar == 0); | |||
| for(i=0; i<s-> in.ch_count; i++) | |||
| in ->ch[i]= in_arg[0] + i* in->bps; | |||
| for(i=0; i<s->out.ch_count; i++) | |||
| out->ch[i]= out_arg[0] + i*out->bps; | |||
| // in_max= out_count*(int64_t)s->in_sample_rate / s->out_sample_rate + resample_filter_taps; | |||
| // in_count= FFMIN(in_count, in_in + 2 - s->hist_buffer_count); | |||
| if((ret=realloc_audio(&s->postin, in_count))<0) | |||
| return ret; | |||
| if(s->resample_first){ | |||
| av_assert0(s->midbuf.ch_count == s-> in.ch_count); | |||
| if((ret=realloc_audio(&s->midbuf, out_count))<0) | |||
| return ret; | |||
| }else{ | |||
| av_assert0(s->midbuf.ch_count == s->out.ch_count); | |||
| if((ret=realloc_audio(&s->midbuf, in_count))<0) | |||
| return ret; | |||
| } | |||
| if((ret=realloc_audio(&s->preout, out_count))<0) | |||
| return ret; | |||
| postin= &s->postin; | |||
| midbuf_tmp= s->midbuf; | |||
| midbuf= &midbuf_tmp; | |||
| preout_tmp= s->preout; | |||
| preout= &preout_tmp; | |||
| if(s->int_sample_fmt == s-> in_sample_fmt && s->in.planar) | |||
| postin= in; | |||
| if(s->resample_first ? !s->resample : !s->rematrix) | |||
| midbuf= postin; | |||
| if(s->resample_first ? !s->rematrix : !s->resample) | |||
| preout= midbuf; | |||
| if(s->int_sample_fmt == s->out_sample_fmt && s->out.planar){ | |||
| if(preout==in){ | |||
| out_count= FFMIN(out_count, in_count); //TODO check at teh end if this is needed or redundant | |||
| av_assert0(s->in.planar); //we only support planar internally so it has to be, we support copying non planar though | |||
| copy(out, in, out_count); | |||
| return out_count; | |||
| } | |||
| else if(preout==postin) preout= midbuf= postin= out; | |||
| else if(preout==midbuf) preout= midbuf= out; | |||
| else preout= out; | |||
| } | |||
| if(in != postin){ | |||
| swr_audio_convert(s->in_convert, postin, in, in_count); | |||
| } | |||
| if(s->resample_first){ | |||
| if(postin != midbuf) | |||
| out_count= resample(s, midbuf, out_count, postin, in_count); | |||
| if(midbuf != preout) | |||
| swr_rematrix(s, preout, midbuf, out_count, preout==out); | |||
| }else{ | |||
| if(postin != midbuf) | |||
| swr_rematrix(s, midbuf, postin, in_count, midbuf==out); | |||
| if(midbuf != preout) | |||
| out_count= resample(s, preout, out_count, midbuf, in_count); | |||
| } | |||
| if(preout != out){ | |||
| //FIXME packed doesnt need more than 1 chan here! | |||
| swr_audio_convert(s->out_convert, out, preout, out_count); | |||
| } | |||
| return out_count; | |||
| } | |||
| /** | |||
| * | |||
| * out may be equal in. | |||
| */ | |||
| static void buf_set(AudioData *out, AudioData *in, int count){ | |||
| if(in->planar){ | |||
| int ch; | |||
| for(ch=0; ch<out->ch_count; ch++) | |||
| out->ch[ch]= in->ch[ch] + count*out->bps; | |||
| }else | |||
| out->ch[0]= in->ch[0] + count*out->ch_count*out->bps; | |||
| } | |||
| /** | |||
| * | |||
| * @return number of samples output per channel | |||
| */ | |||
| static int resample(SwrContext *s, AudioData *out_param, int out_count, | |||
| const AudioData * in_param, int in_count){ | |||
| AudioData in, out, tmp; | |||
| int ret_sum=0; | |||
| int border=0; | |||
| int ch_count= s->resample_first ? s->in.ch_count : s->out.ch_count; | |||
| tmp=out=*out_param; | |||
| in = *in_param; | |||
| do{ | |||
| int ret, size, consumed; | |||
| if(!s->resample_in_constraint && s->in_buffer_count){ | |||
| buf_set(&tmp, &s->in_buffer, s->in_buffer_index); | |||
| ret= swr_multiple_resample(s->resample, &out, out_count, &tmp, s->in_buffer_count, &consumed); | |||
| out_count -= ret; | |||
| ret_sum += ret; | |||
| buf_set(&out, &out, ret); | |||
| s->in_buffer_count -= consumed; | |||
| s->in_buffer_index += consumed; | |||
| if(!in_count) | |||
| break; | |||
| if(s->in_buffer_count <= border){ | |||
| buf_set(&in, &in, -s->in_buffer_count); | |||
| in_count += s->in_buffer_count; | |||
| s->in_buffer_count=0; | |||
| s->in_buffer_index=0; | |||
| border = 0; | |||
| } | |||
| } | |||
| if(in_count && !s->in_buffer_count){ | |||
| s->in_buffer_index=0; | |||
| ret= swr_multiple_resample(s->resample, &out, out_count, &in, in_count, &consumed); | |||
| out_count -= ret; | |||
| ret_sum += ret; | |||
| buf_set(&out, &out, ret); | |||
| in_count -= consumed; | |||
| buf_set(&in, &in, consumed); | |||
| } | |||
| //TODO is this check sane considering the advanced copy avoidance below | |||
| size= s->in_buffer_index + s->in_buffer_count + in_count; | |||
| if( size > s->in_buffer.count | |||
| && s->in_buffer_count + in_count <= s->in_buffer_index){ | |||
| buf_set(&tmp, &s->in_buffer, s->in_buffer_index); | |||
| copy(&s->in_buffer, &tmp, s->in_buffer_count); | |||
| s->in_buffer_index=0; | |||
| }else | |||
| if((ret=realloc_audio(&s->in_buffer, size)) < 0) | |||
| return ret; | |||
| if(in_count){ | |||
| int count= in_count; | |||
| if(s->in_buffer_count && s->in_buffer_count+2 < count && out_count) count= s->in_buffer_count+2; | |||
| buf_set(&tmp, &s->in_buffer, s->in_buffer_index + s->in_buffer_count); | |||
| copy(&tmp, &in, /*in_*/count); | |||
| s->in_buffer_count += count; | |||
| in_count -= count; | |||
| border += count; | |||
| buf_set(&in, &in, count); | |||
| s->resample_in_constraint= 0; | |||
| if(s->in_buffer_count != count || in_count) | |||
| continue; | |||
| } | |||
| break; | |||
| }while(1); | |||
| s->resample_in_constraint= !!out_count; | |||
| return ret_sum; | |||
| } | |||
| @@ -0,0 +1,79 @@ | |||
| /* | |||
| * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at) | |||
| * | |||
| * This file is part of libswresample | |||
| * | |||
| * libswresample is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * libswresample is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with libswresample; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #ifndef SWR_H | |||
| #define SWR_H | |||
| #include <inttypes.h> | |||
| #include "libavutil/samplefmt.h" | |||
| #define LIBSWRESAMPLE_VERSION_MAJOR 0 | |||
| #define LIBSWRESAMPLE_VERSION_MINOR 0 | |||
| #define LIBSWRESAMPLE_VERSION_MICRO 0 | |||
| #define SWR_CH_MAX 16 | |||
| #define SWR_FLAG_RESAMPLE 1///< Force resampling even if equal sample rate | |||
| //TODO use int resample ? | |||
| //long term TODO can we enable this dynamically? | |||
| struct SwrContext; | |||
| /** | |||
| * Allocate SwrContext. | |||
| * @see swr_init(),swr_free() | |||
| * @return NULL on error | |||
| */ | |||
| struct SwrContext *swr_alloc(void); | |||
| /** | |||
| * Initialize context after user parameters have been set. | |||
| * @return negativo n error | |||
| */ | |||
| int swr_init(struct SwrContext *s); | |||
| /** | |||
| * Allocate SwrContext. | |||
| * @see swr_init(),swr_free() | |||
| * @return NULL on error | |||
| */ | |||
| struct SwrContext *swr_alloc2(struct SwrContext *s, int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate, | |||
| int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate, | |||
| int log_offset, void *log_ctx); | |||
| /** | |||
| * Free the given SwrContext. | |||
| * And set the pointer to NULL | |||
| */ | |||
| void swr_free(struct SwrContext **s); | |||
| /** | |||
| * Convert audio. | |||
| * @param in_count Number of input samples available in one channel. | |||
| * @param out_count Amount of space available for output in samples per channel. | |||
| * @return number of samples output per channel | |||
| */ | |||
| int swr_convert(struct SwrContext *s, uint8_t *out[SWR_CH_MAX], int out_count, | |||
| const uint8_t *in [SWR_CH_MAX], int in_count); | |||
| void swr_compensate(struct SwrContext *s, int sample_delta, int compensation_distance); | |||
| #endif | |||
| @@ -0,0 +1,77 @@ | |||
| /* | |||
| * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at) | |||
| * | |||
| * This file is part of libswresample | |||
| * | |||
| * libswresample is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * libswresample is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with libswresample; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #ifndef SWR_INTERNAL_H | |||
| #define SWR_INTERNAL_H | |||
| #include "swresample.h" | |||
| typedef struct AudioData{ | |||
| uint8_t *ch[SWR_CH_MAX]; | |||
| uint8_t *data; | |||
| int ch_count; | |||
| int bps; | |||
| int count; | |||
| int planar; | |||
| } AudioData; | |||
| typedef struct SwrContext { //FIXME find unused fields | |||
| AVClass *av_class; | |||
| int log_level_offset; | |||
| void *log_ctx; | |||
| enum AVSampleFormat in_sample_fmt; | |||
| enum AVSampleFormat int_sample_fmt; ///<AV_SAMPLE_FMT_FLT OR AV_SAMPLE_FMT_S16 | |||
| enum AVSampleFormat out_sample_fmt; | |||
| int64_t in_ch_layout; | |||
| int64_t out_ch_layout; | |||
| int in_sample_rate; | |||
| int out_sample_rate; | |||
| int flags; | |||
| float slev, clev; | |||
| //below are private | |||
| int int_bps; | |||
| int resample_first; | |||
| int rematrix; ///< flag to indicate if rematrixing is used | |||
| AudioData in, postin, midbuf, preout, out, in_buffer; | |||
| int in_buffer_index; | |||
| int in_buffer_count; | |||
| int resample_in_constraint; | |||
| struct AVAudioConvert *in_convert; | |||
| struct AVAudioConvert *out_convert; | |||
| struct AVResampleContext *resample; | |||
| float matrix[SWR_CH_MAX][SWR_CH_MAX]; | |||
| uint8_t matrix_ch[SWR_CH_MAX][SWR_CH_MAX+1]; | |||
| //TODO callbacks for asm optims | |||
| }SwrContext; | |||
| struct AVResampleContext *swr_resample_init(struct AVResampleContext *, int out_rate, int in_rate, int filter_size, int phase_shift, int linear, double cutoff); | |||
| void swr_resample_free(struct AVResampleContext **c); | |||
| int swr_multiple_resample(struct AVResampleContext *c, AudioData *dst, int dst_size, AudioData *src, int src_size, int *consumed); | |||
| void swr_resample_compensate(struct AVResampleContext *c, int sample_delta, int compensation_distance); | |||
| int swr_resample(struct AVResampleContext *c, short *dst, short *src, int *consumed, int src_size, int dst_size, int update_ctx); | |||
| int swr_rematrix_init(SwrContext *s); | |||
| int swr_rematrix(SwrContext *s, AudioData *out, AudioData *in, int len, int mustcopy); | |||
| #endif | |||
| @@ -0,0 +1,149 @@ | |||
| /* | |||
| * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at) | |||
| * | |||
| * This file is part of libswresample | |||
| * | |||
| * libswresample is free software; you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation; either version 2 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * libswresample is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with libswresample; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #include "libavutil/avassert.h" | |||
| #include "libavutil/common.h" | |||
| #include "libavutil/audioconvert.h" | |||
| #include "swresample.h" | |||
| #undef fprintf | |||
| #define SAMPLES 1000 | |||
| #define ASSERT_LEVEL 2 | |||
| static double get(const void *p, int index, enum AVSampleFormat f){ | |||
| switch(f){ | |||
| case AV_SAMPLE_FMT_U8 : return ((const uint8_t*)p)[index]/255.0*2-1.0; | |||
| case AV_SAMPLE_FMT_S16: return ((const int16_t*)p)[index]/32767.0; | |||
| case AV_SAMPLE_FMT_S32: return ((const int32_t*)p)[index]/2147483647.0; | |||
| case AV_SAMPLE_FMT_FLT: return ((const float *)p)[index]; | |||
| case AV_SAMPLE_FMT_DBL: return ((const double *)p)[index]; | |||
| default: av_assert2(0); | |||
| } | |||
| } | |||
| static void set(void *p, int index, enum AVSampleFormat f, double v){ | |||
| switch(f){ | |||
| case AV_SAMPLE_FMT_U8 : ((uint8_t*)p)[index]= (v+1.0)*255.0/2; break; | |||
| case AV_SAMPLE_FMT_S16: ((int16_t*)p)[index]= v*32767; break; | |||
| case AV_SAMPLE_FMT_S32: ((int32_t*)p)[index]= v*2147483647; break; | |||
| case AV_SAMPLE_FMT_FLT: ((float *)p)[index]= v; break; | |||
| case AV_SAMPLE_FMT_DBL: ((double *)p)[index]= v; break; | |||
| default: av_assert2(0); | |||
| } | |||
| } | |||
| uint64_t layouts[]={ | |||
| AV_CH_LAYOUT_MONO , | |||
| AV_CH_LAYOUT_STEREO , | |||
| AV_CH_LAYOUT_2_1 , | |||
| AV_CH_LAYOUT_SURROUND , | |||
| AV_CH_LAYOUT_4POINT0 , | |||
| AV_CH_LAYOUT_2_2 , | |||
| AV_CH_LAYOUT_QUAD , | |||
| AV_CH_LAYOUT_5POINT0 , | |||
| AV_CH_LAYOUT_5POINT1 , | |||
| AV_CH_LAYOUT_5POINT0_BACK , | |||
| AV_CH_LAYOUT_5POINT1_BACK , | |||
| AV_CH_LAYOUT_7POINT0 , | |||
| AV_CH_LAYOUT_7POINT1 , | |||
| AV_CH_LAYOUT_7POINT1_WIDE , | |||
| 0 | |||
| }; | |||
| int main(int argc, char **argv){ | |||
| int in_sample_rate, out_sample_rate, ch ,i, in_ch_layout_index, out_ch_layout_index, osr; | |||
| uint64_t in_ch_layout, out_ch_layout; | |||
| enum AVSampleFormat in_sample_fmt, out_sample_fmt; | |||
| int sample_rates[]={8000,11025,16000,22050,32000}; | |||
| uint8_t array_in[SAMPLES*8*8]; | |||
| uint8_t array_mid[SAMPLES*8*8*3]; | |||
| uint8_t array_out[SAMPLES*8*8+100]; | |||
| struct SwrContext * forw_ctx= NULL; | |||
| struct SwrContext *backw_ctx= NULL; | |||
| in_sample_rate=16000; | |||
| for(osr=0; osr<5; osr++){ | |||
| out_sample_rate= sample_rates[osr]; | |||
| for(in_sample_fmt= AV_SAMPLE_FMT_U8; in_sample_fmt<=AV_SAMPLE_FMT_DBL; in_sample_fmt++){ | |||
| for(out_sample_fmt= AV_SAMPLE_FMT_U8; out_sample_fmt<=AV_SAMPLE_FMT_DBL; out_sample_fmt++){ | |||
| for(in_ch_layout_index=0; layouts[in_ch_layout_index]; in_ch_layout_index++){ | |||
| in_ch_layout= layouts[in_ch_layout_index]; | |||
| int in_ch_count= av_get_channel_layout_nb_channels(in_ch_layout); | |||
| for(out_ch_layout_index=0; layouts[out_ch_layout_index]; out_ch_layout_index++){ | |||
| int out_count, mid_count; | |||
| out_ch_layout= layouts[out_ch_layout_index]; | |||
| int out_ch_count= av_get_channel_layout_nb_channels(out_ch_layout); | |||
| fprintf(stderr, "ch %d->%d, rate:%5d->%5d, fmt:%s->%s", | |||
| in_ch_count, out_ch_count, | |||
| in_sample_rate, out_sample_rate, | |||
| av_get_sample_fmt_name(in_sample_fmt), av_get_sample_fmt_name(out_sample_fmt)); | |||
| forw_ctx = swr_alloc2(forw_ctx, out_ch_layout, out_sample_fmt, out_sample_rate, | |||
| in_ch_layout, in_sample_fmt, in_sample_rate, 0, 0); | |||
| backw_ctx = swr_alloc2(backw_ctx,in_ch_layout, in_sample_fmt, in_sample_rate, | |||
| out_ch_layout, out_sample_fmt, out_sample_rate, 0, 0); | |||
| if(swr_init( forw_ctx) < 0) | |||
| fprintf(stderr, "swr_init(->) failed\n"); | |||
| if(swr_init(backw_ctx) < 0) | |||
| fprintf(stderr, "swr_init(<-) failed\n"); | |||
| if(!forw_ctx) | |||
| fprintf(stderr, "Failed to init forw_cts\n"); | |||
| if(!backw_ctx) | |||
| fprintf(stderr, "Failed to init backw_ctx\n"); | |||
| //FIXME test planar | |||
| for(ch=0; ch<in_ch_count; ch++){ | |||
| for(i=0; i<SAMPLES; i++) | |||
| set(array_in, ch + i*in_ch_count, in_sample_fmt, sin(i*i*3/SAMPLES)); | |||
| } | |||
| mid_count= swr_convert(forw_ctx, ( uint8_t*[]){array_mid}, 3*SAMPLES, | |||
| (const uint8_t*[]){array_in }, SAMPLES); | |||
| out_count= swr_convert(backw_ctx,( uint8_t*[]){array_out}, 3*SAMPLES, | |||
| (const uint8_t*[]){array_mid}, mid_count); | |||
| for(ch=0; ch<in_ch_count; ch++){ | |||
| double sse, x, maxdiff=0; | |||
| double sum_a= 0; | |||
| double sum_b= 0; | |||
| double sum_aa= 0; | |||
| double sum_bb= 0; | |||
| double sum_ab= 0; | |||
| for(i=0; i<SAMPLES; i++){ | |||
| double a= get(array_in , ch + i*in_ch_count, in_sample_fmt); | |||
| double b= get(array_out, ch + i*in_ch_count, in_sample_fmt); | |||
| sum_a += a; | |||
| sum_b += b; | |||
| sum_aa+= a*a; | |||
| sum_bb+= b*b; | |||
| sum_ab+= a*b; | |||
| maxdiff= FFMAX(maxdiff, FFABS(a-b)); | |||
| } | |||
| x = sum_ab/sum_bb; | |||
| sse= sum_aa + sum_bb*x*x - 2*x*sum_ab; | |||
| fprintf(stderr, "[%f %f %f] len:%5d\n", sqrt(sse/SAMPLES), x, maxdiff, out_count); | |||
| } | |||
| fprintf(stderr, "\n"); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| @@ -1,4 +1,4 @@ | |||
| fd090ddf05cc3401cc75c4a5ace1d05a *./tests/data/acodec/g726.wav | |||
| a76fc937faac62c5de057cd69191732a *./tests/data/acodec/g726.wav | |||
| 24052 ./tests/data/acodec/g726.wav | |||
| 74abea06027375111eeac1b2f8c7d3af *./tests/data/g726.acodec.out.wav | |||
| stddev: 8554.55 PSNR: 17.69 MAXDIFF:29353 bytes: 95984/ 1058400 | |||
| 124de13e6cb5af64ea8758aa49feb7fc *./tests/data/g726.acodec.out.wav | |||
| stddev: 8554.23 PSNR: 17.69 MAXDIFF:29353 bytes: 95984/ 1058400 | |||
| @@ -66,7 +66,7 @@ stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 1058400/ 1058400 | |||
| 529256 ./tests/data/acodec/pcm_zork.wav | |||
| 864c8c866ac25642c29a13b122c70709 *./tests/data/pcm.acodec.out.wav | |||
| stddev: 633.11 PSNR: 40.30 MAXDIFF:32768 bytes: 1058400/ 1058400 | |||
| 8168a5c1343553ef027541830f2cb879 *./tests/data/acodec/pcm_s24daud.302 | |||
| 1b75d5198ae789ab3c48f7024e08f4a9 *./tests/data/acodec/pcm_s24daud.302 | |||
| 10368730 ./tests/data/acodec/pcm_s24daud.302 | |||
| f552afadfdfcd6348a07095da6382de5 *./tests/data/pcm.acodec.out.wav | |||
| stddev: 9416.28 PSNR: 16.85 MAXDIFF:42744 bytes: 6911796/ 1058400 | |||
| 4708f86529c594e29404603c64bb208c *./tests/data/pcm.acodec.out.wav | |||
| stddev: 8967.92 PSNR: 17.28 MAXDIFF:42548 bytes: 6911796/ 1058400 | |||
| @@ -1,3 +1,3 @@ | |||
| 188f804bd2d10cd436c8a7b111bdcd2a *./tests/data/lavf/lavf.dv | |||
| 6e716216d5f9e3819db8eb8796de9129 *./tests/data/lavf/lavf.dv | |||
| 3600000 ./tests/data/lavf/lavf.dv | |||
| ./tests/data/lavf/lavf.dv CRC=0x02c0af30 | |||
| ./tests/data/lavf/lavf.dv CRC=0x92d1e3f0 | |||
| @@ -1,3 +1,3 @@ | |||
| b3174e2db508564c1cce0b5e3c1bc1bd *./tests/data/lavf/lavf.mxf_d10 | |||
| 8eb67301f72f2b5860fafab422b920ad *./tests/data/lavf/lavf.mxf_d10 | |||
| 5330989 ./tests/data/lavf/lavf.mxf_d10 | |||
| ./tests/data/lavf/lavf.mxf_d10 CRC=0xc3f4f92e | |||
| ./tests/data/lavf/lavf.mxf_d10 CRC=0x96c02dfd | |||