-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Added ability to invert the phase of only one channel #183
Conversation
This is cool. I'd suggest recombining the channels with Also, if possible, could you add a few lines to the unit test for invert_phase that exercise new API you've added? Finally, I'm not sure, but I think the existing code works on audio streams with more than 2 channels - it's good to support arbitrary channel counts where possible so that we can eventually have complete support for surround sound :) |
How would you suggest implementing support for an arbitrary number of channels- **kwargs? |
Fixed the unit test. I haven't been able to test the unit test in line with the rest of them since avconv throws an error when I run the testing code. So I recommend you run it yourself before pushing. Also, another question, what is rms? |
RMS is a method of measuring signal strength (stands for root mean squared), though I'd recommend using dBFS since it better matches how humans perceive loudness. regarding multichannel, could be something like this (based on your code): def invert_phase(seg, channels=None):
if channels is None:
inverted = audioop.mul(seg._data, seg.sample_width, -1.0)
return seg._spawn(data=inverted)
if seg.channels != len(channels):
raise ValueError("the \"channels\" kwarg must contain one value for each channel in the AudioSegment.")
mono_segs = seg.split_to_mono()
for mono_seg, phase_change in zip(mono_segs, channels):
if phase_change == 1:
mono_seg = mono_seg.invert_phase()
mono_segs.append(mono_seg)
return seg.from_mono_audiosegments(left, right) |
Working on it 😺 def invert_phase(seg, channels=None):
"""
channels- specifies which channel (left or right) to reverse the phase of.
Note that mono AudioSegments will become stereo.
"""
if channels == None:
inverted = audioop.mul(seg._data, seg.sample_width, -1.0)
return seg._spawn(data=inverted)
else:
if seg.channels == len(channels):
mono_segs = seg.split_to_mono()
for i in range(len(channels)):
if channels[i] == 1:
mono_segs[i] == mono_segs[i].invert_phase()
elif seg.channels == 1 and len(channels) > seg.channels:
mono_segs = []
for i in range(len(channels)):
mono_segs.append(seg)
if channels[i] == 1:
mono_segs[i] == mono_segs[i].invert_phase()
else:
raise ValueError("the \"channels\" kwarg must contain one value for each channel in the AudioSegment.")
return seg.from_mono_audiosegments(*mono_segs)` edit: formatting is screwy and I'm not sure why :| |
In that function, the first section (which deals with |
I've thought about this some and tested your hypothesis and came to the conclusion that there are a couple of different cases where you might want to be using a mono audio segment, and they both have to be handled differently: Case 1, when you have a mono AudioSegment and want it to become an AudioSegment with multiple channels. A similar example in Pydub is that if you have a mono recording and want to pan it, the recording is going to go from mono to stereo. Case 2, when you have a mono AudioSegment and you want it to stay mono. By default, Case 2 should happen, because if you have a mono AudioSegment and don't specify that you want it to become stereo (or multi channel) then it definitely should not. But if a mono audio segment is passed and a number of channels is specified, then it should follow the pattern of other functions in pydub and convert it from mono to stereo. So, three different cases need to be handled: -Mono to mono (1 channel > 1 channel) The only case that shouldn't work is (n channels -> x channels) where x != n. The other way to deal with this is to make it so that mono NEVER converts to stereo automatically and requiring an explicit statement (e.g. AudioSegment.mono_to_stereo(channels)) but it would be annoying to set up because it would break programs that already use Pydub. However it might in the long run make it so that blocks of logic like this aren't as necessary. |
Seems to me that pan is an operation that specifically deals with the stereo aspect of a signal, where phase inversion does not. So pan doesn't really have any meaning except in the context of a stereo output. Phase inversion does though, and probably (I think) shouldn't change the number of channels. I don't think it's too annoying to write: stereo_sound = mono_sound.set_channels(2).invert_phase([1, 0]) |
Yeah I reckon you're right. I'm not sure whether that's a problem for a separate issue or not, since if there's one instance of assuming either stereo or mono, it's probably present throughout audioop and may warrant its own project to change it. |
@cruxicheiros you can use |
No, you can't.
|
ah, right you are. This is still outstanding: #165 edit: btw thanks for all your work on this ticket :) |
And I'd like to thank you for being awesome to work with on it. I reckon the next steps should be to:
I'd be happy to contribute to that as well since this is a really useful repository and I'm currently using it quite a lot. 😸 |
works for me - my only concern is that invert_phase shouldn't implicitly change the number of channels. I can make that change during the merge unless you disagree (or prefer to do it) |
This should do it. edit: Ah pants, looks like I messed something up while 'fixing' it :| Gonna go find out why that build failed and sort it out |
Okay, so this should be that problem solved, for real. |
What it says on the tin.
Since reversing the phase of only one channel requires use of audioop, I figured it would be better to extend the invert_phase function within pydub rather than add that functionality in an external program.
This shouldn't break any existing programs using Pydub since it will behave in exactly the same way as it did before if no arguments are passed.