{"_id":"digital-filter","_rev":"15-6b249f35974f485e8858c212fd669f75","name":"digital-filter","dist-tags":{"latest":"2.4.2"},"versions":{"1.0.0":{"name":"digital-filter","version":"1.0.0","keywords":["scijs","dsp","digital filters","psychoacoustics","noise","equal-loudness-curve","ISO-226","ITU-R-468"],"author":{"name":"Dima Yv","email":"dfcreative@gmail.com"},"license":"MIT","_id":"digital-filter@1.0.0","maintainers":[{"name":"dfcreative","email":"df.creative@gmail.com"}],"homepage":"https://github.com/scijs/digital-filter#readme","bugs":{"url":"https://github.com/scijs/digital-filter/issues"},"dist":{"shasum":"fcd8a1f07a8ad6dc491a1a1adcf858a0d3208377","tarball":"https://registry.npmjs.org/digital-filter/-/digital-filter-1.0.0.tgz","integrity":"sha512-7ZGapgvoiWpN9cx3VYXPIqQwA6VyHhusb6A6B5rB6gCpchH4BXOkER5DnIeqmx65Zrlz8//wxuB1nNPuBGuGbQ==","signatures":[{"sig":"MEUCIQCEob8oypCydOC4467xtQSOCxtuIcefH8ctwoUNgPcjkQIgF9uxTwIhf4cnNDnKfuBJs/b1Xi4XrF3kwxb+kuNyeZU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh2oO0CRA9TVsSAnZWagAAGxIP/RK0JxoyXxE/sFJcixfi\nNfmVvuLAHYSDm3Z7RnDxkpELuC3CJj5L1upZKkaotVKzfU9KHWHC9YleQV2N\nm73Tvf4lV1U328bO+DcYZk+OjBiYJbAeVlFhNMV4wNgVjSk0UeFeg9qyQZfE\nN7ibRniEbRzYmyHJRFOnmu4Bw/aJQ5PmgAlzrShY4mhbLwLaUR9ZBsoXMB2Y\nSxtm5/nTz6AhQxp7aDCUM/10j0bKTIVBG5oeHkikHDSrn/kKZZHBFTCRsZd2\nh9hLM4868suzwrVUjTevpRm3zSEps+Gk6hTe3kzI9heqPwqG5Yk0iXedvAoe\nrLVe3EJy1BxszO1nxmSi5J5uNH2e6AtAD9CHPXU3bRAup3HBoIWvwfjTqfTn\nXbS1lVG/Sgtcy2ZxQSiDa7Eev2WpDRI1Cj7lyigjb+vFCDuEF00uF98Saj7V\nwjskOvrqn2HzKzt/kJu0WvzvUgK4Cuk7O1Za6syRo+4IJgYAVJu8EGWO10jH\n4VgC9SWALPrHTBWQBs32R273TPtSD6A5Wo3rESy0dh58ua4qHwUcYkhc0t6m\nMqF9m9B9nqWSTo+yIEFFOqWeaNTJu5sJXTY7hCZU4b5wla+qUuhQ5SF4o3V8\nLYG5FGX3ZiwmfplpoCmq9pMPhFeHcRZoyUae7MCF/NiKfomSHRt75Bxa3mVs\nrewX\r\n=5goG\r\n-----END PGP SIGNATURE-----\r\n"},"main":"index.js","_from":".","_shasum":"fcd8a1f07a8ad6dc491a1a1adcf858a0d3208377","gitHead":"1d58276ed8e8a957bf6b8e36fb8ac2f0605c6350","scripts":{"test":"node test.js"},"_npmUser":{"name":"dfcreative","email":"df.creative@gmail.com"},"deprecated":"I have no qualification to complete that. Please email if you need this nice package name.","repository":{"url":"git+https://github.com/scijs/digital-filter.git","type":"git"},"_npmVersion":"3.7.2","description":"Collection of digital filters","directories":{},"_nodeVersion":"6.2.0","dependencies":{"average":"0.0.1"},"devDependencies":{"tape":"^4.6.3","almost-equal":"^1.1.0"},"_npmOperationalInternal":{"tmp":"tmp/digital-filter-1.0.0.tgz_1496072237402_0.40179607341997325","host":"s3://npm-registry-packages"}},"2.0.0":{"name":"digital-filter","version":"2.0.0","keywords":["dsp","digital-filter","biquad","iir","fir","butterworth","chebyshev","elliptic","bessel","audio","signal-processing","equalizer","filter-design","lowpass","highpass","bandpass","notch","smoothing","adaptive","multirate"],"author":{"name":"Dmitry Iv.","email":"dfcreative@gmail.com"},"license":"MIT","_id":"digital-filter@2.0.0","maintainers":[{"name":"dfcreative","email":"df.creative@gmail.com"},{"name":"dy","email":"df.creative@gmail.com"}],"homepage":"https://github.com/audiojs/digital-filter#readme","bugs":{"url":"https://github.com/audiojs/digital-filter/issues"},"dist":{"shasum":"1e0ea4f27099ca59c550f8eb5f4659d1b270fe0e","tarball":"https://registry.npmjs.org/digital-filter/-/digital-filter-2.0.0.tgz","fileCount":120,"integrity":"sha512-zmVb+mMOoDSlrDYTja6qfOrHWMQA/3P5pv50+vbUqzTf5f6gEQq7AAn+UMVrXQGlSvtKlnaqSls5h5P6+F4WDQ==","signatures":[{"sig":"MEQCIEDJu6SKbDO1y5Dsvsw5MVPO4sEjjrNnyUtsPArFG4uJAiBJSrhvWDSDy1lCQPcHOcxX8DC5fMtQtjYczNoogFCyOA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1861540},"type":"module","engines":{"node":">=18"},"exports":{".":"./index.js","./plot":"./plot/index.js","./plot/*":"./plot/*","./fir/*.js":"./fir/*.js","./iir/*.js":"./iir/*.js","./core/*.js":"./core/*.js","./smooth/*.js":"./smooth/*.js","./adaptive/*.js":"./adaptive/*.js","./multirate/*.js":"./multirate/*.js"},"gitHead":"48d64c15972ef0bcfd4ee4ddec15219b2c0ff5f4","scripts":{"plot":"node plot/generate.js","test":"node test.js"},"_npmUser":{"name":"dy","email":"df.creative@gmail.com"},"repository":{"url":"git+https://github.com/audiojs/digital-filter.git","type":"git"},"_npmVersion":"11.11.0","description":"Digital filter design & processing: IIR, FIR, smoothing, adaptive, multirate","directories":{},"_nodeVersion":"25.8.1","dependencies":{"window-function":"^3.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tst":"^9.4.0"},"_npmOperationalInternal":{"tmp":"tmp/digital-filter_2.0.0_1774681393976_0.855639155155709","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"digital-filter","version":"2.1.0","keywords":["dsp","digital-filter","biquad","iir","fir","butterworth","chebyshev","elliptic","bessel","audio","signal-processing","equalizer","filter-design","lowpass","highpass","bandpass","notch","smoothing","adaptive","multirate"],"author":{"name":"Dmitry Iv.","email":"dfcreative@gmail.com"},"license":"MIT","_id":"digital-filter@2.1.0","maintainers":[{"name":"dfcreative","email":"df.creative@gmail.com"},{"name":"dy","email":"df.creative@gmail.com"}],"homepage":"https://github.com/audiojs/digital-filter#readme","bugs":{"url":"https://github.com/audiojs/digital-filter/issues"},"dist":{"shasum":"dd493b5e3913f8c930d1f6f77fc3bc8d3bed9ef5","tarball":"https://registry.npmjs.org/digital-filter/-/digital-filter-2.1.0.tgz","fileCount":123,"integrity":"sha512-QkRVoyf9MfL8TIEgcd+UE52mt67lcgGpWr5jQnpJPGAzav9LhjXAsUmyI5C2XfD0yej1DRBjofo+ENmQnoru0g==","signatures":[{"sig":"MEUCIQC8Xx9Hxcwvma8eqKZolooa0bPDOxDA3X7PTwacnz3CQQIgY75de5bKNylEcH7qiK/L8IVUzBvhtekJrHPOodS5fhc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1880066},"type":"module","engines":{"node":">=18"},"exports":{".":"./index.js","./plot":"./plot/index.js","./plot/*":"./plot/*","./fir/*.js":"./fir/*.js","./iir/*.js":"./iir/*.js","./core/*.js":"./core/*.js","./smooth/*.js":"./smooth/*.js","./adaptive/*.js":"./adaptive/*.js","./multirate/*.js":"./multirate/*.js"},"gitHead":"e6e62c2c84031b1720ab1b8747a9499b62b15a82","scripts":{"plot":"node plot/generate.js","test":"node test.js"},"_npmUser":{"name":"dy","email":"df.creative@gmail.com"},"repository":{"url":"git+https://github.com/audiojs/digital-filter.git","type":"git"},"_npmVersion":"11.11.0","description":"Digital filter design & processing: IIR, FIR, smoothing, adaptive, multirate","directories":{},"_nodeVersion":"25.8.1","dependencies":{"window-function":"^3.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tst":"^9.4.0"},"_npmOperationalInternal":{"tmp":"tmp/digital-filter_2.1.0_1774904453540_0.9590777297519919","host":"s3://npm-registry-packages-npm-production"}},"2.2.0":{"name":"digital-filter","version":"2.2.0","keywords":["dsp","digital-filter","biquad","iir","fir","butterworth","chebyshev","elliptic","bessel","audio","signal-processing","equalizer","filter-design","lowpass","highpass","bandpass","notch","smoothing","adaptive","multirate"],"author":{"name":"Dmitry Iv.","email":"dfcreative@gmail.com"},"license":"MIT","_id":"digital-filter@2.2.0","maintainers":[{"name":"dfcreative","email":"df.creative@gmail.com"},{"name":"dy","email":"df.creative@gmail.com"}],"homepage":"https://github.com/audiojs/digital-filter#readme","bugs":{"url":"https://github.com/audiojs/digital-filter/issues"},"dist":{"shasum":"62f6ceb96018a7b86dd07917a4862c5e980398b4","tarball":"https://registry.npmjs.org/digital-filter/-/digital-filter-2.2.0.tgz","fileCount":123,"integrity":"sha512-/Di+wNg3gR940dc3yaGLDY9/BhKivdkR94BEauGHGVOdADpsA4dG/3aNsi6nmO5JZaNLFhbueDSz87XZLZumxw==","signatures":[{"sig":"MEYCIQCkCvyMRNu1XsS3uthyaDvTf9MIotcym7Dsevt7hRY76wIhANasj/f9Qm8JNd2WZZKUahaRd2olBVJZzH+t7GBOXoa3","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":10822064},"type":"module","engines":{"node":">=18"},"exports":{".":"./index.js","./plot":"./plot/index.js","./plot/*":"./plot/*","./fir/*.js":"./fir/*.js","./iir/*.js":"./iir/*.js","./core/*.js":"./core/*.js","./smooth/*.js":"./smooth/*.js","./adaptive/*.js":"./adaptive/*.js","./multirate/*.js":"./multirate/*.js"},"gitHead":"23c2e8eddd81dbc50e046520b754c6a6c96eb200","scripts":{"plot":"node plot/generate.js","test":"node test.js"},"_npmUser":{"name":"dy","email":"df.creative@gmail.com"},"repository":{"url":"git+https://github.com/audiojs/digital-filter.git","type":"git"},"_npmVersion":"11.11.0","description":"Digital filter design & processing: IIR, FIR, smoothing, adaptive, multirate","directories":{},"_nodeVersion":"25.8.1","dependencies":{},"_hasShrinkwrap":false,"devDependencies":{"tst":"^9.4.0"},"_npmOperationalInternal":{"tmp":"tmp/digital-filter_2.2.0_1774905432998_0.44351245909204207","host":"s3://npm-registry-packages-npm-production"}},"2.3.0":{"name":"digital-filter","version":"2.3.0","keywords":["dsp","digital-filter","biquad","iir","fir","butterworth","chebyshev","elliptic","bessel","audio","signal-processing","equalizer","filter-design","lowpass","highpass","bandpass","notch","smoothing","adaptive","multirate"],"author":{"name":"Dmitry Iv.","email":"dfcreative@gmail.com"},"license":"MIT","_id":"digital-filter@2.3.0","maintainers":[{"name":"dfcreative","email":"df.creative@gmail.com"},{"name":"dy","email":"df.creative@gmail.com"}],"homepage":"https://github.com/audiojs/digital-filter#readme","bugs":{"url":"https://github.com/audiojs/digital-filter/issues"},"dist":{"shasum":"3dcf25a339fae150d359a1947d2d1c12e151ffff","tarball":"https://registry.npmjs.org/digital-filter/-/digital-filter-2.3.0.tgz","fileCount":129,"integrity":"sha512-QuNh4EuMjymF9dtyNBXA51jUeXE8rfWV5t5cgEq8LKy3aBIsRDgaquTbRMgXIrDlbQi5u0H/8kIKm3rKgw+cgA==","signatures":[{"sig":"MEUCIQCt3RQ8l4M1W99INNSFC1KMuS4vLPU744lnJZabSTkzpwIgOoDiZzjmos/KPXHgcLMLjmip2rSqmpM7nWAlv5KV/yE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":10836271},"type":"module","engines":{"node":">=18"},"exports":{".":"./index.js","./plot":"./plot/index.js","./plot/*":"./plot/*","./fir/*.js":"./fir/*.js","./iir/*.js":"./iir/*.js","./core/*.js":"./core/*.js","./smooth/*.js":"./smooth/*.js","./adaptive/*.js":"./adaptive/*.js","./multirate/*.js":"./multirate/*.js"},"gitHead":"565d8423568102c50c23f598b82041864541c0a6","scripts":{"plot":"node plot/generate.js","test":"node test.js"},"_npmUser":{"name":"dy","email":"df.creative@gmail.com"},"repository":{"url":"git+https://github.com/audiojs/digital-filter.git","type":"git"},"_npmVersion":"11.11.0","description":"Digital filter design & processing: IIR, FIR, smoothing, adaptive, multirate","directories":{},"_nodeVersion":"25.8.1","dependencies":{},"_hasShrinkwrap":false,"devDependencies":{"tst":"^9.4.0"},"_npmOperationalInternal":{"tmp":"tmp/digital-filter_2.3.0_1774925687857_0.6260035991535644","host":"s3://npm-registry-packages-npm-production"}},"2.3.1":{"name":"digital-filter","version":"2.3.1","keywords":["dsp","digital-filter","biquad","iir","fir","butterworth","chebyshev","elliptic","bessel","audio","signal-processing","equalizer","filter-design","lowpass","highpass","bandpass","notch","smoothing","adaptive","multirate"],"author":{"name":"Dmitry Iv.","email":"dfcreative@gmail.com"},"license":"MIT","_id":"digital-filter@2.3.1","maintainers":[{"name":"dfcreative","email":"df.creative@gmail.com"},{"name":"dy","email":"df.creative@gmail.com"}],"homepage":"https://github.com/audiojs/digital-filter#readme","bugs":{"url":"https://github.com/audiojs/digital-filter/issues"},"dist":{"shasum":"cbecf09d608ea3d427219a899f489db0c0b9dc34","tarball":"https://registry.npmjs.org/digital-filter/-/digital-filter-2.3.1.tgz","fileCount":129,"integrity":"sha512-KzcWuhDLTt2lKFF4KZCrHSPOqHBTH+CxZwWDO0coqNTKiYyxgIwoTAuIQj1F8nFgwnCKomgwOXU62YCfsYVshg==","signatures":[{"sig":"MEYCIQCw9k/jRNxz8LYV+LLjQ9yxHhl8cnckWDJQmXNcVAk+RAIhAP2wFqQycDr8sO5MZFkELAi7ydLnjPoVZtKLjzgDSV9y","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":10836295},"type":"module","engines":{"node":">=18"},"exports":{".":"./index.js","./plot":"./plot/index.js","./plot/*":"./plot/*","./fir/*.js":"./fir/*.js","./iir/*.js":"./iir/*.js","./core/*.js":"./core/*.js","./smooth/*.js":"./smooth/*.js","./adaptive/*.js":"./adaptive/*.js","./multirate/*.js":"./multirate/*.js"},"gitHead":"c88e05279451a62e74661192dac183f8c68342e4","scripts":{"plot":"node plot/generate.js","test":"node test.js"},"_npmUser":{"name":"dy","email":"df.creative@gmail.com"},"repository":{"url":"git+https://github.com/audiojs/digital-filter.git","type":"git"},"_npmVersion":"11.11.0","description":"Digital filter design & processing: IIR, FIR, smoothing, adaptive, multirate","directories":{},"sideEffects":false,"_nodeVersion":"25.8.1","dependencies":{},"_hasShrinkwrap":false,"devDependencies":{"tst":"^9.4.0"},"_npmOperationalInternal":{"tmp":"tmp/digital-filter_2.3.1_1775579287680_0.6634935277309382","host":"s3://npm-registry-packages-npm-production"}},"2.4.0":{"name":"digital-filter","version":"2.4.0","keywords":["dsp","digital-filter","biquad","iir","fir","butterworth","chebyshev","elliptic","bessel","audio","signal-processing","equalizer","filter-design","lowpass","highpass","bandpass","notch","smoothing","adaptive","multirate"],"author":{"name":"Dmitry Iv.","email":"dfcreative@gmail.com"},"license":"MIT","_id":"digital-filter@2.4.0","maintainers":[{"name":"dfcreative","email":"df.creative@gmail.com"},{"name":"dy","email":"df.creative@gmail.com"}],"homepage":"https://github.com/audiojs/digital-filter#readme","bugs":{"url":"https://github.com/audiojs/digital-filter/issues"},"dist":{"shasum":"1b6de29e9c5071d71de948c304c8d12bd5c5ebb0","tarball":"https://registry.npmjs.org/digital-filter/-/digital-filter-2.4.0.tgz","fileCount":129,"integrity":"sha512-1iamIZuSBeB8EntqzQA4XyYIqtR6fZ3NMmkUQqcave2X8WZbFlrbNDObjv9xgNeHwV/wzaQnEwS07FVhyUzViA==","signatures":[{"sig":"MEQCIEodwAAtwrzLXojTa/J0RRoEwZvZ0kwJ72yPZxI6cU/VAiAH5GmHbmO7nsoBOlBx1BKvciUN/cW/Ay9B/L9ru4YQ0g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":10715277},"type":"module","engines":{"node":">=18"},"exports":{".":"./index.js","./plot":"./plot/index.js","./plot/*":"./plot/*","./fir/*.js":"./fir/*.js","./iir/*.js":"./iir/*.js","./core/*.js":"./core/*.js","./smooth/*.js":"./smooth/*.js","./adaptive/*.js":"./adaptive/*.js","./multirate/*.js":"./multirate/*.js"},"gitHead":"d555675dede27eba55dda53bd2f17351f7268b6c","scripts":{"plot":"node plot/generate.js","test":"node test.js"},"_npmUser":{"name":"dy","email":"df.creative@gmail.com"},"repository":{"url":"git+https://github.com/audiojs/digital-filter.git","type":"git"},"_npmVersion":"11.12.1","description":"Digital filter design & processing: IIR, FIR, smoothing, adaptive, multirate","directories":{},"sideEffects":false,"_nodeVersion":"25.9.0","dependencies":{},"_hasShrinkwrap":false,"devDependencies":{"tst":"^9.4.0"},"_npmOperationalInternal":{"tmp":"tmp/digital-filter_2.4.0_1783724969581_0.6546716559701033","host":"s3://npm-registry-packages-npm-production"}},"2.4.1":{"name":"digital-filter","version":"2.4.1","keywords":["dsp","digital-filter","biquad","iir","fir","butterworth","chebyshev","elliptic","bessel","audio","signal-processing","equalizer","filter-design","lowpass","highpass","bandpass","notch","smoothing","adaptive","multirate"],"author":{"name":"Dmitry Iv.","email":"dfcreative@gmail.com"},"license":"MIT","_id":"digital-filter@2.4.1","maintainers":[{"name":"dfcreative","email":"df.creative@gmail.com"},{"name":"dy","email":"df.creative@gmail.com"}],"homepage":"https://github.com/audiojs/digital-filter#readme","bugs":{"url":"https://github.com/audiojs/digital-filter/issues"},"dist":{"shasum":"15979e4f54319eb1158d828120acb3c034c825e2","tarball":"https://registry.npmjs.org/digital-filter/-/digital-filter-2.4.1.tgz","fileCount":129,"integrity":"sha512-evhe3IvozrrChdHduGElEcRaVDQqxQDEl8kHeYRLqrCLTeyJgMzDzK8ZrRHKD4rzmUYnDfls8Yj5AKNypjRKPA==","signatures":[{"sig":"MEUCIQCB2C2C3iasQzJakVhVB6weJ0KmcgKDcnbof8S3Wbq58AIgUYxHRTR8jOJAUGcDkiE8zzUcuFDsYkz2ONxZVadViHY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":10714644},"type":"module","engines":{"node":">=18"},"exports":{".":"./index.js","./plot":"./plot/index.js","./plot/*":"./plot/*","./fir/*.js":"./fir/*.js","./iir/*.js":"./iir/*.js","./core/*.js":"./core/*.js","./smooth/*.js":"./smooth/*.js","./adaptive/*.js":"./adaptive/*.js","./multirate/*.js":"./multirate/*.js"},"gitHead":"d555675dede27eba55dda53bd2f17351f7268b6c","scripts":{"plot":"node plot/generate.js","test":"node test.js"},"_npmUser":{"name":"dy","email":"df.creative@gmail.com"},"repository":{"url":"git+https://github.com/audiojs/digital-filter.git","type":"git"},"_npmVersion":"11.12.1","description":"Digital filter design & processing: IIR, FIR, smoothing, adaptive, multirate","directories":{},"sideEffects":false,"_nodeVersion":"25.9.0","dependencies":{"window-function":"^3.0.1"},"_hasShrinkwrap":false,"devDependencies":{"tst":"^9.4.0"},"_npmOperationalInternal":{"tmp":"tmp/digital-filter_2.4.1_1783725292449_0.8511398184737016","host":"s3://npm-registry-packages-npm-production"}},"2.4.2":{"name":"digital-filter","version":"2.4.2","type":"module","sideEffects":false,"description":"Digital filter design & processing: IIR, FIR, smoothing, adaptive, multirate","exports":{".":"./index.js","./core/*.js":"./core/*.js","./iir/*.js":"./iir/*.js","./fir/*.js":"./fir/*.js","./smooth/*.js":"./smooth/*.js","./adaptive/*.js":"./adaptive/*.js","./multirate/*.js":"./multirate/*.js","./plot":"./plot/index.js","./plot/*":"./plot/*"},"scripts":{"test":"node test.js","plot":"node plot/generate.js"},"repository":{"type":"git","url":"git+https://github.com/audiojs/digital-filter.git"},"keywords":["dsp","digital-filter","biquad","iir","fir","butterworth","chebyshev","elliptic","bessel","audio","signal-processing","equalizer","filter-design","lowpass","highpass","bandpass","notch","smoothing","adaptive","multirate"],"author":{"name":"Dmitry Iv.","email":"dfcreative@gmail.com"},"license":"MIT","bugs":{"url":"https://github.com/audiojs/digital-filter/issues"},"homepage":"https://github.com/audiojs/digital-filter#readme","engines":{"node":">=18"},"dependencies":{"window-function":"^3.0.1"},"devDependencies":{"tst":"^9.4.0"},"funding":"https://github.com/sponsors/audiojs","gitHead":"41bc62587fa0cb11b78bfdf3b8bc0adb712781b8","_id":"digital-filter@2.4.2","_nodeVersion":"25.9.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-whhiIcz/YlKqDca8CqpGjMvP4Jly/De2QCCmilPAeKgZFAyzTS62prcdl0pKZOQxhez33pipbsF8WYVY3/9YTA==","shasum":"cdf14064bce93c81576c1c285e85a14580302689","tarball":"https://registry.npmjs.org/digital-filter/-/digital-filter-2.4.2.tgz","fileCount":129,"unpackedSize":10714696,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIAhQj2pvL0vjLfU/78Y94HmEDaUtadsyad6DZXaLt8wLAiB0eCVYxne9td6P6/Ag4LrErW+n4X+05ZhSMgvfnJfD/Q=="}]},"_npmUser":{"name":"dy","email":"df.creative@gmail.com"},"directories":{},"maintainers":[{"name":"dfcreative","email":"df.creative@gmail.com"},{"name":"dy","email":"df.creative@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/digital-filter_2.4.2_1783732014888_0.9721832872846716"},"_hasShrinkwrap":false}},"time":{"created":"2017-05-29T15:37:18.295Z","modified":"2026-07-11T01:06:55.185Z","1.0.0":"2017-05-29T15:37:18.295Z","2.0.0":"2026-03-28T07:03:14.154Z","2.1.0":"2026-03-30T21:00:53.752Z","2.2.0":"2026-03-30T21:17:13.217Z","2.3.0":"2026-03-31T02:54:48.096Z","2.3.1":"2026-04-07T16:28:07.973Z","2.4.0":"2026-07-10T23:09:29.812Z","2.4.1":"2026-07-10T23:14:52.704Z","2.4.2":"2026-07-11T01:06:55.079Z"},"bugs":{"url":"https://github.com/audiojs/digital-filter/issues"},"author":{"name":"Dmitry Iv.","email":"dfcreative@gmail.com"},"license":"MIT","homepage":"https://github.com/audiojs/digital-filter#readme","keywords":["dsp","digital-filter","biquad","iir","fir","butterworth","chebyshev","elliptic","bessel","audio","signal-processing","equalizer","filter-design","lowpass","highpass","bandpass","notch","smoothing","adaptive","multirate"],"repository":{"type":"git","url":"git+https://github.com/audiojs/digital-filter.git"},"description":"Digital filter design & processing: IIR, FIR, smoothing, adaptive, multirate","maintainers":[{"name":"dfcreative","email":"df.creative@gmail.com"},{"name":"dy","email":"df.creative@gmail.com"}],"readme":"# digital-filter [![test](https://github.com/audiojs/digital-filter/actions/workflows/test.yml/badge.svg)](https://github.com/audiojs/digital-filter/actions/workflows/test.yml) [![npm](https://img.shields.io/npm/v/digital-filter)](https://www.npmjs.com/package/digital-filter) [![MIT](https://img.shields.io/badge/MIT-%E0%A5%90-white)](https://github.com/krishnized/license)\n\nDigital filter design and processing.\n\n<table><tr><td valign=\"top\">\n\n**[IIR](#iir)**<br>\n<sub>[biquad](#biquad) · [svf](#svfdata-params) · [butterworth](#butterworthorder-fc-fs-type) · [chebyshev](#chebyshevorder-fc-fs-ripple-type) · [chebyshev2](#chebyshev2order-fc-fs-attenuation-type) · [elliptic](#ellipticorder-fc-fs-ripple-attenuation-type) · [bessel](#besselorder-fc-fs-type) · [legendre](#legendreorder-fc-fs-type) · [linkwitzRiley](#linkwitzrileyorder-fc-fs) · [iirdesign](#iirdesignfpass-fstop-rp-rs-fs) · [buttord](#buttordfpass-fstop-rp-rs-fs) · [cheb1ord](#cheb1ordfpass-fstop-rp-rs-fs) · [cheb2ord](#cheb2ordfpass-fstop-rp-rs-fs) · [ellipord](#ellipordfpass-fstop-rp-rs-fs)</sub>\n\n**[FIR](#fir)**<br>\n<sub>[firwin](#firwinnumtaps-cutoff-fs-opts) · [firls](#firlsnumtaps-bands-desired-weight) · [remez](#remeznumtaps-bands-desired-weight) · [firwin2](#firwin2numtaps-freq-gain-opts) · [hilbert](#hilbertn) · [differentiator](#differentiatorn-opts) · [raisedCosine](#raisedcosinen-beta-sps-opts) · [gaussianFir](#gaussianfirn-bt-sps) · [matchedFilter](#matchedfiltertemplate) · [minimumPhase](#minimumphaseh) · [yulewalk](#yulewalkorder-frequencies-magnitudes) · [kaiserord](#kaiserorddeltaf-attenuation) · [integrator](#integratorrule) · [lattice](#latticedata-params) · [warpedFir](#warpedfirdata-params)</sub>\n\n**[Smooth](#smooth)**<br>\n<sub>[onePole](#onepoledata-params) · [movingAverage](#movingaveragedata-params) · [leakyIntegrator](#leakyintegratordata-params) · [savitzkyGolay](#savitzkygolaydata-params) · [gaussianIir](#gaussianiirdata-params) · [dynamicSmoothing](#dynamicsmoothingdata-params) · [median](#mediandata-params) · [oneEuro](#oneeuropdata-params)</sub>\n\n</td><td valign=\"top\">\n\n**[Adaptive](#adaptive)**<br>\n<sub>[lms](#lmsinput-desired-params) · [nlms](#nlmsinput-desired-params) · [rls](#rlsinput-desired-params) · [levinson](#levinsonr-order)</sub>\n\n**[Multirate](#multirate)**<br>\n<sub>[decimate](#decimatedata-factor-opts) · [interpolate](#interpolatedata-factor-opts) · [resample](#resampledata-p-q-opts) · [upfirdn](#upfirdndata-h-up-down) · [halfBand](#halfbandnumtaps) · [cic](#cicdata-r-n) · [polyphase](#polyphaseh-m) · [farrow](#farrowdata-params) · [thiran](#thirandelay-order) · [oversample](#oversampledata-factor-opts)</sub>\n\n**[Core](#core)**<br>\n<sub>[filter](#filterdata-params) · [iir](#iirdata-params) · [filtfilt](#filtfiltdata-params) · [convolution](#convolutionsignal-ir) · [detrend](#detrenddata-type) · [freqz](#freqzcoefs-n-fs) · [mag2db](#mag2dbmag) · [groupDelay](#groupdelaycoefs-n-fs) · [phaseDelay](#phasedelaycoefs-n-fs) · [impulseResponse](#impulseresponsecoefs-n) · [stepResponse](#stepresponsecoefs-n) · [isStable](#isstablesos) · [isMinPhase](#isminphasesos) · [isFir](#isfirsos) · [isLinPhase](#islinphaseh) · [sos2zpk](#sos2zpksos) · [sos2tf](#sos2tfsos) · [tf2zpk](#tf2zpkb-a) · [tf2sos](#tf2sosb-a) · [zpk2sos](#zpk2soszpk) · [zpk2tf](#zpk2tfzpk) · [tf2ss](#tf2ssb-a--ss2tfa-b-c-d) · [ss2tf](#tf2ssb-a--ss2tfa-b-c-d) · [residue](#residueb-a) · [matchedZ](#matchedzpoles-zeros-fs-normfreq) · [deconvolve](#deconvolveb-a) · [wiener](#wienerdata-opts) · [sosfiltZi](#sosfiltzisos) · [transform](#transform)</sub>\n\n</td></tr></table>\n\n## Install\n\n```\nnpm install digital-filter\n```\n\n```js\nimport { butterworth, filter, onePole } from 'digital-filter'\n// import butterworth from 'digital-filter/iir/butterworth.js'\n\nonePole(data, { fc: 100, fs: 44100 })           // smooth in-place\n\nlet sos = butterworth(4, 1000, 44100)            // design a 4th-order lowpass\nfilter(data, { coefs: sos })                     // apply in-place\n\nlet params = { coefs: sos }\nfilter(block1, params)                           // state persists between blocks\nfilter(block2, params)\n```\n\n> For audio-domain filters (weighting, EQ, synth, measurement) see [audio-filter](https://github.com/audiojs/audio-filter).\n\n## Intro\n\n**Filter.** Takes an array of samples, outputs an array of samples. `output[i] = (input[i] + input[i-1] + input[i-2]) / 3` smooths out fast changes – that's a lowpass.\n\n**Frequency response.** Every filter passes some frequencies and cuts others. The plots show how much each frequency is kept (magnitude, in dB) and how much it's delayed (phase). 0 dB = unchanged, –3 dB = half power.\n\n**IIR vs FIR.**\n\n| | IIR | FIR |\n|---|---|---|\n| **How** | Feedback (output depends on previous output) | No feedback |\n| **Efficiency** | 5–20 multiplies for sharp lowpass | 100–1000+ multiplies |\n| **Phase** | Nonlinear (always) | Linear (symmetric coefficients) |\n| **Stability** | Can blow up | Always stable |\n| **Latency** | Low (few samples) | High (N/2 samples) |\n| **Adaptive** | Hard to adapt coefficients in real time | Easy – coefficients are the taps (LMS, NLMS) |\n| **Use for** | Real-time, low latency | Offline, linear phase, adaptive |\n\n**Adaptive**. Means the filter adjusts its own coefficients in real time to minimize error against a reference signal — used for echo cancellation, noise cancellation, system identification.\n\n**SOS.** Second-Order Sections – an IIR filter split into a chain of biquads (2nd-order, 5 coefficients each). A 4th-order Butterworth = 2 biquads. All design functions return SOS arrays to avoid float64 precision loss.\n\n**Plots.** Four panels. Top-left: magnitude (dB vs Hz). Top-right: phase (degrees vs Hz). Bottom-left: group delay (samples vs Hz), flat = no distortion. Bottom-right: impulse response. Dashed line = $f_c$.\n\n**Formulas.** $|H(j\\omega)|^2$: analog prototype magnitude. $H(z)$: digital transfer function. $h[n]$: impulse response / FIR coefficients.\n\n\n## IIR\n\nIIR filters use feedback – efficient (5–20 multiplies for a sharp lowpass), low latency, nonlinear phase. Designed from analog prototypes via the [bilinear transform](#faq) (or matched-z transform), implemented as cascaded second-order sections (SOS).[^sos]\n\n[^sos]: Direct form above order ~6 loses precision with float64. Cascaded biquads don't.\n\n### `biquad`\n\nNine second-order filter types – the building block for everything else. Every parametric EQ, every crossover, every Butterworth cascade is made of these.[^rbj]\n\n[^rbj]: Robert Bristow-Johnson, [Audio EQ Cookbook](https://www.w3.org/TR/audio-eq-cookbook/), 1998.\n\n* `biquad.lowpass(fc, Q, fs)` · `highpass` · `bandpass` · `bandpass2` · `notch` · `allpass`\n* `biquad.peaking(fc, Q, fs, dBgain)` · `lowshelf` · `highshelf`\n\n<sup>Q controls peak width – 0.707 is Butterworth-flat, higher = sharper resonance.</sup>\n\n$H(z) = (b_0 + b_1 z^{-1} + b_2 z^{-2}) / (1 + a_1 z^{-1} + a_2 z^{-2})$\n\n```js\nlet lp = biquad.lowpass(1000, 0.707, 44100)\nfilter(data, { coefs: lp })\n```\n\n**Use when**: single-band EQ, notch, shelf, simple 2nd-order filter.<br>\n**Not for**: steeper than –12 dB/oct (use butterworth/chebyshev which cascade biquads).<br>\n**scipy**: `scipy.signal.iirfilter(1, ...)`. **MATLAB**: various Audio Toolbox functions.\n\n<img src=\"plot/biquad-types.svg\">\n\n### `svf(data, params)`\n\nState variable filter – same transfer function as a biquad, but trapezoidal integration allows zero-delay feedback. Safe for per-sample parameter modulation. Six response types from one topology. Simper/Cytomic (2011). Params: `fc`, `Q`, `fs`, `type`.\n\n$g = \\tan(\\pi f_c/f_s)$, $k = 1/Q$\n\n```js\nsvf(data, { fc: 1000, Q: 2, fs: 44100, type: 'lowpass' })\n```\n\n**Use when**: real-time synthesis with parameter modulation (LFO, envelope, touch).<br>\n**Not for**: need SOS coefficients for analysis (use biquad), higher than 2nd order.\n\n<img src=\"plot/svf-lowpass.svg\">\n\n### `butterworth(order, fc, fs, type?)`\n\nMaximally flat magnitude – no ripple anywhere. The safe default for anti-aliasing, crossovers, general-purpose filtering. Butterworth (1930).[^bw]\n\n[^bw]: S. Butterworth, \"On the Theory of Filter Amplifiers,\" *Wireless Engineer*, 1930.\n\n$|H(j\\omega)|^2 = 1/(1 + (\\omega/\\omega_c)^{2N})$<br>\nPoles at $s_k = \\omega_c \\cdot e^{j\\pi(2k+N+1)/(2N)}$.\n\n**–3 dB at fc · –6N dB/oct slope · 10.9% overshoot at order 4 · 73 samples settling**\n\n```js\nlet sos = butterworth(4, 1000, 44100)\nfilter(data, { coefs: sos })\n```\n\n**Use when**: general-purpose filtering, anti-aliasing, crossovers.<br>\n**Not for**: sharpest transition (use [chebyshev](#chebyshev2order-fc-fs-attenuation-type)/[elliptic](#ellipticorder-fc-fs-ripple-attenuation-type)), waveform preservation (use [bessel](#besselorder-fc-fs-type)).<br>\n**scipy**: `scipy.signal.butter`. **MATLAB**: `butter`.\n\n<img src=\"plot/butterworth.svg\">\n\n\n### `chebyshev(order, fc, fs, ripple?, type?)`\n\nSteeper cutoff than Butterworth for the same order – at the cost of passband ripple.\n\n$|H(j\\omega)|^2 = 1/(1 + \\varepsilon^2 T_N^2(\\omega/\\omega_c))$ — $T_N$ is the Nth Chebyshev polynomial (oscillates in passband, grows fast in stopband). $\\varepsilon = \\sqrt{10^{R_p/10} - 1}$.\n\n**Default 1 dB ripple · –34 dB at 2× fc · 8.7% overshoot · 256 samples settling**\n\n```js\nlet sos = chebyshev(4, 1000, 44100, 1)  // 1 dB ripple\n```\n\n**Use when**: sharper cutoff than Butterworth, passband ripple tolerable.<br>\n**Not for**: passband flatness (use butterworth/legendre), waveform shape (use bessel).<br>\n**scipy**: `scipy.signal.cheby1`. **MATLAB**: `cheby1`.\n\n<img src=\"plot/chebyshev.svg\">\n\n\n### `chebyshev2(order, fc, fs, attenuation?, type?)`\n\nFlat passband, equiripple stopband. The ripple goes into the rejection region instead. **`fc` is the STOPBAND edge** — the frequency where attenuation first reaches the floor — not the passband edge like every other IIR family.\n\n$|H(j\\omega)|^2 = 1/(1 + 1/(\\varepsilon^2 T_N^2(\\omega_c/\\omega)))$ — inverse of Type I. Zeros on $j\\omega$ axis enforce stopband floor.\n\n**Flat passband · –40 dB stopband floor · –40 dB at 2× fc**\n\n```js\nlet sos = chebyshev2(4, 2000, 44100, 40)  // exactly -40 dB at 2 kHz, flat below\n```\n\n**Use when**: flat passband needed with sharper rolloff than Butterworth.<br>\n**Not for**: deep stopband at high frequencies (Butterworth keeps falling; Cheby II bounces).<br>\n**scipy**: `scipy.signal.cheby2`. **MATLAB**: `cheby2`.\n\n<img src=\"plot/chebyshev2.svg\">\n\n### `elliptic(order, fc, fs, ripple?, attenuation?, type?)`\n\nSharpest transition for a given order – ripple in both passband and stopband. A 4th-order elliptic matches a 7th-order Butterworth. Cauer (1958).[^cauer]\n\n[^cauer]: W. Cauer, *Synthesis of Linear Communication Networks*, 1958.\n\n$|H(j\\omega)|^2 = 1/(1 + \\varepsilon^2 R_N^2(\\omega/\\omega_c))$ — $R_N$ is a rational Chebyshev (Jacobi elliptic) function.\n\n**Default 1 dB ripple, 40 dB attenuation · –40 dB at 2× fc · 10.6% overshoot**\n\n```js\nlet sos = elliptic(4, 1000, 44100, 1, 40)\n```\n\n**Use when**: minimum order / sharpest transition is critical.<br>\n**Not for**: passband flatness or waveform shape (worst phase response of all families).<br>\n**scipy**: `scipy.signal.ellip`. **MATLAB**: `ellip`.\n\n<img src=\"plot/elliptic.svg\">\n\n### `bessel(order, fc, fs, type?)`\n\nMaximally flat group delay – preserves waveform shape with near-zero overshoot. For biomedical signals (ECG, EEG), control systems, anywhere ringing distorts the measurement. Thomson (1949).[^thomson]\n\n[^thomson]: W.E. Thomson, \"Delay Networks Having Maximally Flat Frequency Characteristics,\" *Proc. IEE*, 1949.\n\n$H(s) = \\theta_N(0)/\\theta_N(s/\\omega_c)$ — $\\theta_N$ is the reverse Bessel polynomial. Poles cluster near negative real axis.\n\n**–3 dB at fc · –14 dB at 2× fc (gentlest rolloff) · 0.9% overshoot · 28 samples settling**\n\n```js\nlet sos = bessel(4, 1000, 44100)\n```\n\n**Use when**: waveform preservation (ECG, transients, control systems).<br>\n**Not for**: sharp frequency cutoff (gentlest rolloff of all families).<br>\n**scipy**: `scipy.signal.bessel`. **MATLAB**: `besself` (analog only).\n\n<img src=\"plot/bessel.svg\">\n\n### `legendre(order, fc, fs, type?)`\n\nSteepest monotonic (ripple-free) rolloff. Between Butterworth and Chebyshev. Papoulis (1958), Bond (2004).[^papoulis]\n\n[^papoulis]: A. Papoulis, \"Optimum Filters with Monotonic Response,\" *Proc. IRE*, 1958.\n\n$|H(j\\omega)|^2 = 1 - P_N(1 - 2(\\omega/\\omega_c)^2)$ — $P_N$ maximizes rolloff slope while staying monotonic.\n\n**–3 dB at fc · –31 dB at 2× fc · no ripple · 11.3% overshoot**\n\n```js\nlet sos = legendre(4, 1000, 44100)\n```\n\n**Use when**: sharpest cutoff without any ripple.<br>\n**Not for**: ripple tolerable (chebyshev is steeper), waveform shape (use bessel).\n\n<img src=\"plot/legendre.svg\">\n\n### `linkwitzRiley(order, fc, fs)`\n\nCrossover: LP + HP sum to perfectly flat magnitude. Two cascaded Butterworth filters. Linkwitz & Riley (1976).[^lr] Returns `{ low, high }`. Order must be even (2, 4, 6, 8).\n\n[^lr]: S.H. Linkwitz, \"Active Crossover Networks for Noncoincident Drivers,\" *JAES*, 1976.\n\n**–6 dB at fc (both bands) · bands sum to 0 dB at all frequencies**\n\n```js\nlet { low, high } = linkwitzRiley(4, 2000, 44100)\n```\n\n**Use when**: crossover networks, multiband processing.<br>\n**Not for**: single LP or HP (use butterworth directly), more than 2 bands (use crossover from audio-filter).<br>\n**MATLAB**: `fdesign.crossover` (Audio Toolbox).\n\n<img src=\"plot/linkwitz-riley-low.svg\">\n\n### `iirdesign(fpass, fstop, rp?, rs?, fs?)`\n\nGive it your specs and it picks the best family and minimum order automatically. Returns `{ sos, order, type }`.\n\n```js\nlet { sos, order, type } = iirdesign(1000, 1500, 1, 40, 44100)\n```\n\n**Use when**: you know specs (passband, stopband, ripple, attenuation) but not the family.<br>\n**Not for**: specific family needed (use butterworth/chebyshev/etc directly).<br>\n**scipy**: `scipy.signal.iirdesign`. **MATLAB**: `designfilt`.\n\n### `buttord(fpass, fstop, rp, rs, fs)` · `cheb1ord` · `cheb2ord` · `ellipord`\n\nEstimate minimum filter order needed to meet specs. Returns `{ order, Wn }`.\n\n```js\nlet { order, Wn } = buttord(1000, 1500, 1, 40, 44100)     // → order = 13\nlet { order: nEl } = ellipord(1000, 1500, 1, 40, 44100)   // → order = 5 (much lower)\n```\n\n**scipy**: `scipy.signal.buttord`, `cheb1ord`, `cheb2ord`, `ellipord`.<br>\n**MATLAB**: `buttord`, `cheb1ord`, `cheb2ord`, `ellipord`.\n\n### IIR comparison\n\nAll at order 4, $f_c = 1\\text{kHz}$, $f_s = 44100\\text{Hz}$:\n\n<img src=\"plot/iir-comparison.svg\">\n\n| | Butterworth | Chebyshev I | Chebyshev II | Elliptic | Bessel | Legendre |\n|---|---|---|---|---|---|---|\n| **Passband** | Flat | 1 dB ripple | Flat | 1 dB ripple | Flat (soft) | Flat |\n| **@2 kHz** | –24 dB | –34 dB | –40 dB | –40 dB | –14 dB | –31 dB |\n| **Overshoot** | 10.9% | 8.7% | 13.0% | 10.6% | **0.9%** | 11.3% |\n| **Best for** | General | Sharp cutoff | Flat pass | Min order | No ringing | Sharp, no ripple |\n\n\n## FIR\n\nFinite impulse response – no feedback, always stable. Symmetric coefficients give perfect linear phase. More taps = sharper cutoff = more latency. All design functions return `Float64Array`.\n\n### `firwin(numtaps, cutoff, fs, opts?)`\n\nWindow method FIR – truncated sinc multiplied by a window function. Supports `lowpass`, `highpass`, `bandpass`, `bandstop`. `opts.window`: `'hamming'` (default), `'hann'`, `'blackman'`, `'blackman-harris'`, `'bartlett'`, `'rectangular'`, `['kaiser', beta]`, a `Float64Array`, or a `(N) => array` function — unknown names throw.\n\n$h[n] = \\sin(\\omega_c n)/(\\pi n) \\cdot w[n]$ — sinc gives ideal brick-wall, window smooths truncation.\n\n**Linear phase · delay = (N–1)/2 samples · –43 dB sidelobes (Hamming)**\n\n```js\nlet h = firwin(63, 1000, 44100)\n```\n\n**Use when**: 80% of FIR tasks – quick, predictable LP/HP/BP/BS.<br>\n**Not for**: tight specs (use remez), arbitrary shapes (use firwin2).<br>\n**scipy**: `scipy.signal.firwin`. **MATLAB**: `fir1`.\n\n<img src=\"plot/firwin-lp.svg\">\n\n### `firls(numtaps, bands, desired, weight?)`\n\nLeast-squares optimal – minimizes total squared error. Smoother transitions than remez.\n\n$\\min \\int |W(\\omega)(H(\\omega) - D(\\omega))|^2 d\\omega$\n\n**Linear phase · smooth transition · no equiripple**\n\n```js\nlet h = firls(63, [0, 0.3, 0.4, 1], [1, 1, 0, 0])\n```\n\n**Use when**: average error matters more than worst-case, audio interpolation.<br>\n**Not for**: tight stopband specs (use remez).<br>\n**scipy**: `scipy.signal.firls`. **MATLAB**: `firls`.\n\n<img src=\"plot/firls.svg\">\n\n### `remez(numtaps, bands, desired, weight?)`\n\nParks-McClellan equiripple – narrowest transition band for given taps. Parks & McClellan (1972).[^pm]\n\n[^pm]: T.W. Parks, J.H. McClellan, \"Chebyshev Approximation for Nonrecursive Digital Filters,\" *IEEE Trans.*, 1972.\n\n$\\min \\max_\\omega |W(\\omega)(H(\\omega) - D(\\omega))|$ — minimizes peak (worst-case) error.\n\n**Linear phase · equiripple · sharpest cutoff per tap**\n\n```js\nlet h = remez(63, [0, 0.3, 0.4, 1], [1, 1, 0, 0])\n```\n\n**Use when**: tight specs, guaranteed worst-case rejection.<br>\n**Not for**: sidelobes must decay (use firwin with window), average error (use firls).<br>\n**scipy**: `scipy.signal.remez`. **MATLAB**: `firpm`.\n\n<img src=\"plot/remez.svg\">\n\n### `firwin2(numtaps, freq, gain, opts?)`\n\nArbitrary magnitude response via frequency sampling. Specify gain at any frequency points.\n\n$H(\\omega_k) = G_k$\n\n**Linear phase · any shape**\n\n```js\nlet h = firwin2(201, [0, 0.1, 0.2, 0.4, 0.5, 1], [0, 0, 1, 1, 0, 0])\n```\n\n**Use when**: custom EQ curves, matching measured responses.<br>\n**scipy**: `scipy.signal.firwin2`. **MATLAB**: `fir2`.\n\n<img src=\"plot/firwin2.svg\">\n\n### `hilbert(N)`\n\n90° phase shift at unity magnitude. For analytic signal, envelope extraction, SSB modulation, pitch detection.\n\n$h[n] = 2/(\\pi n)$ for odd $n$, $0$ for even — ideal Hilbert transformer, windowed.\n\n**Linear phase · zero at DC and Nyquist**\n\n```js\nlet h = hilbert(65)\n```\n\n**Use when**: analytic signal, envelope, instantaneous frequency.<br>\n**Not for**: wideband to DC (Hilbert is zero at DC/Nyquist).<br>\n**scipy**: `scipy.signal.hilbert` (different – applies to signal, not design).<br>\n**MATLAB**: `hilbert` (same caveat).\n\n<img src=\"plot/hilbert.svg\">\n\n### `differentiator(N, opts?)`\n\nFIR derivative with better noise immunity than a first difference.\n\n$h[n] = (-1)^n/n$ windowed.\n\n**Antisymmetric · linear phase**\n\n```js\nlet h = differentiator(31)\n```\n\n**Use when**: rate-of-change, velocity from position, edge detection.<br>\n**Not for**: noisy data needing simultaneous smoothing (use savitzkyGolay with derivative:1).\n\n<img src=\"plot/differentiator.svg\">\n\n### `raisedCosine(N, beta?, sps?, opts?)`\n\nPulse shaping for digital communications (QAM, PSK, OFDM). Zero ISI at symbol centers. `root: true` for matched TX/RX pair.\n\n$h(t) = \\text{sinc}(t/T) \\cdot \\cos(\\pi\\beta t/T)/(1-(2\\beta t/T)^2)$\n\n$\\beta$ controls excess bandwidth: 0 = minimum (long ringing), 0.35 = standard, 1 = widest.\n\n```js\nlet h = raisedCosine(101, 0.35, 8, { root: true })\n```\n\n**Use when**: QAM/PSK pulse shaping, SDR baseband.<br>\n**MATLAB**: `rcosdesign`.\n\n<img src=\"plot/raised-cosine.svg\">\n\n### `gaussianFir(N, bt?, sps?)`\n\nGaussian pulse shaping – the standard for GMSK (GSM) and Bluetooth. More spectrally compact than raised cosine at the cost of some ISI.\n\n$h(t) = \\exp(-2\\pi^2(BT)^2 t^2/T^2)$\n\n```js\nlet h = gaussianFir(33, 0.3, 4)\n```\n\n**Use when**: GMSK/Bluetooth modulation.<br>\n**Not for**: ISI-free pulses (use raisedCosine).<br>\n**MATLAB**: `gaussdesign`.\n\n<img src=\"plot/gaussian-fir.svg\">\n\n### `matchedFilter(template)`\n\nOptimal detector for a known waveform in white noise – time-reversed, energy-normalized template. Maximizes SNR at detection point; correlating against the template itself peaks at exactly 1.\n\n$h[n] = s[N-1-n] / \\|s\\|^2$\n\n```js\nlet h = matchedFilter(template)\nlet corr = convolution(received, h)\n```\n\n**Use when**: radar, sonar, preamble/sync detection.<br>\n**Not for**: colored noise (pre-whiten first), unknown template (use adaptive).\n\n<img src=\"plot/matched-filter.svg\">\n\n### `minimumPhase(h)`\n\nConvert linear-phase FIR to minimum-phase via cepstral method. Same magnitude, ~half the delay.\n\n$h_{min} = \\text{IFFT}(\\exp(\\text{hilbert}(\\log|H|)))$\n\n```js\nlet hMin = minimumPhase(firwin(101, 4000, 44100))\n```\n\n**Use when**: FIR latency too high, linear phase not required.<br>\n**scipy**: `scipy.signal.minimum_phase`.\n\n<img src=\"plot/minimum-phase.svg\">\n\n### `yulewalk(order, frequencies, magnitudes)`\n\nIIR approximation of an arbitrary magnitude response via Yule-Walker method. Returns `{ b, a }`.\n\n$R \\cdot a = r$ (Yule-Walker equations from target autocorrelation).\n\n```js\nlet { b, a } = yulewalk(8, [0, 0.2, 0.3, 0.5, 1], [1, 1, 0, 0, 0])\n```\n\n**Use when**: IIR match to target curve. **MATLAB**: `yulewalk`.\n\n<img src=\"plot/yulewalk.svg\">\n\n### `integrator(rule?)`\n\nNewton-Cotes quadrature coefficients. Rules: `rectangular` [1], `trapezoidal` [0.5, 0.5], `simpson` [1/6, 4/6, 1/6], `simpson38` [1/8, 3/8, 3/8, 1/8].\n\nSimpson: $h = [1/6, 4/6, 1/6]$\n\n```js\nlet h = integrator('simpson')\n```\n\n**Use when**: numerical integration of sampled data.\n\n<img src=\"plot/integrator.svg\">\n\n### `lattice(data, params)`\n\nLattice filter over reflection coefficients (PARCOR). Alternative topology to direct form – each stage is independently stable when $|k_i| < 1$. Params: `k` (reflection coefficients), `type` (`'fir'` analysis/whitening — default, or `'iir'` all-pole synthesis), `v` (ladder/feedforward, optional). `iir` inverts `fir` exactly: `lattice(lattice(x, {k}), {k, type: 'iir'}) === x`.\n\n```js\n// Reflection coefficients from levinson LPC analysis\nlet { k } = levinson(autocorrelation, 12)\nlet error = lattice(data, { k })     // analysis: whiten to prediction error\nlattice(error, { k, type: 'iir' })   // synthesis: rebuild the signal\n```\n\n**Use when**: LPC analysis/synthesis, speech coding, adaptive structures.<br>\n**Not for**: general filtering (use filter with SOS).<br>\n**MATLAB**: `latcfilt`.\n\n<img src=\"plot/lattice.svg\">\n\n### `warpedFir(data, params)`\n\nFrequency-warped FIR – replaces unit delays with allpass delays. Concentrates frequency resolution at low frequencies, matching how the ear perceives pitch. Params: `coefs`, `lambda` (warping factor).\n\n```js\n// lambda ≈ 0.7 for 44.1 kHz maps to Bark-like resolution\nwarpedFir(data, { coefs: new Float64Array([0.5, 0.3, 0.15, 0.05]), lambda: 0.7 })\n```\n\n**Use when**: perceptual audio coding, low-order EQ that sounds better than uniform-resolution FIR.<br>\n**Not for**: linear phase (warped FIR is not linear phase).\n\n<img src=\"plot/warped-fir.svg\">\n\n### `kaiserord(deltaF, attenuation)`\n\nEstimates how many FIR taps you need and what Kaiser window $\\beta$ to use, given your transition width and desired stopband rejection. Feed the result into `firwin`.\n\n```js\n// \"I need 60 dB rejection with 10% of Nyquist transition width\"\nlet { numtaps, beta } = kaiserord(0.1, 60)\n// numtaps ≈ 75, beta ≈ 5.65\nlet h = firwin(numtaps, 4000, 44100, { window: ['kaiser', beta] })\n```\n\n**Use when**: estimating FIR order before designing with firwin.<br>\n**scipy**: `scipy.signal.kaiserord`. **MATLAB**: `kaiserord`.\n\n### FIR comparison\n\n| Method | Optimality | Best for | Weakness |\n|---|---|---|---|\n| `firwin` | Good enough | Quick prototyping, 80% of tasks | Not optimal for tight specs |\n| `firls` | Least-squares | Smooth specs, audio | Wider transition than remez |\n| `remez` | Minimax | Sharpest transitions, tight specs | Convergence issues at high order |\n| `firwin2` | Frequency sampling | Arbitrary shapes, EQ curves | Not formally optimal |\n\n## Smooth\n\nSmoothing and denoising. All operate in-place: `fn(data, params) → data`.\n\n### `onePole(data, params)`\n\nExponential moving average – the simplest IIR smoother. One multiply, no overshoot. Params: `fc`, `fs`.\n\n$y[n] = (1-a)\\,x[n] + a\\,y[n-1]$, $a = e^{-2\\pi f_c/f_s}$, $H(z) = (1-a)/(1-az^{-1})$\n\n**–6 dB/oct · 1 multiply · IIR**\n\n```js\nonePole(data, { fc: 100, fs: 44100 })\n```\n\n**Use when**: smoothing control signals, sensor data, parameter changes.<br>\n**Not for**: sharp cutoff (use butterworth), preserving peaks (use savitzkyGolay).<br>\n**scipy**: `scipy.signal.lfilter([1-a], [1, -a])`. **MATLAB**: `filter(1-a, [1 -a], x)`.\n\n<img src=\"plot/one-pole.svg\">\n\n### `movingAverage(data, params)`\n\nBoxcar average of last N samples. Params: `memory`.\n\n$h[n] = 1/N$ for $n = 0..N-1$, $H(z) = (1 - z^{-N})/(N(1 - z^{-1}))$\n\n**Linear phase · FIR · nulls at multiples of fs/N**\n\n```js\nmovingAverage(data, { memory: 8 })\n```\n\n**Use when**: periodic noise removal, simple averaging.<br>\n**Not for**: preserving peaks (use savitzkyGolay).<br>\n**MATLAB**: `movmean`.\n\n<img src=\"plot/moving-average.svg\">\n\n### `leakyIntegrator(data, params)`\n\nExponential decay accumulator. Same as onePole but parameterized by decay factor. Params: `lambda` (0–1).\n\n$y[n] = \\lambda\\,y[n-1] + (1-\\lambda)\\,x[n]$\n\n**1 multiply · IIR**\n\n```js\nleakyIntegrator(data, { lambda: 0.95 })\n```\n\n**Use when**: running average, DC estimation.\n\n<img src=\"plot/leaky-integrator.svg\">\n\n### `savitzkyGolay(data, params)`\n\nPolynomial fit to sliding window – preserves peak height, width, and shape. Also computes smooth derivatives. Savitzky & Golay (1964).[^sg] Params: `windowSize`, `degree`, `derivative`.\n\n[^sg]: A. Savitzky, M.J.E. Golay, \"Smoothing and Differentiation of Data,\" *Analytical Chemistry*, 1964.\n\nFits polynomial of degree $d$ to window of $2m+1$ samples by least-squares.\n\n**FIR · linear phase · preserves moments up to degree**\n\n```js\nsavitzkyGolay(data, { windowSize: 11, degree: 3 })\n```\n\n**Use when**: spectroscopy, chromatography, peak-sensitive measurement.<br>\n**Not for**: frequency-selective filtering, causal/online processing.<br>\n**scipy**: `scipy.signal.savgol_filter`. **MATLAB**: `sgolayfilt`.\n\n<img src=\"plot/savitzky-golay.svg\">\n\n### `gaussianIir(data, params)`\n\nRecursive Gaussian (Young-van Vliet). O(1) cost regardless of sigma. Forward-backward for zero phase. Params: `sigma`.\n\nApproximates $h(t) = \\exp(-t^2/(2\\sigma^2))$ using 3rd-order recursive filter.\n\n**IIR · zero phase (offline) · O(1) per sample**\n\n```js\ngaussianIir(data, { sigma: 10 })\n```\n\n**Use when**: large-kernel Gaussian smoothing.<br>\n**Not for**: exact Gaussian (approximation), causal filtering.\n\n<img src=\"plot/gaussian-iir.svg\">\n\n### `dynamicSmoothing(data, params)`\n\nSelf-modulating lowpass – the filter's own band energy opens its cutoff, so it snaps to fast changes and settles glassy-smooth. Simper (2016).[^dynsmooth] Params: `fc` (base cutoff), `sensitivity`, `fs`.\n\n[^dynsmooth]: A. Simper, [\"Dynamic Smoothing Using Self Modulating Filter\"](https://cytomic.com/files/dsp/DynamicSmoothing.pdf), Cytomic technical paper, 2016.\n\n$g = \\min(g_0 + s \\cdot |low_1 - low_2|,\\ 1)$ — two cascaded one-pole stages sharing the modulated coefficient $g$\n\n**Adaptive · 2 cascaded one-pole stages**\n\n```js\ndynamicSmoothing(data, { fc: 1, sensitivity: 1, fs: 44100 })\n```\n\n**Use when**: audio parameter smoothing at audio rate.<br>\n**Not for**: non-audio rate (use oneEuro).\n\n<img src=\"plot/dynamic-smoothing.svg\">\n\n### `median(data, params)`\n\nNonlinear – replaces each sample with neighborhood median. Removes impulse noise while preserving edges. Params: `size`.\n\n**Nonlinear · preserves edges · O(N log N) per sample**\n\n```js\nmedian(data, { size: 5 })\n```\n\n**Use when**: clicks, pops, sensor outliers, edge-preserving denoising.<br>\n**Not for**: frequency-selective filtering (no defined frequency response).<br>\n**scipy**: `scipy.signal.medfilt`. **MATLAB**: `medfilt1`.\n\n<img src=\"plot/median.svg\">\n\n### `oneEuro(data, params)`\n\nAdaptive lowpass – cutoff increases with signal speed. Smooth at rest, responsive when moving. Casiez et al. (2012).[^euro] Params: `minCutoff`, `beta`, `dCutoff`, `fs`.\n\n[^euro]: G. Casiez et al., \"1€ Filter,\" *CHI*, 2012.\n\n$f_c(n) = f_{min} + \\beta \\cdot |\\dot{x}[n]|$\n\n**Adaptive · 1st-order IIR with time-varying cutoff**\n\n```js\noneEuro(data, { minCutoff: 1, beta: 0.007, fs: 60 })\n```\n\n**Use when**: mouse/touch/gaze input, sensor fusion, UI jitter.<br>\n**Not for**: audio-rate processing (use dynamicSmoothing).\n\n<img src=\"plot/one-euro.svg\">\n\n### Smooth comparison\n\n| Filter | Type | Phase | Edge preservation | Adaptive | Best for |\n|---|---|---|---|---|---|\n| `onePole` | IIR | Nonlinear | No | No | Simplest smoothing |\n| `movingAverage` | FIR | Linear | No | No | Periodic noise, simple |\n| `leakyIntegrator` | IIR | Nonlinear | No | No | Decay-factor control |\n| `median` | Nonlinear | — | Yes | No | Impulse noise, outliers |\n| `savitzkyGolay` | FIR | Linear | Yes (peaks) | No | Peak-preserving measurement |\n| `gaussianIir` | IIR | Zero (offline) | No | No | Large-kernel smoothing |\n| `oneEuro` | IIR | Nonlinear | No | Yes | UI jitter, sensor fusion |\n| `dynamicSmoothing` | IIR | Nonlinear | No | Yes | Audio parameter smoothing |\n\n## Adaptive\n\nFilters that learn from a reference signal. Returns filtered output; `params.error` contains error; `params.w` updated in place.\n\n### `lms(input, desired, params)`\n\nLeast Mean Squares – stochastic gradient descent. Widrow & Hoff (1960).[^lms] Params: `order`, `mu`.\n\n[^lms]: B. Widrow, M.E. Hoff, \"Adaptive Switching Circuits,\" *IRE WESCON*, 1960.\n\n$\\mathbf{w}[n+1] = \\mathbf{w}[n] + \\mu\\,e[n]\\,\\mathbf{x}[n]$, $e[n] = d[n] - \\mathbf{w}^T\\mathbf{x}[n]$. Convergence: $0 < \\mu < 2/(N\\sigma_x^2)$.\n\n**O(N)/sample · slow convergence · very robust**\n\n```js\nlms(input, desired, { order: 128, mu: 0.01 })\n```\n\n**Use when**: educational, simple implementation.<br>\n**Not for**: practice – almost always use nlms instead.<br>\n**MATLAB**: `dsp.LMSFilter`.\n\n<img src=\"plot/lms.svg\">\n\n### `nlms(input, desired, params)`\n\nNormalized LMS – step size adapts to input power. The practical default. Params: `order`, `mu` (0–2), `eps`.\n\n$\\mathbf{w}[n+1] = \\mathbf{w}[n] + \\mu\\,e[n]\\,\\mathbf{x}[n] / (\\mathbf{x}^T\\mathbf{x} + \\varepsilon)$. Convergence: $0 < \\mu < 2$ regardless of input level.\n\n**O(N)/sample · medium convergence · robust**\n\n```js\nnlms(farEnd, microphone, { order: 512, mu: 0.5 })\n// params.error = cleaned signal\n```\n\n**Use when**: echo cancellation, noise cancellation, system identification. Start here.<br>\n**Not for**: fastest convergence (use rls).<br>\n**MATLAB**: `dsp.LMSFilter` (normalized mode).\n\n<img src=\"plot/nlms.svg\">\n\n### `rls(input, desired, params)`\n\nRecursive Least Squares – fastest convergence (~2N samples) via inverse correlation matrix. Params: `order`, `lambda`, `delta`.\n\n$\\mathbf{w}[n] = \\mathbf{w}[n-1] + \\mathbf{k}[n] \\cdot e[n]$ where $\\mathbf{k} = P\\mathbf{x}/(\\lambda + \\mathbf{x}^T P\\mathbf{x})$\n\n**O(N²)/sample · fast convergence · fragile if lambda wrong · use for N ≤ 64**\n\n```js\nrls(input, desired, { order: 32, lambda: 0.99, delta: 100 })\n```\n\n**Use when**: fast-changing systems, short filters.<br>\n**Not for**: N > 128 (O(N²) too expensive), robustness matters (use nlms).<br>\n**MATLAB**: `dsp.RLSFilter`.\n\n<img src=\"plot/rls.svg\">\n\n### `levinson(R, order?)`\n\nLevinson-Durbin recursion – takes autocorrelation values, returns LPC prediction coefficients. The standard way to compute linear prediction for speech, spectral estimation, and lattice filter coefficients. Returns `{ a, error, k }` (prediction coefficients, prediction error power, reflection coefficients).\n\nSolves $R\\mathbf{a} = \\mathbf{r}$ recursively (Toeplitz structure gives O(N²) instead of O(N³)).\n\n**O(N²)/block · batch (not real-time)**\n\n```js\n// Compute autocorrelation of a speech frame, then solve for LPC coefficients\nlet R = new Float64Array(13)\nfor (let lag = 0; lag < 13; lag++)\n  for (let i = 0; i < frame.length - lag; i++)\n    R[lag] += frame[i] * frame[i + lag]\n\nlet { a, error, k } = levinson(R, 12)\n// a = prediction coefficients, k = reflection coefficients for lattice\n```\n\n**Use when**: LPC analysis, speech coding (CELP, LPC-10), AR spectral estimation.<br>\n**Not for**: real-time sample-by-sample adaptation (use nlms/rls).<br>\n**MATLAB**: `levinson`.\n\n<img src=\"plot/levinson.svg\">\n\n### Adaptive comparison\n\n| Algorithm | Complexity | Convergence | Tracking | Stability | Best for |\n|---|---|---|---|---|---|\n| `lms` | O(N)/sample | Slow | Slow | Very robust | Educational |\n| `nlms` | O(N)/sample | Medium | Medium | Robust | Real-world default |\n| `rls` | O(N²)/sample | Fast (~2N) | Fast | Fragile | Short filters, fast-changing |\n| `levinson` | O(N²)/block | Instant (batch) | N/A | Stable | LPC, speech coding |\n\n## Multirate\n\nChange sample rates without aliasing. Anti-alias before decimating, anti-image after interpolating.\n\n### `decimate(data, factor, opts?)`\n\nAnti-alias FIR lowpass + downsample by factor M. Returns shorter `Float64Array`.\n\n$y[m] = (h * x)[mM]$\n\n```js\nlet down = decimate(data, 4)\n```\n\n**Use when**: reducing sample rate, downsampling for analysis.<br>\n**scipy**: `scipy.signal.decimate`. **MATLAB**: `decimate`.\n\n<img src=\"plot/decimate.svg\">\n\n### `interpolate(data, factor, opts?)`\n\nUpsample by factor L + anti-image FIR lowpass. Returns longer `Float64Array`.\n\n$y[n] = (h * x_\\uparrow)[n]$\n\n```js\nlet up = interpolate(data, 4)\n```\n\n**Use when**: increasing sample rate, upsampling before nonlinear processing.<br>\n**scipy**: `scipy.signal.resample_poly`. **MATLAB**: `interp`.\n\n<img src=\"plot/interpolate.svg\">\n\n### `resample(data, p, q, opts?)`\n\nRational-rate resampling by p/q — upsample, anti-alias/anti-image FIR, downsample in one pass (via `upfirdn`), delay-compensated to sample-exact alignment.\n\n```js\nlet out = resample(data, 3, 2)   // 44.1k → 66.15k: 1.5× the rate\nlet cd = resample(data, 160, 147) // 44.1k → 48k\n```\n\n**Use when**: converting between arbitrary rational rates.<br>\n**scipy**: `scipy.signal.resample_poly`. **MATLAB**: `resample`.\n\n### `upfirdn(data, h, up, down)`\n\nUpsample → FIR → downsample, the general multirate primitive. No intermediate upsampled buffer.\n\n```js\nlet out = upfirdn(data, h, 3, 2)\n```\n\n**scipy**: `scipy.signal.upfirdn`.\n\n### `halfBand(numtaps?)`\n\nHalf-band FIR – nearly half the coefficients are zero, halving multiply count. The building block for efficient 2× rate changes.\n\n$h[n] = 0$ for even $n \\neq 0$, $h[0] = 0.5$\n\n```js\nlet h = halfBand(31)\n```\n\n**Use when**: efficient 2× decimation/interpolation. Cascade for 4×, 8×.<br>\n**Not for**: non-2x rate changes.\n\n<img src=\"plot/half-band.svg\">\n\n### `cic(data, R, N?)`\n\nCascaded Integrator-Comb – multiplier-free decimation. Only additions and subtractions.\n\n$H(z) = ((1 - z^{-RM})/(1 - z^{-1}))^N$\n\n**Multiplier-free · sinc-shaped passband droop**\n\n```js\nlet down = cic(data, 8, 3)\n```\n\n**Use when**: high decimation ratios (10×–1000×), hardware/FPGA, SDR.<br>\n**Not for**: precision (sinc-shaped passband droop needs compensation).<br>\n**MATLAB**: `dsp.CICDecimator`.\n\n<img src=\"plot/cic.svg\">\n\n### `polyphase(h, M)`\n\nDecompose FIR into M polyphase components. Compute only the output samples you keep. Returns `Array<Float64Array>`.\n\n$H(z) = \\sum_{k=0}^{M-1} z^{-k} E_k(z^M)$\n\n```js\nlet phases = polyphase(firCoefs, 4)\n```\n\n**Use when**: efficient multirate filtering.\n\n<img src=\"plot/polyphase.svg\">\n\n### `farrow(data, params)`\n\nFractional delay via polynomial interpolation. Delay can change every sample. Params: `delay`, `order`.\n\n$y(d) = \\sum_{k=0}^{N} c_k(n) \\cdot d^k$\n\n```js\nfarrow(data, { delay: 3.7, order: 3 })\n```\n\n**Use when**: pitch shifting, variable-rate resampling, time-stretching.<br>\n**Not for**: fixed delay (use thiran – allpass, preserves all frequencies).\n\n<img src=\"plot/farrow.svg\">\n\n### `thiran(delay, order?)`\n\nAllpass fractional delay – unity magnitude, maximally flat group delay. Returns `{ b, a }`.\n\n$a_k = (-1)^k \\binom{N}{k} \\prod_{n=0}^{N} (D-N+n)/(D-N+k+n)$\n\n```js\nlet { b, a } = thiran(3.7)\n```\n\n**Use when**: physical modeling synthesis (waveguide strings, tubes).<br>\n**Not for**: variable delay per sample (use farrow).\n\n<img src=\"plot/thiran.svg\">\n\n### `oversample(data, factor, opts?)`\n\nUpsampling with a Kaiser anti-image FIR designed from an attenuation spec — the filter length scales with the factor, so rejection holds at any factor. Oversample before nonlinear processing, then decimate back. Params: `attenuation?` (dB, default 70), `numtaps?` (override).\n\n```js\nlet up = oversample(data, 4)\nlet hq = oversample(data, 8, { attenuation: 90 })\n```\n\n**Use when**: oversampling before distortion/waveshaping/saturation.<br>\n**Not for**: integer rate changes (use decimate/interpolate).\n\n<img src=\"plot/oversample.svg\">\n\n\n## Core\n\nApply coefficients, analyze responses, convert formats.\n\n### `filter(data, params)`\n\nApplies SOS coefficients to data in-place. Direct Form II Transposed. State persists between calls. Params: `coefs`.\n\n$y[n] = b_0 x[n] + b_1 x[n-1] + b_2 x[n-2] - a_1 y[n-1] - a_2 y[n-2]$\n\n```js\nlet sos = butterworth(4, 1000, 44100)\nlet params = { coefs: sos }\nfilter(block1, params)   // state preserved\nfilter(block2, params)   // seamless\n```\n\n### `iir(data, params)`\n\nArbitrary-order IIR processing. Direct Form II Transposed with feedforward/feedback coefficient arrays (up to any order). Params: `b` (numerator), `a` (denominator), `state`.\n\n```js\niir(data, { b: [0.1, 0.2, 0.1], a: [1, -0.8, 0.2] })\n```\n\n**Use when**: need direct transfer function form (b/a arrays), higher than 2nd order without SOS, Web Audio IIRFilterNode compatibility.\n\n### `filtfilt(data, params)`\n\nZero-phase forward-backward filtering. Doubles effective order, eliminates phase distortion. Odd-reflection padding + steady-state initial conditions keep the edges transient-free (scipy `sosfiltfilt` semantics). Offline only. Params: `coefs`, `padlen?` (default `3·(2·sections+1)`, `0` disables).\n\n```js\nfiltfilt(data, { coefs: butterworth(4, 1000, 44100) })\n```\n\n### `detrend(data, type?)`\n\nRemove DC offset or linear trend from data in-place. Types: `'linear'` (default), `'constant'`/`'dc'`.\n\n```js\ndetrend(data)              // remove linear trend\ndetrend(data, 'constant')  // remove DC offset only\n```\n\n**scipy**: `scipy.signal.detrend`. **MATLAB**: `detrend`.\n\n### `convolution(signal, ir)`\n\nDirect convolution. Returns `Float64Array` of length N + M – 1.\n\n$(f * g)[n] = \\sum_k f[k]\\,g[n-k]$\n\n```js\nlet out = convolution(signal, firCoefs)\n```\n\n### `freqz(coefs, n?, fs?)` · `mag2db(mag)`\n\nFrequency response of an SOS filter or a `{b, a}` transfer function (the shape `yulewalk`/`thiran` return). Returns `{ frequencies, magnitude, phase }`. Second argument can be a number (evenly spaced points) or an array of Hz values.\n\n```js\nlet resp = freqz(sos, 512, 44100)                  // 512 evenly spaced points\nlet picked = freqz(sos, [100, 1000, 10000], 44100) // at specific frequencies\nlet tfResp = freqz(thiran(3.5), 512, 44100)        // {b, a} transfer function\nlet dB = mag2db(resp.magnitude)                    // 20·log10(mag)\n```\n\n### `groupDelay(coefs, n?, fs?)` · `phaseDelay(coefs, n?, fs?)`\n\n$\\tau_g = -d\\phi/d\\omega$ (group delay), $\\tau_p = -\\phi/\\omega$ (phase delay). Returns `{ frequencies, delay }`.\n\n```js\nlet { frequencies, delay } = groupDelay(sos, 512, 44100)\n```\n\n### `impulseResponse(coefs, N?)` · `stepResponse(coefs, N?)`\n\nTime-domain analysis. Returns `Float64Array`.\n\n```js\nlet ir = impulseResponse(sos, 256)\nlet step = stepResponse(sos, 256)\n```\n\n### `isStable(sos)` · `isMinPhase(sos)` · `isFir(sos)` · `isLinPhase(h)`\n\nFilter property tests.\n\n```js\nisStable(sos)     // all poles inside unit circle?\nisMinPhase(sos)   // all zeros inside unit circle?\nisFir(sos)        // a1=a2=0 (no feedback)?\nisLinPhase(h)     // symmetric/antisymmetric coefficients?\n```\n\n### `sos2zpk` · `sos2tf` · `tf2zpk` · `tf2sos` · `zpk2sos` · `zpk2tf`\n\nFormat conversion between SOS, transfer function (b/a), and zeros/poles/gain.\n\n```js\nlet { zeros, poles, gain } = sos2zpk(sos)\nlet { b, a } = sos2tf(sos)\nlet zpk = tf2zpk(b, a)\nlet sos2 = zpk2sos(zpk)\nlet sos3 = tf2sos(b, a)       // shortcut: tf → zpk → sos\nlet { b: b2, a: a2 } = zpk2tf(zpk)\n```\n\n### `tf2ss(b, a)` · `ss2tf(A, B, C, D)`\n\nTransfer function ↔ state-space (controllable canonical form).\n\n```js\nlet { A, B, C, D } = tf2ss([1, 0.5], [1, -0.8])\nlet { b, a } = ss2tf(A, B, C, D)\n```\n\n**scipy**: `scipy.signal.tf2ss` / `ss2tf`.\n\n### `residue(b, a)`\n\nPartial fraction expansion of $B(z)/A(z)$ over $z^{-1}$: $H(z) = \\sum_k r_k/(1 - p_k z^{-1}) + \\sum_m k_m z^{-m}$. Simple poles only — repeated poles throw.\n\n```js\nlet { r, p, k } = residue([1], [1, -1.5, 0.5])\n// r: residues, p: poles, k: direct FIR terms\n```\n\n**scipy**: `scipy.signal.residuez`. **MATLAB**: `residuez`.\n\n### `matchedZ(poles, zeros, fs, normFreq?)`\n\nMatched-z transform: map analog poles/zeros to digital via $z = e^{sT}$ — an alternative to the bilinear transform that preserves pole frequencies exactly (no warping) at the cost of aliasing the response.\n\n```js\nlet sos = matchedZ([{re: -1000, im: 8000}], [], 44100, 0)\n```\n\n**MATLAB**: `impinvar`-adjacent; classic matched-z from Rabiner & Gold.\n\n### `deconvolve(b, a)`\n\nPolynomial long division — inverse of `convolution`: recover a signal from its convolution with a known kernel.\n\n```js\nlet { q, r } = deconvolve(convolved, kernel)  // q: quotient, r: remainder\n```\n\n**scipy**: `scipy.signal.deconvolve`. **MATLAB**: `deconv`.\n\n### `wiener(data, opts?)`\n\nLocal adaptive Wiener denoising — attenuates toward the local mean where the local variance is near the noise floor.\n\n```js\nwiener(data, { size: 5 })          // noise power estimated from the signal\nwiener(data, { size: 5, noise: 0.01 })\n```\n\n**scipy**: `scipy.signal.wiener`. **MATLAB**: `wiener2` (1-D analog).\n\n### `sosfiltZi(sos)`\n\nCompute initial conditions for SOS filter to start in steady state (no transient when input starts at a non-zero value). Scale by the first sample: `zi.map(([a, b]) => [a * x0, b * x0])`.\n\n```js\nlet sos = butterworth(4, 1000, 44100)\nlet zi = sosfiltZi(sos)\nfilter(data, { coefs: sos, state: zi })  // no startup transient for unit-level input\n```\n\n### `transform`\n\nAnalog prototype → digital SOS pipeline. Used internally by IIR design functions.\n\n```js\ntransform.polesSos(poles, fc, fs, 'lowpass')\ntransform.poleZerosSos(poles, zeros, [fLow, fHigh], fs, 'bandpass')\ntransform.prewarp(fc, fs)  // bilinear frequency prewarping\n```\n\n\n## FAQ\n\n**What does the dB scale mean?** $\\text{dB} = 20\\log_{10}(\\text{ratio})$. 0 dB = unchanged, –3 dB = half power, –6 dB = half amplitude, –20 dB = 10%, –60 dB = 0.1%.\n\n**When does phase matter?** When waveform shape must be preserved: crossovers (drivers must sum correctly), biomedical (ECG/EEG morphology), communications (intersymbol interference). For EQ, phase is usually inaudible.\n\n**What is the bilinear transform?** Maps analog prototypes to digital: $s = (2/T)(z-1)/(z+1)$. All IIR design functions prewarp automatically – the cutoff you specify is the cutoff you get.\n\n**When can a filter become unstable?** When poles move outside the unit circle. Causes: coefficient quantization (use SOS, not direct form), Q approaching 0, feedback gain too high. Check with `isStable(sos)`. FIR is always stable.\n\n**What is aliasing?** Frequencies above $f_s/2$ (Nyquist) fold back as artifacts. `decimate` and `interpolate` handle anti-aliasing automatically.\n\n\n## Choosing a filter\n\n| I need to… | Use | Notes |\n|---|---|---|\n| Remove frequencies above/below a cutoff | `butterworth(N, fc, fs)` | Default, flat passband |\n| Sharpest possible cutoff | `elliptic(N, fc, fs, rp, rs)` | Minimum order for specs |\n| Sharp, passband ripple OK | `chebyshev(N, fc, fs, ripple)` | Steeper than Butterworth |\n| Sharp, no ripple anywhere | `legendre(N, fc, fs)` | Between Butterworth and Chebyshev |\n| Auto-select family + order | `iirdesign(fpass, fstop, rp, rs, fs)` | From specs |\n| Notch out one frequency | `biquad.notch(fc, Q, fs)` | Q=30 for narrow null |\n| Boost/cut a band | `biquad.peaking(fc, Q, fs, dB)` | Parametric EQ |\n| Split into bands | `linkwitzRiley(4, fc, fs)` | LP+HP sum to flat |\n| No ringing/overshoot | `bessel(N, fc, fs)` | Flat group delay |\n| No phase distortion | `filtfilt(data, {coefs})` | Zero-phase, offline |\n| Smooth a signal | `onePole(data, {fc, fs})` | Simplest |\n| Smooth, preserve peaks | `savitzkyGolay(data, {windowSize, degree})` | Polynomial fit |\n| Reduce sensor jitter | `oneEuro(data, params)` | Adaptive |\n| Cancel echo/noise | `nlms(input, desired, params)` | Start here |\n| Quick FIR | `firwin(N, fc, fs, {type})` | Window method |\n| Sharp FIR | `remez(N, bands, desired)` | Parks-McClellan |\n| Downsample | `decimate(data, factor)` | Anti-alias included |\n| Upsample | `interpolate(data, factor)` | Anti-image included |\n\n### IIR family decision tree\n\n```\nLinear phase needed?\n├── Yes → FIR or filtfilt (offline)\n└── No\n    Waveform must be preserved?\n    ├── Yes → bessel\n    └── No\n        Passband ripple OK?\n        ├── Yes\n        │   ├── Stopband ripple also OK? → elliptic\n        │   └── Stopband monotonic → chebyshev\n        └── No (passband must be flat)\n            ├── Stopband ripple OK? → chebyshev2\n            └── No ripple anywhere?\n                ├── Steepest monotonic? → legendre\n                └── Default → butterworth\n```\n\n\n## Recipes\n\n### Hum removal\n\n```js\nfor (let f of [60, 120, 180]) filter(data, { coefs: biquad.notch(f, 30, 44100) })\n```\n\n### Echo cancellation\n\n```js\nlet params = { order: 512, mu: 0.5 }\nnlms(farEnd, microphone, params)\n// params.error = cleaned signal\n```\n\n### ECG filtering\n\n```js\nfilter(data, { coefs: butterworth(2, 0.5, 500, 'highpass') })  // baseline wander\nfilter(data, { coefs: butterworth(4, 40, 500) })                 // noise\nfilter(data, { coefs: biquad.notch(50, 35, 500) })               // powerline\n```\n\n### Pulse shaping\n\n```js\nlet htx = raisedCosine(101, 0.35, 8, { root: true })\nlet shaped = convolution(symbols, htx)\n```\n\n### EEG band extraction\n\n```js\nlet fs = 256\nlet delta = butterworth(4, [0.5, 4], fs, 'bandpass')   // deep sleep\nlet theta = butterworth(4, [4, 8], fs, 'bandpass')      // drowsiness\nlet alpha = butterworth(4, [8, 13], fs, 'bandpass')     // relaxed, eyes closed\nlet beta  = butterworth(4, [13, 30], fs, 'bandpass')    // active thinking\n\nlet signal = Float64Array.from(data)\nfilter(signal, { coefs: alpha })\n// Band power:\nlet power = 0\nfor (let i = 0; i < signal.length; i++) power += signal[i] ** 2\npower /= signal.length\n```\n\n### LPC analysis\n\n```js\n// Autocorrelation → LPC coefficients → lattice synthesis\nlet order = 12, R = new Float64Array(order + 1)\nfor (let k = 0; k <= order; k++)\n  for (let i = 0; i < frame.length - k; i++)\n    R[k] += frame[i] * frame[i + k]\n\nlet { a, k } = levinson(R, order)\n// k = reflection coefficients, use with lattice() for synthesis\n// a = prediction coefficients, use with iir() for inverse filtering\n```\n\n### Karplus-Strong string\n\nUses `comb` from the companion [audio-filter](https://github.com/audiojs/audio-filter) package (not part of digital-filter):\n\n```js\nimport { comb } from 'audio-filter'          // external package\nimport { onePole } from 'digital-filter'\n\nlet delay = Math.round(44100 / 440)  // A4\nlet data = new Float64Array(44100)\nfor (let i = 0; i < delay; i++) data[i] = Math.random() * 2 - 1\ncomb(data, { delay, gain: 0.996, type: 'feedback' })\nonePole(data, { fc: 4000, fs: 44100 })\n// delay sets pitch, gain ≈ 1 = long sustain, lower fc = duller (nylon)\n```\n\n\n## Pitfalls\n\n- **FIR when IIR suffices** – Butterworth order 4: 10 multiplies. Equivalent FIR: 100+.\n- **High order when elliptic works** – elliptic 4 ≈ Butterworth 12. Use `iirdesign`.\n- **Forgetting SOS** – never use high-order direct form. This library returns SOS by default.\n- **filtfilt in real-time** – needs entire signal for backward pass.\n- **LP+HP for crossover** – doesn't sum flat. Use `linkwitzRiley`.\n- **Q too high** – Q > 10 creates tall resonance. For EQ: 0.5–8.\n- **In-place** – `filter()` modifies data. Copy first: `Float64Array.from(data)`.\n- **Stale state** – state persists in params. New signal → new params.\n\n\n## Plot generation\n\nGenerate 4-panel SVG plots (magnitude, phase, group delay, impulse response) for any filter. Used internally for this readme's illustrations; available for downstream packages like [audio-filter](https://github.com/audiojs/audio-filter).\n\n```js\nimport { plotFilter, plotFir, plotCompare, theme } from 'digital-filter/plot'\nimport { writeFileSync } from 'node:fs'\n\n// SOS filter → SVG string\nwriteFileSync('my-filter.svg', plotFilter(sos, 'Butterworth order 4, fc=1kHz'))\n\n// Impulse response → SVG string\nwriteFileSync('my-fir.svg', plotFir(h, 'firwin lowpass, 63 taps'))\n\n// Compare multiple filters overlaid\nwriteFileSync('comparison.svg', plotCompare([\n  ['Butterworth', butterworth(4, 1000, 44100)],\n  ['Chebyshev', chebyshev(4, 1000, 44100, 1)],\n], 'IIR comparison, fc=1kHz'))\n```\n\n**`plotFilter(sos, title?, opts?)`** – SOS (biquad cascade) → 4-panel SVG.\n**`plotFir(h, title?, opts?)`** – impulse response array → 4-panel SVG.\n**`plotCompare(filters, title?, opts?)`** – multiple SOS overlaid → 4-panel SVG. `filters`: array of `[name, sos]` or `[name, sos, color]`.\n\nOptions: `{ fs, bins, color, fill }`. Defaults from `theme`.\n\n**`theme`** – mutable object controlling defaults:\n\n```js\ntheme.colors = ['#4a90d9', '#e74c3c', '#2ecc71']  // per-panel or per-series\ntheme.fill = true      // fill under curves\ntheme.fs = 44100\ntheme.bins = 2048      // FFT bins\ntheme.grid = '#e5e7eb' // grid line color\ntheme.axis = '#d1d5db' // axis color\ntheme.text = '#6b7280' // label color\n```\n\nRegenerate all plots: `npm run plot`\n\n## References\n\n**Textbooks**\n- Oppenheim & Schafer, *Discrete-Time Signal Processing*, 3rd ed, 2009 – the canonical DSP textbook\n- J.O. Smith III, [*Introduction to Digital Filters*](https://ccrma.stanford.edu/~jos/filters/) – free, audio-focused\n- Zolzer, *DAFX: Digital Audio Effects*, 2nd ed, 2011 – audio effects\n- Haykin, *Adaptive Filter Theory*, 5th ed, 2014 – LMS, RLS, Kalman\n- Zavalishin, [*The Art of VA Filter Design*](https://www.native-instruments.com/fileadmin/ni_media/downloads/pdf/VAFilterDesign_2.1.2.pdf) – virtual analog, SVF, ZDF\n\n**Papers & standards**\n- [RBJ Audio EQ Cookbook](https://www.w3.org/TR/audio-eq-cookbook/) (W3C Note, 1998) – biquad coefficient formulas[^rbj]\n- Butterworth, \"On the Theory of Filter Amplifiers,\" 1930[^bw]\n- Thomson, \"Delay Networks Having Maximally Flat Frequency Characteristics,\" 1949[^thomson]\n- Papoulis, \"Optimum Filters with Monotonic Response,\" 1958[^papoulis]\n- Cauer, *Synthesis of Linear Communication Networks*, 1958[^cauer]\n- Parks & McClellan, \"Chebyshev Approximation for Nonrecursive Digital Filters,\" 1972[^pm]\n- Linkwitz, \"Active Crossover Networks for Noncoincident Drivers,\" 1976[^lr]\n- Widrow & Hoff, \"Adaptive Switching Circuits,\" 1960[^lms]\n- Savitzky & Golay, \"Smoothing and Differentiation of Data,\" 1964[^sg]\n- Casiez et al., \"1€ Filter,\" CHI, 2012[^euro]\n- Simper, \"Linear Trapezoidal Integrated SVF,\" Cytomic, 2011\n\n**Online**\n- [ccrma.stanford.edu/~jos](https://ccrma.stanford.edu/~jos/) – J.O. Smith's 4 DSP books (free)\n- [earlevel.com](https://www.earlevel.com/main/) – biquad tutorials (Nigel Redmon)\n- [musicdsp.org](https://www.musicdsp.org/) – audio DSP snippet archive\n- [dsprelated.com](https://www.dsprelated.com/) – DSP community and free books\n\n## See also\n\n- **[audio-filter](https://github.com/audiojs/audio-filter)** – weighting, EQ, synthesis, measurement, effects\n- **[window-function](https://github.com/scijs/window-function)** – collection of window functions\n","readmeFilename":"readme.md"}