New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
python version #4
Conversation
Wow! It's so impressive, thank you! 🙏🙏🙏 |
Tested on fresh Ubuntu 18.04 install: I continue to review... |
Example of usage: I continue to review... |
@yoyololicon, the result is impressive! You've done so much. You are the man! I merged it, of course. What do you recommend for sound limiting? Original Matchering used Voxengo Elephant with default preset for it. We need such transparent brickwall limiter here. I messaged Elephant's author about our work, but he doesn't want to participiate in open source stuff. What i would like to do by myself next: If I comment out this line |
@yoyololicon About (1): Done. |
Thanks man!
In my opinion, let user decided to use their favorite limiter plugin would be the best solution, but I agree a limiter is definitely needed, to make the final result more complete.
I forgot to take the power of magnitude, so dumb Orz
Not know mush about web programming but sounds intereseting.
I add this line is because soundfile would clip the value outside [-1, 1], and it sounds horrible when I test on my mixes. So I normalize it before write into file, and display the compensation value to user. |
we can add this command in readme quick start section. |
Done
Yes, I would be happy to do that, hopefully this month
Yes, i agree too. We could give the user the choice:
|
Sounds like a good idea. Although it might need some times to study, I can take the limiter part. |
https://stackoverflow.com/questions/34833846/how-to-amplify-sounds-without-distortion-in-python I could check it out. Looks interesting. |
Yes |
Or not. :)
|
@yoyololicon @sergree Wow, Guys! You've done a great job! That's awesome news we have python version now! The only thing that kept me out of evaluation of original algorithm was MatLab. Now we can bring this up even as a docker service! |
Moved this to: |
@sergree
In this way, the runtime should be a lot faster. |
Thank you, @yoyololicon, your idea is great. I had some similar thoughts about offline Peak Limiter these days:
Next we can do smth with (4) to make it more smooth, to prevent hard clipping, but so that its values DO NOT decrease. For e.g.: ??? 5) ??? Convolve (4) with some sampled Attack-Release curve ??? like this: https://pasteboard.co/IKJlchC.png
I'M REALLY NOT SURE ABOUT (5)! Because, I think, the effect will be too strong, depending on the more overloaded samples will be near. I hope my ideas will be useful to you too. P.S. I will provide some industry standard attack / release values (in ms or N of samples) this week for the Peak Limiter. Need to research it. |
Tested on pure square signal Attack: 1,24 ms - 1,36 ms or 55-60 samples at 44100 Release: 3 sec - 6 sec |
Was working around the max filter and IIR filter idea these days, and here's the result, simulate on a 200 hz sin wave: I did some sound tests and it sounds similiar to regular peak limiter To recreate the result, here's the code: def offline_limiter(y, sr, attack=5, release=100, hold_time=8, threshold=-6, ceil=-0.1):
M = int(sr * attack * 1e-3)
K = int(sr * hold_time * 1e-3)
if not M & 1:
M += 1
at = math.exp(-2.2 / (attack * 0.001 * sr))
rt = math.exp(-2.2 / (release * 0.001 * sr))
thresh = 10 ** (threshold / 20)
ceil = 10 ** (ceil / 20)
vol = ceil / thresh
output_len = y.shape[0]
rect_y = np.abs(y).max(1)
# hold maximum value to make sure envelope can reach its maximum in attack stage
unfold_rect_y = np.pad(rect_y, (2 * M - 1, 0), 'constant', constant_values=0)
unfold_rect_y = np.lib.stride_tricks.as_strided(unfold_rect_y, (output_len, 2 * M), unfold_rect_y.strides * 2)
raw_env = unfold_rect_y.max(1)
# simulate attack curve using forward and backward IIR
env_a = signal.filtfilt([1 - at], [1, -at], raw_env)
# hold maximum peak longer to avoid ripple effect
unfold_env_a = np.pad(env_a, (K - 1, 0), 'constant', constant_values=0)
unfold_env_a = np.lib.stride_tricks.as_strided(unfold_env_a, (output_len, K), unfold_env_a.strides * 2)
env_a = unfold_env_a.max(1)
# add release decay curve to the envelope
env_r = signal.lfilter([1 - rt], [1., -rt], env_a)
final_env = np.maximum(env_a, env_r)
gain = np.minimum(1, thresh / final_env)
output = y * gain[:, None] * vol
return output |
Hello, @yoyololicon! I tested your script, and found some issues. My test file - Pure square signal at -3db, +3db, -6db, +6db, -3db The result i got after your 1st issue: We lost all information about the dynamics of the signal (no -3db or -6db values, just ~-0.001). We don't need leveling stuff here, because the leveling is done very accurate in other Matchering stages. Usual mastering peak limiters don't touch these parts (with default preset). Maybe i need to change 2nd issue: We got peaks in the limited output, as you see. Usual digital realtime limiters use look-ahead to prevent them, as i know. But it already looks impressive, thanks! 🙏 |
Updated code base on above issues: def offline_limiter(y, sr, attack=5, release=100, hold_time=8):
M = int(sr * attack * 1e-3)
K = int(sr * hold_time * 1e-3)
if not M & 1:
M += 1
at = math.exp(-2.2 / (attack * 0.001 * sr))
rt = math.exp(-2.2 / (release * 0.001 * sr))
thresh = 10 ** (-0.1/ 20) # fix threshold to -0.1 db
output_len = y.shape[0]
rect_y = np.abs(y).max(1)
# hold maximum value to make sure envelope can reach its maximum in attack stage
# the moving window is an attack time forwarded to simulate look ahead behavior
unfold_rect_y = np.pad(rect_y, (M - 1, M - 1), 'constant', constant_values=0)
unfold_rect_y = np.lib.stride_tricks.as_strided(unfold_rect_y, (output_len, 2 * M - 1), unfold_rect_y.strides * 2)
raw_env = unfold_rect_y.max(1)
# simulate attack curve using forward and backward IIR
env_a = signal.filtfilt([1 - at], [1, -at], raw_env)
# hold maximum peak longer to avoid ripple effect
unfold_env_a = np.pad(env_a, (K - 1, 0), 'constant', constant_values=0)
unfold_env_a = np.lib.stride_tricks.as_strided(unfold_env_a, (output_len, K), unfold_env_a.strides * 2)
env_a = unfold_env_a.max(1)
# add release decay curve to the envelope
env_r = signal.lfilter([1 - rt], [1., -rt], env_a)
final_env = np.maximum(env_a, env_r)
gain = np.minimum(1, thresh / final_env)
output = y * gain[:, None]
return output This will retain the original volume and dynamics below -0.1 db, and should have no significant peaks. |
Sure, thanks, @yoyololicon. The leveling issue is fixed, but I still get peaks. State-of-the-art Peak Limiter (Voxengo Elephant): |
@yoyololicon I made some draft adjustments to params.
Attack is good now. But 2 problems exist:
|
Looks like Voxengo's attack time is around 1 ms (mine is 5 ms). We can change that. |
Thank you again. I already edited the attack time and got good result. I hope I will have time the coming months to dive into this offline limiter topic and finish it on the part of peaks and release curve. |
I just wanted to let you know that my idea with novel max-convolution worked. I use some your limiter ideas too. The result limiter will be very accurate, I hope. I need some time to tune the parameters and add features such as a true peak limiting and a transient detection. After I finish everything in the anaconda, I will create a separate branch here and start refactoring and developing the single page web application for it. Just wanted to ask you for your donation link like 🤝 🙏 🥳 |
That's awesome! I'm looking forward to it. |
@yoyololicon Hello! My idea with maximum convolution failed. The calculation of max convolution took too long. But I have greatly modified your limiter and added it to the chain. It works like Voxengo Elephant now, but it is a little more aggressive. Perfect for EDM stuff. https://github.com/sergree/Matchering/blob/master/python/limiter.py Next: refactoring + web app from me. |
@sergree wow you really did a great job! The result looks very promising. |
Thanks!
UPD 2: Nvm :) I found the |
Dear @yoyololicon, thank you for resurrecting this project. I did a complete refactoring, trying to follow the DRY, KISS, and SOLID principles as best I can. I also fixed a few inaccuracies in your version, brought back the lowess algorithm (I checked for a long time, it worked best), added MP3 support via FFmpeg. I also prepared a convenient API and built a package for PyPI. Now it can be installed via pip. https://pypi.org/project/matchering/ I also made a handy command-line application for working with the Matchering library that supports writing logs to a file. https://github.com/sergree/matchering-cli I'll take a break for a few days, and then start implementing a containerized web application for Matchering. 🥳 🎉 🙏 With greetings from Russia! 🐻🍾🌲 |
@sergree Man that's A LOT of hard work! Because the next goal is to make it as a web service, which is not what I am familiar with, I'll leave it to you and check if there's any improvement or issue I can make that is related to audio processing / music information retrieval. In summary, I want to thank you for your active support and quick response on this thread. |
We did The Thing. https://www.youtube.com/watch?v=8Su5STDYfcA 🙏 |
Hey, my friend @yoyololicon I would like to share with you the WhatBPM, this is my new project, the ideological continuation of Matchering, only from a different angle. It allows you to adopt the best practices of reference tracks even before you start writing music. 😱 Simply put, it is a web service that automatically analyzes EDM trends on a daily basis and outputs recommended values for use in music: BPM, key, root note, track duration, and so on. I hope you find this useful. Sincerely from your Russian colleague 💓 |
Create a python re-write of matchering, because matlab is not that easy to acquire for normal user.
Differences
Have run this script on my laptop very smoothly, but I can't compare the differences cuz I currently don't have matlab installed device (the last time I use matlab was in college lol).