FDWaveformView icon indicating copy to clipboard operation
FDWaveformView copied to clipboard

add an option to prevent scrolling past the end of the audio

Open calioptrix opened this issue 8 years ago • 6 comments

When zoomed in, you can swipe the view until the last part of the waveform is on the left edge of the screen. This seems a bit disorienting to me. It would be nice to have an option so that the end of the waveform stays at the right edge of the screen.

calioptrix avatar Jul 19 '17 16:07 calioptrix

This is not intended behavior and sounds like a bug.

fulldecent avatar Jul 24 '17 22:07 fulldecent

I fixed it locally by:


 @objc func handlePinchGesture(_ recognizer: UIPinchGestureRecognizer) {
        if !doesAllowStretch {
            return
        }
        
        if recognizer.scale == 1 {
            return
        }
        
        setScale(scale: scale * recognizer.scale, fixedX: recognizer.location(in: self).x/bounds.width)
        recognizer.scale = 1
    }



    private var scale:CGFloat = 1
    
    func setScale(scale:CGFloat, fixedX:CGFloat) {
        if scale < 1
        {
            return
        }
        self.scale = scale
        
        let zoomSamplesCount = CGFloat(zoomSamples.count)
        let pinchCenterSample = zoomSamples.lowerBound + Int(zoomSamplesCount * fixedX)
        
        let newZoomSamplesCount = CGFloat(totalSamples)/scale
        var newZoomStart = pinchCenterSample - Int(newZoomSamplesCount * fixedX )
        var newZoomEnd = newZoomStart + Int(newZoomSamplesCount)
        if newZoomEnd > totalSamples
        {
            newZoomStart = totalSamples - Int(newZoomSamplesCount)
            newZoomEnd = totalSamples
        }
        
        zoomSamples = (newZoomStart ..< newZoomEnd).clamped(to: 0 ..< totalSamples)
    }



override open func layoutSubviews() {
        super.layoutSubviews()
        guard audioContext != nil && !zoomSamples.isEmpty else {
            return
        }

        switch cacheStatus() {
        case .dirty:
            renderWaveform()
            return
        case .notDirty(let cancelInProgressRenderOperation):
            if cancelInProgressRenderOperation {
                inProgressWaveformRenderOperation = nil
            }
        }

        let childFrame : CGRect
        
        
        if let cachedSampleRange = cachedWaveformRenderOperation?.sampleRange, !cachedSampleRange.isEmpty , !zoomSamples.isEmpty
        {
            let cacheScale = CGFloat(cachedSampleRange.count)/CGFloat(zoomSamples.count)
            
            let width = frame.width * cacheScale
            let x = CGFloat(cachedSampleRange.lowerBound-zoomSamples.lowerBound)*width/CGFloat(cachedSampleRange.count)
            childFrame = CGRect(x: x, y: 0, width: width, height: frame.height)
        }
        else
        {
            childFrame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
        }
        
        imageView.frame = childFrame
        highlightedImage.frame = childFrame
        //clipping.frame = CGRect(x: frame.width * scaledHighlightedX, y: 0, width: frame.width * scaledHighlightedWidth, height: frame.height)
        clipping.isHidden = !(highlightedSamples?.overlaps(zoomSamples) ?? false)
        print("layed-out frames: \(frame) -- \(imageView.frame)")
    }

Note: clipping.frame is not set

DamonChen117 avatar Dec 31 '17 06:12 DamonChen117

Thanks for sharing. Does this work for anyone else?

Also related: #96

fulldecent avatar Jan 03 '18 04:01 fulldecent

I was experiencing this in a consumer app because I'm manually placing some additional controls over the waveform, and it seemed like my math was ok but the handles still would end up in the wrong place when zooming/scrolling the waveform (and otherwise ok, but that proves little since the math isn't used when at zero zoom).

I tried the above fix, but it clearly is not the issue since all it does is not apply the frame changes to the clipping view, which is only for the highlighted frames functionality. You can see this by commenting the above line, building the test app, and scrubbing. You'll see that the scrub and zoom are now no longer in sync.

I believe the underlying issue here is not that it is being scrolled outside of the view, but that the render cache is wrong and it's not being re-rendered often enough. In the demo app, you can see the waveform sometimes "catch up" to the current (or perhaps just a recent) render. I'll be playing with with spare time to see if I can find a reasonable fix.

jesseditson avatar Mar 11 '19 01:03 jesseditson

I started testing this, and it seems to be at least one very old issue - the simple repro case I'm working with is that when you are zoomed in, the waveform should not compress when panning. I figured it once worked and regressed, but trying to bisect the issue got me all the way back to before v1.0, and I could reproduce on every version that I could see a waveform in (I stopped testing at 9ba0b654e988c25b01e21a3d43bc6b178d06804a, which I couldn't get to render anything). Given that, I think this is less of a bug and more of a fundamental logic issue. I suspect that zoom functionality will need to be rewritten or heavily refactored to be accurate, and I do not have reason to believe that it works in any version of this lib - I'd be happy to help review or debug further as this library would save me a lot of time if it worked!

jesseditson avatar Mar 11 '19 01:03 jesseditson

Hello, checking in to see if anybody here has solved this issue for themselves and could share back the solution as a PR?

fulldecent avatar Aug 24 '20 22:08 fulldecent