mirror of
https://github.com/TomHarte/CLK.git
synced 2025-04-22 09:37:21 +00:00
Merge pull request #1472 from TomHarte/FastPRGs
Withdraw PRG support for the Plus 4.
This commit is contained in:
commit
f786f8a970
@ -180,9 +180,8 @@ bool obviously_uses_ted(const File &file) {
|
||||
analysis->machine_code_addresses
|
||||
);
|
||||
|
||||
// If FF3E or FF3F is touched, this is for the +4.
|
||||
// TODO: probably require a very early touch.
|
||||
for(const auto address: {0xff3e, 0xff3f}) {
|
||||
// Check for interrupt status and paging touches.
|
||||
for(const auto address: {0xff3e, 0xff3f, 0xff09}) {
|
||||
for(const auto &collection: {
|
||||
disassembly.external_loads,
|
||||
disassembly.external_stores,
|
||||
|
@ -269,13 +269,14 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
|
||||
|
||||
accumulator.try_standard<Tape::ZX80O81P>(TargetPlatform::ZX8081, "p81");
|
||||
|
||||
static constexpr auto PRGTargets = TargetPlatform::Vic20; //Commodore8bit; // Disabled until analysis improves.
|
||||
if(accumulator.name_matches("prg")) {
|
||||
// Try instantiating as a ROM; failing that accept as a tape.
|
||||
try {
|
||||
accumulator.insert<Cartridge::PRG>(TargetPlatform::Commodore8bit, file_name);
|
||||
accumulator.insert<Cartridge::PRG>(PRGTargets, file_name);
|
||||
} catch(...) {
|
||||
try {
|
||||
accumulator.insert<Tape::PRG>(TargetPlatform::Commodore8bit, file_name);
|
||||
accumulator.insert<Tape::PRG>(PRGTargets, file_name);
|
||||
} catch(...) {}
|
||||
}
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ public:
|
||||
}
|
||||
|
||||
// HACK. NOCOMMIT.
|
||||
int pulse_num_ = 0;
|
||||
// int pulse_num_ = 0;
|
||||
|
||||
Cycles perform_bus_operation(
|
||||
const CPU::MOS6502::BusOperation operation,
|
||||
@ -254,15 +254,18 @@ public:
|
||||
|
||||
// Update other subsystems.
|
||||
advance_timers_and_tape(length);
|
||||
video_.run_for(length);
|
||||
if(!superspeed_) {
|
||||
video_.run_for(length);
|
||||
|
||||
if(c1541_) {
|
||||
c1541_cycles_ += length * Cycles(1'000'000);
|
||||
c1541_->run_for(c1541_cycles_.divide(media_divider_));
|
||||
if(c1541_) {
|
||||
c1541_cycles_ += length * Cycles(1'000'000);
|
||||
c1541_->run_for(c1541_cycles_.divide(media_divider_));
|
||||
}
|
||||
|
||||
|
||||
time_since_audio_update_ += length;
|
||||
}
|
||||
|
||||
time_since_audio_update_ += length;
|
||||
|
||||
if(operation == CPU::MOS6502::BusOperation::Ready) {
|
||||
return length;
|
||||
}
|
||||
@ -300,8 +303,50 @@ public:
|
||||
serial_port_.set_output(Serial::Line::Attention, Serial::LineLevel(~output & 0x04));
|
||||
}
|
||||
} else if(address < 0xfd00 || address >= 0xff40) {
|
||||
if(use_fast_tape_hack_ && operation == CPU::MOS6502Esque::BusOperation::ReadOpcode && address == 0xe5fd) {
|
||||
read_dipole();
|
||||
// if(
|
||||
// use_fast_tape_hack_ &&
|
||||
// operation == CPU::MOS6502Esque::BusOperation::ReadOpcode
|
||||
// ) {
|
||||
// superspeed_ |= address == 0xe5fd;
|
||||
// superspeed_ &= (address != 0xe68b) && (address != 0xe68d);
|
||||
// }
|
||||
|
||||
constexpr bool use_hle = true;
|
||||
|
||||
// if(
|
||||
// use_fast_tape_hack_ &&
|
||||
// operation == CPU::MOS6502Esque::BusOperation::ReadOpcode &&
|
||||
// address == 0xe5fd
|
||||
// ) {
|
||||
// printf("Pulse %d from %lld ",
|
||||
// pulse_num_,
|
||||
// tape_player_->serialiser()->offset()
|
||||
// );
|
||||
// }
|
||||
|
||||
if(
|
||||
use_fast_tape_hack_ &&
|
||||
operation == CPU::MOS6502Esque::BusOperation::ReadOpcode &&
|
||||
(
|
||||
(use_hle && address == 0xe5fd) ||
|
||||
address == 0xe68b ||
|
||||
address == 0xe68d
|
||||
)
|
||||
) {
|
||||
// ++pulse_num_;
|
||||
if(use_hle) {
|
||||
read_dipole();
|
||||
}
|
||||
|
||||
// using Flag = CPU::MOS6502::Flag;
|
||||
// using Register = CPU::MOS6502::Register;
|
||||
// const auto flags = m6502_.value_of(Register::Flags);
|
||||
// printf("to %lld: %c%c%c\n",
|
||||
// tape_player_->serialiser()->offset(),
|
||||
// flags & Flag::Sign ? 'n' : '-',
|
||||
// flags & Flag::Overflow ? 'v' : '-',
|
||||
// flags & Flag::Carry ? 'c' : '-'
|
||||
// );
|
||||
*value = 0x60;
|
||||
} else {
|
||||
if(is_read(operation)) {
|
||||
@ -311,36 +356,47 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/* if(use_fast_tape_hack_ && operation == CPU::MOS6502Esque::BusOperation::ReadOpcode) {
|
||||
if(address == 0xe9cc) {
|
||||
// Skip the `jsr rdblok` that opens `fah` (i.e. find any header), performing
|
||||
// its function as a high-level emulation.
|
||||
Storage::Tape::Commodore::Parser parser(TargetPlatform::Plus4);
|
||||
auto header = parser.get_next_header(*tape_player_->serialiser());
|
||||
|
||||
const auto tape_position = tape_player_->serialiser()->offset();
|
||||
if(header) {
|
||||
// Copy to in-memory buffer and set type.
|
||||
std::memcpy(&ram_[0x0333], header->data.data(), 191);
|
||||
map_.write(0xb6) = 0x33;
|
||||
map_.write(0xb7) = 0x03;
|
||||
map_.write(0xf8) = header->type_descriptor();
|
||||
// hold_tape_ = true;
|
||||
logger.info().append("Found header");
|
||||
} else {
|
||||
// no header found, so pretend this hack never interceded
|
||||
tape_player_->serialiser()->set_offset(tape_position);
|
||||
// hold_tape_ = false;
|
||||
logger.info().append("Didn't find header");
|
||||
}
|
||||
// TODO: rdbyte and ldsync is probably sufficient?
|
||||
|
||||
// Clear status and the verify flags.
|
||||
ram_[0x90] = 0;
|
||||
ram_[0x93] = 0;
|
||||
|
||||
*value = 0x0c; // NOP abs.
|
||||
}
|
||||
}*/
|
||||
// if(use_fast_tape_hack_ && operation == CPU::MOS6502Esque::BusOperation::ReadOpcode) {
|
||||
// constexpr uint16_t ldsync = 0;
|
||||
// switch(address) {
|
||||
// default: break;
|
||||
//
|
||||
// case ldsync:
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// if(address == 0xe9cc) {
|
||||
// // Skip the `jsr rdblok` that opens `fah` (i.e. find any header), performing
|
||||
// // its function as a high-level emulation.
|
||||
// Storage::Tape::Commodore::Parser parser(TargetPlatform::Plus4);
|
||||
// auto header = parser.get_next_header(*tape_player_->serialiser());
|
||||
//
|
||||
// const auto tape_position = tape_player_->serialiser()->offset();
|
||||
// if(header) {
|
||||
// // Copy to in-memory buffer and set type.
|
||||
// std::memcpy(&ram_[0x0333], header->data.data(), 191);
|
||||
// map_.write(0xb6) = 0x33;
|
||||
// map_.write(0xb7) = 0x03;
|
||||
// map_.write(0xf8) = header->type_descriptor();
|
||||
//// hold_tape_ = true;
|
||||
// logger.info().append("Found header");
|
||||
// } else {
|
||||
// // no header found, so pretend this hack never interceded
|
||||
// tape_player_->serialiser()->set_offset(tape_position);
|
||||
//// hold_tape_ = false;
|
||||
// logger.info().append("Didn't find header");
|
||||
// }
|
||||
//
|
||||
// // Clear status and the verify flags.
|
||||
// ram_[0x90] = 0;
|
||||
// ram_[0x93] = 0;
|
||||
//
|
||||
// *value = 0x0c; // NOP abs.
|
||||
// }
|
||||
// }
|
||||
} else if(address < 0xff00) {
|
||||
// Miscellaneous hardware. All TODO.
|
||||
if(is_read(operation)) {
|
||||
@ -567,7 +623,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
return length;
|
||||
return superspeed_ ? Cycles(0) : length;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -713,6 +769,7 @@ private:
|
||||
bool play_button_ = false;
|
||||
bool allow_fast_tape_hack_ = false; // TODO: implement fast-tape hack.
|
||||
bool use_fast_tape_hack_ = false;
|
||||
bool superspeed_ = false;
|
||||
void set_use_fast_tape() {
|
||||
use_fast_tape_hack_ =
|
||||
allow_fast_tape_hack_ && tape_player_->motor_control() && rom_is_paged_ && !tape_player_->is_at_end();
|
||||
@ -728,6 +785,9 @@ private:
|
||||
|
||||
tape_player_->run_for(length);
|
||||
}
|
||||
|
||||
// TODO: substantially simplify the below; at the minute it's a
|
||||
// literal transcription of the original as a simple first step.
|
||||
void read_dipole() {
|
||||
using Register = CPU::MOS6502::Register;
|
||||
using Flag = CPU::MOS6502::Flag;
|
||||
@ -834,6 +894,7 @@ private:
|
||||
// ldy dsamp1+1
|
||||
ldabs(x, dsamp1);
|
||||
ldabs(y, dsamp1 + 1);
|
||||
advance_cycles(8);
|
||||
|
||||
//badeg1
|
||||
do {
|
||||
@ -845,15 +906,17 @@ private:
|
||||
pha();
|
||||
ldabs(a, dsamp2);
|
||||
pha();
|
||||
advance_cycles(14);
|
||||
|
||||
// lda #$10
|
||||
//rwtl ; wait till rd line is high
|
||||
// bit port [= $0001]
|
||||
// beq rwtl ; !ls!
|
||||
ldimm(a, 0x10);
|
||||
advance_cycles(2);
|
||||
do {
|
||||
bit(io_input());
|
||||
if(advance_cycles(7)) {
|
||||
if(advance_cycles(6)) {
|
||||
return;
|
||||
}
|
||||
} while(eq());
|
||||
@ -863,7 +926,7 @@ private:
|
||||
// bne rwth ; caught the edge
|
||||
do {
|
||||
bit(io_input());
|
||||
if(advance_cycles(7)) {
|
||||
if(advance_cycles(6)) {
|
||||
return;
|
||||
}
|
||||
} while(ne());
|
||||
@ -873,6 +936,8 @@ private:
|
||||
// sty timr2h
|
||||
timers_.write<2>(x);
|
||||
timers_.write<3>(y);
|
||||
advance_cycles(8);
|
||||
|
||||
|
||||
//; go! ...ta
|
||||
//
|
||||
@ -884,6 +949,7 @@ private:
|
||||
timers_.write<4>(a);
|
||||
pla();
|
||||
timers_.write<5>(a);
|
||||
advance_cycles(14);
|
||||
|
||||
|
||||
//; clear timer flags
|
||||
@ -892,6 +958,7 @@ private:
|
||||
// sta tedirq
|
||||
ldimm(a, 0x50);
|
||||
interrupts_.set_status(a);
|
||||
advance_cycles(6);
|
||||
|
||||
|
||||
//; um...check that edge again
|
||||
@ -905,11 +972,12 @@ private:
|
||||
do {
|
||||
ldimm(a, io_input());
|
||||
cmp(io_input());
|
||||
if(advance_cycles(11)) {
|
||||
if(advance_cycles(9)) {
|
||||
return;
|
||||
}
|
||||
} while(ne());
|
||||
andimm(0x10);
|
||||
advance_cycles(5);
|
||||
} while(ne());
|
||||
|
||||
|
||||
@ -926,11 +994,8 @@ private:
|
||||
// lda #$10
|
||||
//wata ; wait for ta to timeout
|
||||
ldimm(a, 0x10);
|
||||
advance_cycles(3);
|
||||
do {
|
||||
if(advance_cycles(13)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// bit port ; kuldge, kludge, kludge !!! <<><>>
|
||||
// bne rshort ; kuldge, kludge, kludge !!! <<><>>
|
||||
bit(io_input());
|
||||
@ -942,6 +1007,10 @@ private:
|
||||
// bit tedirq
|
||||
// beq wata
|
||||
bit(interrupts_.status());
|
||||
|
||||
if(advance_cycles(12)) {
|
||||
return;
|
||||
}
|
||||
} while(eq());
|
||||
|
||||
|
||||
@ -950,21 +1019,21 @@ private:
|
||||
//
|
||||
//casdb2
|
||||
do {
|
||||
if(advance_cycles(11)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// lda port
|
||||
// cmp port
|
||||
ldimm(a, io_input());
|
||||
cmp(io_input());
|
||||
|
||||
if(advance_cycles(9)) {
|
||||
return;
|
||||
}
|
||||
// bne casdb2
|
||||
} while(ne());
|
||||
|
||||
// and #$10
|
||||
// bne rshort ; shorts anyone?
|
||||
andimm(0x10);
|
||||
advance_cycles(3);
|
||||
if(ne()) {
|
||||
rshort();
|
||||
return;
|
||||
@ -981,11 +1050,12 @@ private:
|
||||
//; wait for tb to timeout
|
||||
//; now do the dipole sample #2
|
||||
ldimm(a, 0x40);
|
||||
advance_cycles(3);
|
||||
do {
|
||||
if(advance_cycles(7)) {
|
||||
bit(interrupts_.status());
|
||||
if(advance_cycles(6)) {
|
||||
return;
|
||||
}
|
||||
bit(interrupts_.status());
|
||||
} while(eq());
|
||||
|
||||
|
||||
@ -994,16 +1064,17 @@ private:
|
||||
// cmp port
|
||||
// bne casdb3
|
||||
do {
|
||||
if(advance_cycles(11)) {
|
||||
return;
|
||||
}
|
||||
ldimm(a, io_input());
|
||||
cmp(io_input());
|
||||
if(advance_cycles(9)) {
|
||||
return;
|
||||
}
|
||||
} while(ne());
|
||||
|
||||
// and #$10
|
||||
// bne rlong ; looks like a long from here !ls!
|
||||
andimm(0x10);
|
||||
advance_cycles(2);
|
||||
if(ne()) {
|
||||
rlong();
|
||||
return;
|
||||
@ -1018,6 +1089,7 @@ private:
|
||||
timers_.write<2>(a);
|
||||
ldabs(a, zcell + 1);
|
||||
timers_.write<3>(y);
|
||||
advance_cycles(16);
|
||||
|
||||
|
||||
// ; go! z-cell check
|
||||
@ -1028,15 +1100,16 @@ private:
|
||||
ldimm(a, 0x10);
|
||||
interrupts_.set_status(a);
|
||||
ldimm(a, 0x10);
|
||||
advance_cycles(8);
|
||||
|
||||
//wata2
|
||||
// bit tedirq
|
||||
// beq wata2 ; check z-cell is low
|
||||
do {
|
||||
bit(interrupts_.status());
|
||||
if(advance_cycles(7)) {
|
||||
return;
|
||||
}
|
||||
bit(interrupts_.status());
|
||||
} while(eq());
|
||||
|
||||
//casdb4
|
||||
@ -1044,11 +1117,11 @@ private:
|
||||
// cmp port
|
||||
// bne casdb4
|
||||
do {
|
||||
if(advance_cycles(7)) {
|
||||
return;
|
||||
}
|
||||
ldimm(a, io_input());
|
||||
cmp(io_input());
|
||||
if(advance_cycles(9)) {
|
||||
return;
|
||||
}
|
||||
} while(ne());
|
||||
|
||||
// and #$10
|
||||
@ -1056,11 +1129,13 @@ private:
|
||||
// bit twordd ; got a word dipole
|
||||
// bmi dipok ; !bra
|
||||
andimm(0x10);
|
||||
advance_cycles(2);
|
||||
if(eq()) {
|
||||
rderr1();
|
||||
return;
|
||||
}
|
||||
bit(0x80);
|
||||
advance_cycles(2);
|
||||
dipok();
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,6 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace CRC {
|
||||
|
||||
|
@ -100,8 +100,8 @@ private:
|
||||
|
||||
std::mutex filter_parameters_mutex_;
|
||||
struct FilterParameters {
|
||||
float input_cycles_per_second = 0.0f;
|
||||
float output_cycles_per_second = 0.0f;
|
||||
float input_cycles_per_second = 1.0f;
|
||||
float output_cycles_per_second = 1.0f;
|
||||
float high_frequency_cutoff = -1.0;
|
||||
|
||||
bool parameters_are_dirty = true;
|
||||
|
@ -9,9 +9,11 @@
|
||||
#include "FIRFilter.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
|
||||
#ifndef M_PI
|
||||
static constexpr float M_PI = 3.1415926f;
|
||||
// TODO: use std::numbers::pi_v when switching to C++20.
|
||||
#endif
|
||||
|
||||
using namespace SignalProcessing;
|
||||
@ -36,8 +38,10 @@ using namespace SignalProcessing;
|
||||
"DIGITAL SIGNAL PROCESSING, II", IEEE Press, pages 123-126.
|
||||
*/
|
||||
|
||||
namespace {
|
||||
|
||||
/*! Evaluates the 0th order Bessel function at @c a. */
|
||||
float FIRFilter::ino(float a) {
|
||||
constexpr float ino(float a) {
|
||||
float d = 0.0f;
|
||||
float ds = 1.0f;
|
||||
float s = 1.0f;
|
||||
@ -51,73 +55,78 @@ float FIRFilter::ino(float a) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void FIRFilter::coefficients_for_idealised_filter_response(short *filter_coefficients, float *A, float attenuation, std::size_t number_of_taps) {
|
||||
/* calculate alpha, which is the Kaiser-Bessel window shape factor */
|
||||
float a; // to take the place of alpha in the normal derivation
|
||||
|
||||
if(attenuation < 21.0f) {
|
||||
a = 0.0f;
|
||||
} else {
|
||||
if(attenuation > 50.0f)
|
||||
a = 0.1102f * (attenuation - 8.7f);
|
||||
else
|
||||
a = 0.5842f * powf(attenuation - 21.0f, 0.4f) + 0.7886f * (attenuation - 21.0f);
|
||||
}
|
||||
std::vector<short> coefficients_for_idealised_filter_response(
|
||||
const std::vector<float> &A,
|
||||
const float attenuation,
|
||||
const std::size_t number_of_taps
|
||||
) {
|
||||
/* Calculate alpha, the Kaiser-Bessel window shape factor */
|
||||
const float a = [&] {
|
||||
if(attenuation < 21.0f) {
|
||||
return 0.0f;
|
||||
} else if(attenuation > 50.0f) {
|
||||
return 0.1102f * (attenuation - 8.7f);
|
||||
} else {
|
||||
return 0.5842f * powf(attenuation - 21.0f, 0.4f) + 0.7886f * (attenuation - 21.0f);
|
||||
}
|
||||
} ();
|
||||
|
||||
std::vector<float> filter_coefficients_float(number_of_taps);
|
||||
|
||||
/* work out the right hand side of the filter coefficients */
|
||||
std::size_t Np = (number_of_taps - 1) / 2;
|
||||
float I0 = ino(a);
|
||||
float Np_squared = float(Np * Np);
|
||||
for(unsigned int i = 0; i <= Np; ++i) {
|
||||
/* Work out the right hand side of the filter coefficients. */
|
||||
const float I0 = ino(a);
|
||||
const std::size_t Np = (number_of_taps - 1) / 2;
|
||||
const float Np_squared = float(Np * Np);
|
||||
for(std::size_t i = 0; i <= Np; ++i) {
|
||||
filter_coefficients_float[Np + i] =
|
||||
A[i] *
|
||||
ino(a * sqrtf(1.0f - (float(i * i) / Np_squared) )) /
|
||||
I0;
|
||||
}
|
||||
|
||||
/* coefficients are symmetrical, so copy from right hand side to left side */
|
||||
/* Coefficients are symmetrical, so copy from right hand side to left. */
|
||||
for(std::size_t i = 0; i < Np; ++i) {
|
||||
filter_coefficients_float[i] = filter_coefficients_float[number_of_taps - 1 - i];
|
||||
}
|
||||
|
||||
/* scale back up so that we retain 100% of input volume */
|
||||
float coefficientTotal = 0.0f;
|
||||
for(std::size_t i = 0; i < number_of_taps; ++i) {
|
||||
coefficientTotal += filter_coefficients_float[i];
|
||||
}
|
||||
/* Scale back up to retain 100% of input volume. */
|
||||
const float coefficientTotal =
|
||||
std::accumulate(filter_coefficients_float.begin(), filter_coefficients_float.end(), 0.0f);
|
||||
|
||||
/* we'll also need integer versions, potentially */
|
||||
float coefficientMultiplier = 1.0f / coefficientTotal;
|
||||
/* Hence produce integer versions. */
|
||||
const float coefficientMultiplier = 1.0f / coefficientTotal;
|
||||
std::vector<short> filter_coefficients;
|
||||
filter_coefficients.reserve(number_of_taps);
|
||||
for(std::size_t i = 0; i < number_of_taps; ++i) {
|
||||
filter_coefficients[i] = short(filter_coefficients_float[i] * FixedMultiplier * coefficientMultiplier);
|
||||
filter_coefficients.push_back(short(filter_coefficients_float[i] * FixedMultiplier * coefficientMultiplier));
|
||||
}
|
||||
return filter_coefficients;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<float> FIRFilter::get_coefficients() const {
|
||||
std::vector<float> coefficients;
|
||||
coefficients.reserve(filter_coefficients_.size());
|
||||
for(const auto short_coefficient: filter_coefficients_) {
|
||||
coefficients.push_back(float(short_coefficient) / FixedMultiplier);
|
||||
}
|
||||
return coefficients;
|
||||
}
|
||||
|
||||
FIRFilter::FIRFilter(std::size_t number_of_taps, float input_sample_rate, float low_frequency, float high_frequency, float attenuation) {
|
||||
// we must be asked to filter based on an odd number of
|
||||
// taps, and at least three
|
||||
if(number_of_taps < 3) number_of_taps = 3;
|
||||
if(attenuation < 21.0f) attenuation = 21.0f;
|
||||
|
||||
// ensure we have an odd number of taps
|
||||
number_of_taps |= 1;
|
||||
|
||||
// store instance variables
|
||||
filter_coefficients_.resize(number_of_taps);
|
||||
FIRFilter::FIRFilter(
|
||||
std::size_t number_of_taps,
|
||||
const float input_sample_rate,
|
||||
const float low_frequency,
|
||||
float high_frequency,
|
||||
float attenuation
|
||||
) {
|
||||
// Ensure an odd number of taps greater than or equal to 3, with a minimum attenuation of 21.
|
||||
number_of_taps = std::max<size_t>(3, number_of_taps) | 1;
|
||||
attenuation = std::max(attenuation, 21.0f);
|
||||
|
||||
/* calculate idealised filter response */
|
||||
std::size_t Np = (number_of_taps - 1) / 2;
|
||||
float two_over_sample_rate = 2.0f / input_sample_rate;
|
||||
const std::size_t Np = (number_of_taps - 1) / 2;
|
||||
const float two_over_sample_rate = 2.0f / input_sample_rate;
|
||||
|
||||
// Clamp the high cutoff frequency.
|
||||
high_frequency = std::min(high_frequency, input_sample_rate * 0.5f);
|
||||
@ -125,7 +134,7 @@ FIRFilter::FIRFilter(std::size_t number_of_taps, float input_sample_rate, float
|
||||
std::vector<float> A(Np+1);
|
||||
A[0] = 2.0f * (high_frequency - low_frequency) / input_sample_rate;
|
||||
for(unsigned int i = 1; i <= Np; ++i) {
|
||||
float i_pi = float(i) * float(M_PI);
|
||||
const float i_pi = float(i) * float(M_PI);
|
||||
A[i] =
|
||||
(
|
||||
sinf(two_over_sample_rate * i_pi * high_frequency) -
|
||||
@ -133,20 +142,22 @@ FIRFilter::FIRFilter(std::size_t number_of_taps, float input_sample_rate, float
|
||||
) / i_pi;
|
||||
}
|
||||
|
||||
FIRFilter::coefficients_for_idealised_filter_response(filter_coefficients_.data(), A.data(), attenuation, number_of_taps);
|
||||
filter_coefficients_ = coefficients_for_idealised_filter_response(A, attenuation, number_of_taps);
|
||||
}
|
||||
|
||||
FIRFilter::FIRFilter(const std::vector<float> &coefficients) {
|
||||
filter_coefficients_.reserve(coefficients.size());
|
||||
for(const auto coefficient: coefficients) {
|
||||
filter_coefficients_.push_back(short(coefficient * FixedMultiplier));
|
||||
}
|
||||
}
|
||||
|
||||
FIRFilter FIRFilter::operator+(const FIRFilter &rhs) const {
|
||||
std::vector<float> coefficients = get_coefficients();
|
||||
std::vector<float> rhs_coefficients = rhs.get_coefficients();
|
||||
const auto coefficients = get_coefficients();
|
||||
const auto rhs_coefficients = rhs.get_coefficients();
|
||||
|
||||
std::vector<float> sum;
|
||||
sum.reserve(coefficients.size());
|
||||
for(std::size_t i = 0; i < coefficients.size(); ++i) {
|
||||
sum.push_back((coefficients[i] + rhs_coefficients[i]) / 2.0f);
|
||||
}
|
||||
@ -155,9 +166,11 @@ FIRFilter FIRFilter::operator+(const FIRFilter &rhs) const {
|
||||
}
|
||||
|
||||
FIRFilter FIRFilter::operator-() const {
|
||||
const auto coefficients = get_coefficients();
|
||||
std::vector<float> negative_coefficients;
|
||||
|
||||
for(const auto coefficient: get_coefficients()) {
|
||||
negative_coefficients.reserve(coefficients.size());
|
||||
for(const auto coefficient: coefficients) {
|
||||
negative_coefficients.push_back(1.0f - coefficient);
|
||||
}
|
||||
|
||||
@ -165,10 +178,11 @@ FIRFilter FIRFilter::operator-() const {
|
||||
}
|
||||
|
||||
FIRFilter FIRFilter::operator*(const FIRFilter &rhs) const {
|
||||
std::vector<float> coefficients = get_coefficients();
|
||||
std::vector<float> rhs_coefficients = rhs.get_coefficients();
|
||||
const std::vector<float> coefficients = get_coefficients();
|
||||
const std::vector<float> rhs_coefficients = rhs.get_coefficients();
|
||||
|
||||
std::vector<float> sum;
|
||||
sum.reserve(coefficients.size());
|
||||
for(std::size_t i = 0; i < coefficients.size(); ++i) {
|
||||
sum.push_back(coefficients[i] * rhs_coefficients[i]);
|
||||
}
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include <vector>
|
||||
|
||||
namespace SignalProcessing {
|
||||
constexpr float FixedMultiplier = 32767.0f;
|
||||
constexpr int FixedShift = 15;
|
||||
|
||||
/*!
|
||||
The FIR filter takes a 1d PCM signal with a given sample rate and applies a band-pass filter to it.
|
||||
@ -28,13 +30,10 @@ namespace SignalProcessing {
|
||||
smaller numbers permit a filter that operates more quickly and with less lag but less effectively.
|
||||
*/
|
||||
class FIRFilter {
|
||||
private:
|
||||
static constexpr float FixedMultiplier = 32767.0f;
|
||||
static constexpr int FixedShift = 15;
|
||||
|
||||
public:
|
||||
/*! A suggested default attenuation value. */
|
||||
constexpr static float DefaultAttenuation = 60.0f;
|
||||
static constexpr float DefaultAttenuation = 60.0f;
|
||||
|
||||
/*!
|
||||
Creates an instance of @c FIRFilter.
|
||||
|
||||
@ -59,7 +58,7 @@ public:
|
||||
@param src The source buffer to apply the filter to.
|
||||
@returns The result of applying the filter.
|
||||
*/
|
||||
inline short apply(const short *src, size_t stride = 1) const {
|
||||
inline short apply(const short *const src, const size_t stride = 1) const {
|
||||
#ifdef USE_ACCELERATE
|
||||
short result;
|
||||
vDSP_dotpr_s1_15(
|
||||
@ -105,10 +104,6 @@ public:
|
||||
|
||||
private:
|
||||
std::vector<short> filter_coefficients_;
|
||||
|
||||
static void coefficients_for_idealised_filter_response(
|
||||
short *filterCoefficients, float *A, float attenuation, std::size_t numberOfTaps);
|
||||
static float ino(float a);
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user