def test_codec_ComplexHighPass(): filter = ComplexHighPass(FilterType.LINKWITZ_RILEY, 2, 48000, 100) output = json.dumps(filter.to_json()) assert output == '{"_type": "ComplexHighPass", "filter_type": "LR", "order": 2, "fs": 48000, "fc": 100.0}' decoded = filter_from_json(json.loads(output)) assert decoded is not None assert isinstance(decoded, ComplexHighPass) assert filter.fs == decoded.fs assert filter.type == decoded.type assert filter.order == decoded.order assert filter.freq == decoded.freq assert decoded.get_transfer_function() is not None
def test_codec_CompleteFilter(): filter = CompleteFilter(filters=[PeakingEQ(1000, 50, 3.2, -5), LowShelf(1000, 25, 1, 3.2, count=3), ComplexHighPass(FilterType.BUTTERWORTH, 6, 1000, 12)], description='Hello from me') output = json.dumps(filter.to_json()) expected = '{"_type": "CompleteFilter", "description": "Hello from me", "fs": 1000, "filters": [' \ '{"_type": "ComplexHighPass", "filter_type": "BW", "order": 6, "fs": 1000, "fc": 12.0}, ' \ '{"_type": "LowShelf", "fs": 1000, "fc": 25.0, "q": 1.0, "gain": 3.2, "count": 3}, ' \ '{"_type": "PeakingEQ", "fs": 1000, "fc": 50.0, "q": 3.2, "gain": -5.0}' \ ']}' assert output == expected decoded = filter_from_json(json.loads(output)) assert decoded is not None assert isinstance(decoded, CompleteFilter) assert decoded.description == 'Hello from me' assert decoded.filters is not None assert len(decoded.filters) == len(filter.filters) assert decoded.getTransferFunction() is not None assert isinstance(decoded.filters[0], filter.filters[0].__class__) assert filter.filters[0].fs == decoded.filters[0].fs assert filter.filters[0].type == decoded.filters[0].type assert filter.filters[0].order == decoded.filters[0].order assert filter.filters[0].freq == decoded.filters[0].freq assert isinstance(decoded.filters[1], filter.filters[1].__class__) assert filter.filters[1].fs == decoded.filters[1].fs assert filter.filters[1].q == decoded.filters[1].q assert filter.filters[1].gain == decoded.filters[1].gain assert filter.filters[1].freq == decoded.filters[1].freq assert filter.filters[1].count == decoded.filters[1].count assert isinstance(decoded.filters[2], filter.filters[2].__class__) assert filter.filters[2].fs == decoded.filters[2].fs assert filter.filters[2].q == decoded.filters[2].q assert filter.filters[2].gain == decoded.filters[2].gain assert filter.filters[2].freq == decoded.filters[2].freq
def __convert_to_ffmpeg_filter_complex(self, audio_input, c_name, channel_idx, sig, low_pass_main_bm=False): ''' Formats a channel to a ffmpeg filter chain. :param audio_input:the ffmpeg input channel that contains the audio track. :param c_name: the channel name we're processing (FL, FR etc) :param channel_idx: the channel idx, 0 based. :param sig: the signal containing the filter. :return: a formatted filter string. ''' f_idx = 0 # extract a single channel, convert it to dbl sample format # pipe it through each biquad in the matching signal # convert back to 32bit into the original fs and place in a named mono stream # [0:a]pan=1c|c0=c<channel_num>[<c_name>];[L]aformat=sample_fmts=dbl[Lin];[Lin]biquad=b0=1.00080054343984:b1=-1.99150225042309:b2=0.990752403334702:a0=1.0:a1=-1.99150965346467:a2=0.991545543732965[L1];[L1]biquad=b0=0.999200096917323:b1=-1.98991663875369:b2=0.990752403395918:a0=1.0:a1=-1.98990924163382:a2=0.989959897433105[L2];[L2]aformat=sample_fmts=s32:sample_rates=48000:channel_layouts=mono[Lout]; if low_pass_main_bm: c_name = f"BM{c_name}" filt = f"{audio_input}pan=1c|c0=c{channel_idx}[{c_name}];" filt += f"[{c_name}]aformat=sample_fmts=dbl[{c_name}_{f_idx}];" if sig is not None: sig_filter = sig.active_filter.resample(int(self.__sample_rate), copy_listener=False) for f in sig_filter.filters: for bq in format_biquad(f): filt += f"[{c_name}_{f_idx}]biquad={bq}[{c_name}_{f_idx + 1}];" f_idx += 1 # apply BM high or low pass filter after the filters are applied bmpf = None if low_pass_main_bm: bmpf = ComplexLowPass(FilterType.LINKWITZ_RILEY, 4, int(self.__sample_rate), self.__bm_fs) elif self.bass_manage is True and (self.lfe_idx - 1) != channel_idx: bmpf = ComplexHighPass(FilterType.LINKWITZ_RILEY, 4, int(self.__sample_rate), self.__bm_fs) if bmpf: for bq in format_biquad(bmpf): filt += f"[{c_name}_{f_idx}]biquad={bq}[{c_name}_{f_idx + 1}];" f_idx += 1 # add a passthrough filter if we have no other filters to add if f_idx == 0: filt += f"[{c_name}_{f_idx}]biquad={format_biquad(Passthrough())[0]}[{c_name}_{f_idx + 1}];" f_idx += 1 # gain adjustment if not math.isclose(self.filtered_audio_offset, 0.0): filt += f"[{c_name}_{f_idx}]volume=volume={self.filtered_audio_offset:+g}dB[{c_name}_{f_idx + 1}];" f_idx += 1 # enforce sample format filt += f"[{c_name}_{f_idx}]aformat=sample_fmts=s32:sample_rates={self.__sample_rate}" return filt
def filter_from_json(o): ''' Converts a dict (parsed from json) to a filter. :param o: the dict. :return: the filter. ''' from model.iir import Passthrough, PeakingEQ, LowShelf, HighShelf, FirstOrder_LowPass, \ FirstOrder_HighPass, SecondOrder_LowPass, SecondOrder_HighPass, AllPass, CompleteFilter, ComplexLowPass, \ FilterType, ComplexHighPass filt = None if '_type' not in o: raise ValueError(f"{o} is not a filter") if o['_type'] == Passthrough.__name__: if 'fs' in o: filt = Passthrough(fs=int(o['fs'])) else: filt = Passthrough() elif o['_type'] == Gain.__name__: filt = Gain(o['fs'], o['gain']) elif o['_type'] == PeakingEQ.__name__: filt = PeakingEQ(o['fs'], o['fc'], o['q'], o['gain']) elif o['_type'] == LowShelf.__name__: filt = LowShelf(o['fs'], o['fc'], o['q'], o['gain'], o['count']) elif o['_type'] == HighShelf.__name__: filt = HighShelf(o['fs'], o['fc'], o['q'], o['gain'], o['count']) elif o['_type'] == FirstOrder_LowPass.__name__: filt = FirstOrder_LowPass(o['fs'], o['fc'], o['q']) elif o['_type'] == FirstOrder_HighPass.__name__: filt = FirstOrder_HighPass(o['fs'], o['fc'], o['q']) elif o['_type'] == SecondOrder_LowPass.__name__: filt = SecondOrder_LowPass(o['fs'], o['fc'], o['q']) elif o['_type'] == SecondOrder_HighPass.__name__: filt = SecondOrder_HighPass(o['fs'], o['fc'], o['q']) elif o['_type'] == AllPass.__name__: filt = AllPass(o['fs'], o['fc'], o['q']) elif o['_type'] == CompleteFilter.__name__: kwargs = {} if 'fs' in o: kwargs['fs'] = o['fs'] filt = CompleteFilter( filters=[filter_from_json(x) for x in o['filters']], description=o['description'], **kwargs) elif o['_type'] == ComplexLowPass.__name__: filt = ComplexLowPass(FilterType(o['filter_type']), o['order'], o['fs'], o['fc']) elif o['_type'] == ComplexHighPass.__name__: filt = ComplexHighPass(FilterType(o['filter_type']), o['order'], o['fs'], o['fc']) if filt is None: raise ValueError(f"{o._type} is an unknown filter type") else: if filt.id == -1: filt.id = uuid4() return filt
def __get_post_filt_hpf(self, apply=None): post_filt = None if apply is None: apply = self.__bm_hpf.isChecked() and self.__bm_hpf.isEnabled() if apply is True: from model.iir import FilterType, ComplexHighPass hpf_fs = self.__preferences.get(BASS_MANAGEMENT_LPF_FS) post_filt = ComplexHighPass(FilterType.LINKWITZ_RILEY, 4, 1000, hpf_fs) return post_filt
def create_pass_filter(self): ''' Creates a predefined high or low pass filter. :return: the filter. ''' if self.filterType.currentText() == 'Low Pass': filt = ComplexLowPass(FilterType[self.passFilterType.currentText().upper().replace('-', '_')], self.filterOrder.value(), self.__signal.fs, self.freq.value()) else: filt = ComplexHighPass(FilterType[self.passFilterType.currentText().upper().replace('-', '_')], self.filterOrder.value(), self.__signal.fs, self.freq.value()) filt.id = self.__selected_id return filt