Skip to content
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

[core] audio speed change does not affect pitch #244

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

rucc
Copy link
Contributor

@rucc rucc commented Jan 20, 2020

When a file is being played with the playback application and the speed is changed with eg. uuid_fileman speed:+1 the audio pitch increases. This PR tries to preserve the pitch even when the speed is changed.

How?
Use longer sample chunks (where original freq is preserved)
Search for the best cut position using cross-correlation
Crossfade adjacent chunks to each other

This is basically a copy of my unreviewed PR from the old system

@mjerris
Copy link
Collaborator

mjerris commented Jan 21, 2020

src/switch_ivr_play_say.c: In function 'switch_ivr_play_file':
src/switch_ivr_play_say.c:1592:3: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
const int sp_fadeLen = 32;
^~~~~
src/switch_ivr_play_say.c:1798:5: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
int src_rng = (fh->speed > 0 ? supplement : sp_cut_src_rng)* sp_has_overlap;
^~~
src/switch_ivr_play_say.c:1883:30: error: 'byte' undeclared (first use in this function)
short* ret = (short*)((byte*)switch_buffer_get_head_pointer(fh->sp_audio_buffer) + switch_buffer_inuse(fh->sp_audio_buffer) - 2 * newlen);
^~~~
src/switch_ivr_play_say.c:1883:30: note: each undeclared identifier is reported only once for each function it appears in
src/switch_ivr_play_say.c:1883:35: error: expected expression before ')' token
short* ret = (short*)((byte*)switch_buffer_get_head_pointer(fh->sp_audio_buffer) + switch_buffer_inuse(fh->sp_audio_buffer) - 2 * newlen);
^
cc1: all warnings being treated as errors

Copy link
Collaborator

@mjerris mjerris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you update patch to match coding guidelines, particularly i notice white space around if/else

@rucc
Copy link
Contributor Author

rucc commented Jan 24, 2020

Uhh, I have completely missed that. Corrected.

@rucc rucc requested a review from mjerris January 31, 2020 10:08
@dragos-oancea
Copy link
Contributor

wondering if this has any effect upon your patch: https://github.com/signalwire/freeswitch/pull/342/files

@rucc
Copy link
Contributor Author

rucc commented Feb 18, 2020

wondering if this has any effect upon your patch: https://github.com/signalwire/freeswitch/pull/342/files

I think not. I have just merged that into this PR.

@toraritte
Copy link

Would it be possible to have this reviewed? Not the most crucial of features, but other alternatives are slow and may not even work real-time (e.g., ffmpeg, sox, audacity), or broken (e.g., mod_soundtouch, see #649). Thanks!

@toraritte
Copy link

toraritte commented Oct 19, 2020

Is there a technical reason why this hasn't been merged? I know that it is being used in production in a patched fork of FreeSWITCH but it would be nice to have this in the main one. Also, the requested change has been done months ago. Thank you!

@toraritte
Copy link

Thread on the freeswitch-users mailing list:
https://lists.freeswitch.org/pipermail/freeswitch-users/2020-October/134151.html

@dragos-oancea
Copy link
Contributor

can you rebase and squash it, so its just one commit ?

@rucc
Copy link
Contributor Author

rucc commented Jan 3, 2021

Hi @dragos-oancea ! Sorry for the delay, I moved away from the Telecom industry, but I am keen on working on this PR as I understand how important it is for @toraritte's use case. Unfortunately, I am not that experienced with git. I have read about the squash / rebase workflow in general (for example here, but what happens with the merge commits? When I hit git rebase -i 3aa77d646f9dced785cfdda35c90827a603f385c it is full of commits from the merges. How do I handle this? Just ignore it and pick everything? Or something more fancy?
Question 2: Should I merge the latest master?

@toraritte toraritte force-pushed the feature_keep_pitch_when_playback_speed_is_changed branch from 9ae7adf to 3f2f1e6 Compare February 2, 2021 16:25
@toraritte
Copy link

Hi @dragos-oancea , squashed and rebased. If merged, #649 may also be closed. Thanks!

@signalwire-ci
Copy link

signalwire-ci bot commented Feb 2, 2021

@dragos-oancea
Copy link
Contributor

"Dead store Dead assignment switch_ivr_play_say.c switch_ivr_play_file 1841 1 View Report" . @toraritte

@crienzo
Copy link
Member

crienzo commented Feb 5, 2021

This will require a test before we merge it- to exercise the code so we can check for runtime memory problems.

@crienzo
Copy link
Member

crienzo commented Feb 5, 2021

@dragos-oancea any interest in giving this a try- just a simple test in FS core to execute the function so ASAN can check it. We can listen to the recording generated by the test to spot check the result.

@dragos-oancea
Copy link
Contributor

it's leaking in 2 places, lines 1800 and 1803 . https://github.com/signalwire/freeswitch/pull/244/files#diff-2c79cd0fa8304ec5c3a6352dfe031b430e19db9b90e82d24bef823944fd0911eR1803

==7844==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 30720 byte(s) in 1 object(s) allocated from:
    #0 0x7f0ba63a1ed0 in calloc (/usr/lib/x86_64-linux-gnu/libasan.so.3+0xc1ed0)
    #1 0x7f0ba5453943 in switch_ivr_play_file src/switch_ivr_play_say.c:1803
    #2 0x7f0b936a0e3b in playback_function /root/github/freeswitch/src/mod/applications/mod_dptools/mod_dptools.c:3065
    #3 0x7f0ba52e91b4 in switch_core_session_exec src/switch_core_session.c:2891
    #4 0x7f0ba52e9cd6 in switch_core_session_execute_application_get_flags src/switch_core_session.c:2756
    #5 0x7f0ba52f1f29 in switch_core_standard_on_execute src/switch_core_state_machine.c:354
    #6 0x7f0ba52f1f29 in switch_core_session_run src/switch_core_state_machine.c:651
    #7 0x7f0ba52e4aec in switch_core_session_thread src/switch_core_session.c:1709
    #8 0x7f0ba52da948 in switch_core_session_thread_pool_worker src/switch_core_session.c:1772
    #9 0x7f0ba59673aa in dummy_worker threadproc/unix/thread.c:151
    #10 0x7f0ba46f14a3 in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x74a3)

Direct leak of 1984 byte(s) in 1 object(s) allocated from:
    #0 0x7f0ba63a1ed0 in calloc (/usr/lib/x86_64-linux-gnu/libasan.so.3+0xc1ed0)
    #1 0x7f0ba5451036 in switch_ivr_play_file src/switch_ivr_play_say.c:1800
    #2 0x7f0b936a0e3b in playback_function /root/github/freeswitch/src/mod/applications/mod_dptools/mod_dptools.c:3065
    #3 0x7f0ba52e91b4 in switch_core_session_exec src/switch_core_session.c:2891
    #4 0x7f0ba52e9cd6 in switch_core_session_execute_application_get_flags src/switch_core_session.c:2756
    #5 0x7f0ba52f1f29 in switch_core_standard_on_execute src/switch_core_state_machine.c:354
    #6 0x7f0ba52f1f29 in switch_core_session_run src/switch_core_state_machine.c:651
    #7 0x7f0ba52e4aec in switch_core_session_thread src/switch_core_session.c:1709
    #8 0x7f0ba52da948 in switch_core_session_thread_pool_worker src/switch_core_session.c:1772
    #9 0x7f0ba59673aa in dummy_worker threadproc/unix/thread.c:151
    #10 0x7f0ba46f14a3 in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x74a3)

But indeed, the pitch during speed-up seems preserved by my ears (tried both the original and with this branch).

sp_prev_idx += olen;

if (cont) {
continue;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

switch_safe_free(data);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, thanks!

int extra = datalen - olen;
short* data = NULL;
short* currp = NULL;
switch_zmalloc(data, datalen * 2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

malloc on every frame seems wasteful

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure how to improve this as the size is not constant. What do you suggest?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

heap buf max sized with realloc or a static buf sized to SWITCH_RECOMMENDED_BUFFER_SIZE which is designed to fit sane data frames.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I have switched to using a static buffer

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rucc looks like you missed on removing the switch_zmalloc and the data variable, it is not needed anymore.

Copy link
Contributor

@yois615 yois615 Jul 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ar45 Thanks for your suggestions... data is still needed as a buffer but zmalloc is not needed. I made some changes here
yois615@96ad560

I will open a PR against @rucc branch

short sp_has_overlap = 0;
int sp_prev_idx = 0;
int sp_prev_cap = 0;
short* sp_prev = NULL;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to make sure switch_safe_free(sp_prev) is done every time this loops (continue, break, end of statement)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be enough to free only after the 'inner' cycle ends (there are no jumps from the inner cycle)? (as I did it in my latest rev)

@snaptel2
Copy link

Offering a bounty of $150 to fix this patch and make it ready to merge.

@signalwire signalwire deleted a comment from alaghusun60 Mar 18, 2021
@rucc rucc force-pushed the feature_keep_pitch_when_playback_speed_is_changed branch from d2e5ca5 to 1c9e61c Compare April 9, 2021 12:30
@rucc
Copy link
Contributor Author

rucc commented Apr 9, 2021

@dragos-oancea That leak check is awesome! I am impressed. I hope there are no more left after my last update.
@crienzo I have tried my best to address the points you raised in your review. Please review them, and advise me if further improvements are required.
@snaptel Cool! That bounty offer was the last drop of motivation for me to find some free time to spend on this.

@snaptel2
Copy link

snaptel2 commented Apr 9, 2021

@rucc as soon as it gets approved I will reach out to you about the bounty payment, I really hope this gets in soon as the difference it makes to playback with speed change is just impressive.

@rucc rucc requested a review from crienzo April 11, 2021 17:39
@toraritte
Copy link

If time permits, would you please review this? Thank you.

@@ -1929,6 +1986,10 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess
break;
}
}
if (sp_prev) {
switch_safe_free(sp_prev);
sp_prev = NULL;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't have to do this, switch_safe_free(sp_prev) would set sp_prev to NULL after it frees it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, this was redundant, a simple switch_safe_free(sp_prev); is enough. This is no longer an issue since the sp_prev was moved to static storage.

@signalwire-ci
Copy link

signalwire-ci bot commented May 25, 2021

int extra = datalen - olen;
short* data = NULL;
short* currp = NULL;
switch_zmalloc(data, datalen * 2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rucc looks like you missed on removing the switch_zmalloc and the data variable, it is not needed anymore.



if (cont) {
switch_safe_free(data);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove line switch_safe_free(data);

last_speed = fh->speed;
switch_safe_free(data);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove switch_safe_free(data); line

short sp_ovrlap[32];
short sp_has_overlap = 0;
int sp_prev_idx = 0;
short sp_prev[SWITCH_RECOMMENDED_BUFFER_SIZE];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe initialize to 0?
short sp_prev[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 };

bp++;
if (extra > 0) {
int cont = 0;
currp = data;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, something is off here too

@dragos-oancea
Copy link
Contributor

Any chance to get this merged? this is almost 3 years ( https://freeswitch.org/jira/browse/FS-11734 ), so sad that such a nice improvement doesn't make it into tree

we'd like to get this merged, but the patch is very intrusive - it would be much easier to get it merged if most of this code would be enabled through some sort of flag.

@dragos-oancea
Copy link
Contributor

a channel flag perhaps.

@toraritte
Copy link

it would be much easier to get it merged if most of this code would be enabled through some sort of flag.

It would probably be, but never heard of a bugfix being made optional by a flag. Or do you mean like a flag for an experimental feature, because one does not know what this change may cause in the long term? This I understand, but it would have to be an interim solution nonetheless. Is my thinking flawed? That is, if a PR introduces regressions or causes behaviour in a FreeSWITCH instance then such a PR would never be accepted, flag or no flag.

Please throw some crumbs as to how this change is so intrusive; no long winded report need, links to affected source files, module, anthying would suffice to even start understand the scale of disruption this PR would cause.

yois615 added a commit to yois615/freeswitch that referenced this pull request Jul 27, 2022
yois615 added a commit to yois615/freeswitch that referenced this pull request Jul 28, 2022
yois615 added a commit to yois615/freeswitch that referenced this pull request Jul 29, 2022
yois615 added a commit to yois615/freeswitch that referenced this pull request Jul 31, 2022
yois615 added a commit to yois615/freeswitch that referenced this pull request Aug 2, 2022
Relating to signalwire#244, this commit makes better use of stack and heap
memory allocations
yois615 added a commit to yois615/freeswitch that referenced this pull request Aug 3, 2022
Relating to signalwire#244, this commit makes better use of stack and heap
memory allocations
yois615 added a commit to rucc/freeswitch that referenced this pull request Aug 3, 2022
Relating to signalwire#244, this commit makes better use of stack and heap
memory allocations
@yois615
Copy link
Contributor

yois615 commented Aug 4, 2022

@dragos-oancea I've pushed new changes regarding memory allocation. Can you have CI run the unit tests here? It seems like it's blocked. And can you and/or @crienzo review?

@yois615 yois615 force-pushed the feature_keep_pitch_when_playback_speed_is_changed branch from bf4f13a to 3d09804 Compare August 4, 2022 19:43
yois615 added a commit to rucc/freeswitch that referenced this pull request Aug 4, 2022
Relating to signalwire#244, this commit makes better use of stack and heap
memory allocations
@yois615 yois615 force-pushed the feature_keep_pitch_when_playback_speed_is_changed branch from 3d09804 to 1d65f6e Compare August 5, 2022 01:23
@signalwire-ci
Copy link

signalwire-ci bot commented Aug 5, 2022

@yois615 yois615 force-pushed the feature_keep_pitch_when_playback_speed_is_changed branch from 1d65f6e to ae2e4e8 Compare August 5, 2022 03:13
@anthmFS
Copy link
Contributor

anthmFS commented Aug 18, 2022

We can merge this as long as we are now sure that it's stable and does no harm when not enabled. Since the code path is limited to people who purposely turn it on, it's probably safe, and they will tell us if it's not working.

When we merge, fix the last few places where there are missing spaces and usage of "short" (ambiguous type)

short* foo
vs
int16_t *foo

@yois615 yois615 force-pushed the feature_keep_pitch_when_playback_speed_is_changed branch from ae2e4e8 to af5b28e Compare August 18, 2022 15:09
@yois615
Copy link
Contributor

yois615 commented Aug 18, 2022

@anthmFS - Thanks.

I ran this through ASAN and it comes back clean.

Correct, this code branch only runs if (fh->speed !=0)

short has been replaced with int16_t and int where appropriate.

A redundant if statement checking (fh->speed) again has been removed.

Whitespace cleaned up.

Note that I only made these changes today. They are tested and work but this doesn't have the benefit of running in my prod environment for the last few weeks.

@signalwire-ci
Copy link

signalwire-ci bot commented Aug 18, 2022

@yois615 yois615 force-pushed the feature_keep_pitch_when_playback_speed_is_changed branch from af5b28e to f3d032d Compare November 21, 2022 00:11
@toraritte
Copy link

Would you please merge this or raise actionable objections?

@yois615
Copy link
Contributor

yois615 commented Jul 28, 2024

@toraritte I mentioned this PR at an Office Hours about a year ago, and @andywolk was concerned about some malloc's that aren't explicitly freed.

I mentioned that ASAN doesn't show any issue, and I've had this running in prod for 2 years with no sign of memory leak. I have little interest in recoding/recompiling at this point but if someone wants to 'fix' the issue with unfreed memory it should allow this to be merged.

@toraritte
Copy link

toraritte commented Jul 28, 2024

@yois615 Thank you both for working on this and for providing context! I actually meant to ping anyone from the FreeSWITCH team, sorry for the confusion. I know that you and @rucc have been trying to get this merged for years. Thanks again!

@yois615
Copy link
Contributor

yois615 commented Jan 8, 2025

The code as it is now segfaults with heap buffer overflow at line 1949 from the buffer created in 1888.

This took me a lot of effort to reproduce, this condition is not hit normally, but if I just keep returning random speed, volume, and seek values it eventually will hit on a forward seek, I think only when the audio is transcoded.


AddressSanitizer: heap-buffer-overflow on address 0x6190007ae380 at pc 0x7f2b9e97ffdc bp 0x7f2b9b2fc760 sp 0x7f2b9b2fc758
READ of size 2 at 0x6190007ae380 thread T36
    #0 0x7f2b9e97ffdb in switch_ivr_play_file src/switch_ivr_play_say.c:1949
    #1 0x7f2b9ea240e7 in CoreSession::streamFile(char*, int, int) src/switch_cpp.cpp:1127
    #2 0x7f2b8e111dcf in _wrap_CoreSession_streamFile /usr/src/freeswitch/src/mod/languages/mod_lua/mod_lua_wrap.cpp:7431
    #3 0x7f2b8e097719  (/lib/x86_64-linux-gnu/liblua5.2.so.0+0xe719)
    #4 0x7f2b8e0a3ab2  (/lib/x86_64-linux-gnu/liblua5.2.so.0+0x1aab2)
    #5 0x7f2b8e097ac7  (/lib/x86_64-linux-gnu/liblua5.2.so.0+0xeac7)
    #6 0x7f2b8e09704b  (/lib/x86_64-linux-gnu/liblua5.2.so.0+0xe04b)
    #7 0x7f2b8e097d1e  (/lib/x86_64-linux-gnu/liblua5.2.so.0+0xed1e)
    #8 0x7f2b8e0936d6 in lua_pcallk (/lib/x86_64-linux-gnu/liblua5.2.so.0+0xa6d6)
    #9 0x7f2b8e0e870e in docall /usr/src/freeswitch/src/mod/languages/mod_lua/mod_lua.cpp:92
    #10 0x7f2b8e0e91ec in lua_parse_and_execute /usr/src/freeswitch/src/mod/languages/mod_lua/mod_lua.cpp:194
    #11 0x7f2b8e0e9c31 in lua_function /usr/src/freeswitch/src/mod/languages/mod_lua/mod_lua.cpp:482
    #12 0x7f2b9e7903a2 in switch_core_session_exec src/switch_core_session.c:2968
    #13 0x7f2b9e7916a9 in switch_core_session_execute_application_get_flags src/switch_core_session.c:2825
    #14 0x7f2b9e799433 in switch_core_standard_on_execute src/switch_core_state_machine.c:350
    #15 0x7f2b9e799433 in switch_core_session_run src/switch_core_state_machine.c:647
    #16 0x7f2b9e78aa10 in switch_core_session_thread src/switch_core_session.c:1727
    #17 0x7f2b9e77f42e in switch_core_session_thread_pool_worker src/switch_core_session.c:1791
    #18 0x7f2b9dea81c3  (/lib/x86_64-linux-gnu/libc.so.6+0x891c3)
    #19 0x7f2b9df2885b  (/lib/x86_64-linux-gnu/libc.so.6+0x10985b)

0x6190007ae380 is located 0 bytes to the right of 1024-byte region [0x6190007adf80,0x6190007ae380)
allocated by thread T36 here:
    #0 0x7f2b9f0b83b7 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:77
    #1 0x7f2b9e7103b2 in switch_buffer_create_dynamic src/switch_buffer.c:129
    #2 0x7f2b9e97f82b in switch_ivr_play_file src/switch_ivr_play_say.c:1888
    #3 0x7f2b9ea240e7 in CoreSession::streamFile(char*, int, int) src/switch_cpp.cpp:1127
    #4 0x7f2b8e111dcf in _wrap_CoreSession_streamFile /usr/src/freeswitch/src/mod/languages/mod_lua/mod_lua_wrap.cpp:7431
    #5 0x7f2b8e097719  (/lib/x86_64-linux-gnu/liblua5.2.so.0+0xe719)

Thread T36 created by T9 here:
    #0 0x7f2b9f049726 in __interceptor_pthread_create ../../../../src/libsanitizer/asan/asan_interceptors.cpp:207
    #1 0x7f2b9ec556c1 in fspr_thread_create threadproc/unix/thread.c:194

Thread T9 created by T0 here:
    #0 0x7f2b9f049726 in __interceptor_pthread_create ../../../../src/libsanitizer/asan/asan_interceptors.cpp:207
    #1 0x7f2b9ec556c1 in fspr_thread_create threadproc/unix/thread.c:194

SUMMARY: AddressSanitizer: heap-buffer-overflow src/switch_ivr_play_say.c:1949 in switch_ivr_play_file
Shadow bytes around the buggy address:

When  a  file  is  being played  with  the  playback
application  and  the  speed  is  changed  with  eg.
uuid_fileman  speed:+1  the audio  pitch  increases.
This commit tries to preserve the pitch even when the
speed is changed.

How?
+ Use longer sample chunks (where original freq is preserved)
+ Search for the best cut position using cross-correlation
+ Crossfade adjacent chunks to each other

This updated co-authored commit from the original attempts
to optimize memory usage by allocating memory on the heap
for samples of the previous frame, but using stack memory
for the modified data that is flushed to the buffer.

Co-authored-by: hari <[email protected]>
Co-authored-by: Joseph Nadiv <[email protected]>
@yois615 yois615 force-pushed the feature_keep_pitch_when_playback_speed_is_changed branch from f3d032d to bc6842a Compare January 9, 2025 15:50
@yois615
Copy link
Contributor

yois615 commented Jan 9, 2025

The logic at 1947 was updated to check if newlen is < sp_fadeLen. newlen gets reset to 1 on speed change which is the way to occasionally reproduce this bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants