-
Notifications
You must be signed in to change notification settings - Fork 110
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
Initial istft implementation. #85
base: master
Are you sure you want to change the base?
Conversation
Here is a working example backward_plan{T<:Union(Float32, Float64)}(X::AbstractArray{Complex{T}}, Y::AbstractArray{T}) =
FFTW.Plan(X, Y, 1, FFTW.ESTIMATE, FFTW.NO_TIMELIMIT).plan
x=randn(8)
y=rfft(x) # works also for fft(x)
tmp1=similar(y)
tmp2=similar(x)
plan = backward_plan(tmp1,tmp2)
copy!(tmp1,y)
copy!(tmp2,x)
FFTW.execute(plan, tmp1, tmp2) # warning: destroys tmp1
scale!(tmp2, FFTW.normalization(tmp2)) I suggest you do a copy to for 1:length(wsum)
@inbounds wsum[i] != 0 && (out[i] /= wsum[i])
end
# instead of
pos = wsum .!= 0
out[pos] ./= wsum[pos] |
Thanks! How did you implement
but then if I try to call it I get:
EDIT: I see you posted your backward plan now. So you do not need to use FFTW.BACKWARD in the arguments? |
The plan is exactly how it is done in the FFTW module, so FFTW.BACKWARD is apparently not needed. |
Right, it seems to work perfectly. I'll just test this with different windows and lengths and add tests and documentation, but the function seems to be working well now. Thanks a lot! |
To avoid allocation (if you have not not so already): copy!(tmp1, 1, S, 1+(k-1)*size(S,1), length(tmp1)) |
That's exactly what I did in my last commit. However, execution time and allocation seem to be the same for both implementations, at least for the size of slices I'm using (length 128). |
I mean for the |
You are right. I fixed both lines and memory allocation was reduced to 75% of the previous implementation. Execution times are more or less the same (again, maybe because I am using small window sizes). |
OK good. A small bug: 1+(k-1)*winc:((k-1)*winc+n
# should be
ix + n
# with
ix = (k-1)*winc |
Using
|
@r9y9 That's a good point. It would however require a bit of pointer hacking like tmp1 = pointer_to_array(pointer(S, 1+(k-1)*size(S,1)), size(S,1), false)
# replaces
copy!(tmp1, 1, S, 1+(k-1)*size(S,1), length(tmp1)) |
@gummif Thanks for your comment. I was thinking about replacing: copy!(tmp1, 1, S, 1+(k-1)*size(S,1), length(tmp1))
FFTW.execute(p, tmp1, tmp2) with FFTW.execute(p, sub(S,1:size(S,1), k), tmp2) Though this requires slight allocations in using DSP, Base.Test
wlen = 2048
overlap = div(wlen, 2)
x1 = rand(2^12)
println("testing with short signal (length: $(length(x1)))")
X1 = stft(x1, wlen, overlap)
# force JIT-compilation
istft(X1, wlen, overlap)
istft2(X1, wlen, overlap)
@time y1 = istft(X1, wlen, overlap)
@time y2 = istft2(X1, wlen, overlap)
@test_approx_eq x1 y1
@test_approx_eq y1 y2
x1 = rand(2^22)
println("testing with long signal (length: $(length(x1)))")
X1 = stft(x1, wlen, overlap)
@time y1 = istft(X1, wlen, overlap)
@time y2 = istft2(X1, wlen, overlap)
@test_approx_eq x1 y1
@test_approx_eq y1 y2 The results using julia v0.3.2:
complete code for I agree the current implementation. I think we can add benchmarks and improve performance then. |
I tested both the suggested version with the pointer to array and the one with copy. Surprisingly, the one using copy uses slightly less memory (maybe because a new pointer is allocated at each iteration?). I only averaged over 100 executions but the version using copy was also slightly faster. Since the
When trying both @r9y9 and the
With the |
Oh, I had the same segfaults. Could you modify backward_plan as follows and try again? backward_plan2{T<:Union(Float32, Float64)}(X::AbstractArray{Complex{T}}, Y::AbstractArray{T}) =
FFTW.Plan(X, Y, 1, FFTW.ESTIMATE | FFTW.PRESERVE_INPUT, FFTW.NO_TIMELIMIT).plan to backward_plan2{T<:Union(Float32, Float64)}(X::AbstractArray{Complex{T}}, Y::AbstractArray{T}) =
FFTW.Plan(X, Y, 1, FFTW.ESTIMATE | FFTW.PRESERVE_INPUT, FFTW.NO_TIMELIMIT) and also FFTW.execute(p, tmp1, tmp2) to FFTW.execute(p.plan, tmp1, tmp2) I am not sure why segfaults happen but this changes seems to fix the segfaults in my environment. It's weird.. |
I'm not able to produce a significant difference. So the version with copy might be more safe. julia> @time for i = 1:nn
y2 = istft2(X1, wlen, overlap)
end
elapsed time: 0.148178763 seconds (110615752 bytes allocated, 34.14% gc time)
julia> @time for i = 1:nn
y1 = istft(X1, wlen, overlap)
end
elapsed time: 0.159816237 seconds (110062040 bytes allocated, 46.83% gc time)
julia> @time for i = 1:nn
y2 = istft2(X1, wlen, overlap)
end
elapsed time: 0.148254777 seconds (110615736 bytes allocated, 32.35% gc time)
julia> @time for i = 1:nn
y1 = istft(X1, wlen, overlap)
end
elapsed time: 0.13468912 seconds (110061368 bytes allocated, 37.36% gc time) |
Sorry for my insufficient benchmarks. I agree with @gummif |
This is a late response, but the segfaults may come from trying to apply a plan that expects an array with a certain alignment to an array with a different alignment, which would be the case for a SubArray with an odd offset. You can use the |
Alright, so there is no reason not to use |
Oh, it seems I did something terrible while rebasing. I thought I had squashed everything instead of making this mess. Any tips on how to fix this? |
nframes = size(S,2)-1 | ||
outlen = nfft + nframes*winc | ||
out = zeros(T, outlen) | ||
tmp1 = similar(S[:,1]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would should be Array(eltype(S), size(S, 1))
to avoid making an unnecessary copy and to ensure that the array is actually an Array (which is not necessarily the case if S
is an AbstractMatrix).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, will fix!
Not entirely sure what's going on with the history here, but the diff looks right. I guess the nuclear option is: git diff master > patch.diff
git reset --hard master
patch -p1 < patch.diff
rm patch.diff and then commit and force-push to this branch. |
This should do the trick, thanks @simonster! Let's wait until the tests pass and then finally merge this? |
Any more comments on this? @simonster @gummif @r9y9 |
What ever happened to this? This came up in a discourse thread re: |
@standarddeviant I ended up never being able to finish this PR and in the meantime, Julia changed a lot and I was not using it on a daily basis anymore. Please feel free to do whatever is needed to get this to work with newer versions of Julia and submit another PR. |
I started to write an istft as well, today, until I found out this PR and the MusicProcessing reference mentioned above. The latter turned out to have an alternative implementation of MFCC as well. It would be useful, indeed, to have this While implementing my own little While we're at it, it would also be great to have an option to The advantage would be that such a scheme would have 1000/100 = 10 frames, so that the frames are more easy to align with the original samples (first frame aligned with 0..100, the middle of the analysis window, etc.). The analysis would be symmetric w.r.t. start and end, of the signals, and of the frames themselves. In Kaldi a modern speech recognition toolkit, this is known as the analysis option |
It seems a little bit unnatural to me to circularly wrap a signal by default. When I apply stfts to data, its usually because it's non-stationary data, and the first and the last frames end up being quite different from each other. @davidavdav, would you have any interest in continuing this PR? |
gist here Right now it appears to work ok using a hanning window but wrongly scaled at the beginning when window is "nothing". I am not sure how to properly address this, perhaps the scaling I see in older code? @test x1 ≈ Y1 fails even with hanning window, I believe because of discrepancies at the the first sample. Once we have the algorithm working I'd be happy to work on the tests, documentation and do a proper PR but happy to hear your thoughts |
I don't think signals under a stft transform should be circularly wrapped, either. Where is that suggested and/or happening?
I think that if we have a |
Good question, I don't know where I got that from. |
I wrote an
istft
function to bring a real signal from its per-frame STFT representation to a time-domain signal by using overlap-add. This version is unoptimized as I am having some trouble to create anFFTW.Plan
for a backward RFFT. I can add my non-working code to this branch in case you want to take a look.I understand the PR is in a crude shape right now but I need some help moving forward, and I figured it would be better to submit a PR than try to solve this via e-mail.
Any suggestions for improvement or tips on how I can use the
FFTW.Plan
interface to do what I want here?