1/* 2 * Copyright (c) 2011 Mina Nagy Zaki 3 * Copyright (c) 2000 Edward Beingessner And Sundry Contributors. 4 * This source code is freely redistributable and may be used for any purpose. 5 * This copyright notice must be maintained. Edward Beingessner And Sundry 6 * Contributors are not responsible for the consequences of using this 7 * software. 8 * 9 * This file is part of FFmpeg. 10 * 11 * FFmpeg is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU Lesser General Public 13 * License as published by the Free Software Foundation; either 14 * version 2.1 of the License, or (at your option) any later version. 15 * 16 * FFmpeg is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * Lesser General Public License for more details. 20 * 21 * You should have received a copy of the GNU Lesser General Public 22 * License along with FFmpeg; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 24 */ 25 26/** 27 * @file 28 * Stereo Widening Effect. Adds audio cues to move stereo image in 29 * front of the listener. Adapted from the libsox earwax effect. 30 */ 31 32#include "libavutil/channel_layout.h" 33#include "avfilter.h" 34#include "audio.h" 35#include "formats.h" 36 37#define NUMTAPS 64 38 39static const int8_t filt[NUMTAPS] = { 40/* 30�� 330�� */ 41 4, -6, /* 32 tap stereo FIR filter. */ 42 4, -11, /* One side filters as if the */ 43 -1, -5, /* signal was from 30 degrees */ 44 3, 3, /* from the ear, the other as */ 45 -2, 5, /* if 330 degrees. */ 46 -5, 0, 47 9, 1, 48 6, 3, /* Input */ 49 -4, -1, /* Left Right */ 50 -5, -3, /* __________ __________ */ 51 -2, -5, /* | | | | */ 52 -7, 1, /* .---| Hh,0(f) | | Hh,0(f) |---. */ 53 6, -7, /* / |__________| |__________| \ */ 54 30, -29, /* / \ / \ */ 55 12, -3, /* / X \ */ 56 -11, 4, /* / / \ \ */ 57 -3, 7, /* ____V_____ __________V V__________ _____V____ */ 58 -20, 23, /* | | | | | | | | */ 59 2, 0, /* | Hh,30(f) | | Hh,330(f)| | Hh,330(f)| | Hh,30(f) | */ 60 1, -6, /* |__________| |__________| |__________| |__________| */ 61 -14, -5, /* \ ___ / \ ___ / */ 62 15, -18, /* \ / \ / _____ \ / \ / */ 63 6, 7, /* `->| + |<--' / \ `-->| + |<-' */ 64 15, -10, /* \___/ _/ \_ \___/ */ 65 -14, 22, /* \ / \ / \ / */ 66 -7, -2, /* `--->| | | |<---' */ 67 -4, 9, /* \_/ \_/ */ 68 6, -12, /* */ 69 6, -6, /* Headphones */ 70 0, -11, 71 0, -5, 72 4, 0}; 73 74typedef struct { 75 int16_t taps[NUMTAPS * 2]; 76} EarwaxContext; 77 78static int query_formats(AVFilterContext *ctx) 79{ 80 static const int sample_rates[] = { 44100, -1 }; 81 82 AVFilterFormats *formats = NULL; 83 AVFilterChannelLayouts *layout = NULL; 84 85 ff_add_format(&formats, AV_SAMPLE_FMT_S16); 86 ff_set_common_formats(ctx, formats); 87 ff_add_channel_layout(&layout, AV_CH_LAYOUT_STEREO); 88 ff_set_common_channel_layouts(ctx, layout); 89 ff_set_common_samplerates(ctx, ff_make_format_list(sample_rates)); 90 91 return 0; 92} 93 94//FIXME: replace with DSPContext.scalarproduct_int16 95static inline int16_t *scalarproduct(const int16_t *in, const int16_t *endin, int16_t *out) 96{ 97 int32_t sample; 98 int16_t j; 99 100 while (in < endin) { 101 sample = 0; 102 for (j = 0; j < NUMTAPS; j++) 103 sample += in[j] * filt[j]; 104 *out = av_clip_int16(sample >> 6); 105 out++; 106 in++; 107 } 108 109 return out; 110} 111 112static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) 113{ 114 AVFilterLink *outlink = inlink->dst->outputs[0]; 115 int16_t *taps, *endin, *in, *out; 116 AVFrame *outsamples = ff_get_audio_buffer(inlink, insamples->nb_samples); 117 int len; 118 119 if (!outsamples) { 120 av_frame_free(&insamples); 121 return AVERROR(ENOMEM); 122 } 123 av_frame_copy_props(outsamples, insamples); 124 125 taps = ((EarwaxContext *)inlink->dst->priv)->taps; 126 out = (int16_t *)outsamples->data[0]; 127 in = (int16_t *)insamples ->data[0]; 128 129 len = FFMIN(NUMTAPS, 2*insamples->nb_samples); 130 // copy part of new input and process with saved input 131 memcpy(taps+NUMTAPS, in, len * sizeof(*taps)); 132 out = scalarproduct(taps, taps + len, out); 133 134 // process current input 135 if (2*insamples->nb_samples >= NUMTAPS ){ 136 endin = in + insamples->nb_samples * 2 - NUMTAPS; 137 scalarproduct(in, endin, out); 138 139 // save part of input for next round 140 memcpy(taps, endin, NUMTAPS * sizeof(*taps)); 141 } else 142 memmove(taps, taps + 2*insamples->nb_samples, NUMTAPS * sizeof(*taps)); 143 144 av_frame_free(&insamples); 145 return ff_filter_frame(outlink, outsamples); 146} 147 148static const AVFilterPad earwax_inputs[] = { 149 { 150 .name = "default", 151 .type = AVMEDIA_TYPE_AUDIO, 152 .filter_frame = filter_frame, 153 }, 154 { NULL } 155}; 156 157static const AVFilterPad earwax_outputs[] = { 158 { 159 .name = "default", 160 .type = AVMEDIA_TYPE_AUDIO, 161 }, 162 { NULL } 163}; 164 165AVFilter ff_af_earwax = { 166 .name = "earwax", 167 .description = NULL_IF_CONFIG_SMALL("Widen the stereo image."), 168 .query_formats = query_formats, 169 .priv_size = sizeof(EarwaxContext), 170 .inputs = earwax_inputs, 171 .outputs = earwax_outputs, 172}; 173