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

Flat Spectrogram View #82

Merged
merged 23 commits into from
Mar 18, 2024
Merged

Flat Spectrogram View #82

merged 23 commits into from
Mar 18, 2024

Conversation

aure
Copy link
Member

@aure aure commented Mar 8, 2024

No description provided.

@mahal
Copy link
Contributor

mahal commented Mar 8, 2024

Thanks for getting onto that Spectrogram. I'm sorry about the many hound findings, didn't know about SwiftLint. Added it to my project so my future PRs won't suffer from colon violations and the like.

@aure
Copy link
Member Author

aure commented Mar 8, 2024

If you want to give me write privileges to your repo, I can fix things up there and this PR will be updated, or else I can fork yours and make a new pr off of it. Best to address these things prior to merging.

@mahal
Copy link
Contributor

mahal commented Mar 12, 2024

Sure, added you as collaborator.

@mahal
Copy link
Contributor

mahal commented Mar 12, 2024

Hey @aure I have fixed all but one hound linter warning. Will read more about protocol Identifiable, maybe use an lint exception.

@aure aure requested review from getdunne and removed request for getdunne March 15, 2024 18:29
@aure aure marked this pull request as draft March 15, 2024 18:30
@aure aure marked this pull request as ready for review March 15, 2024 18:30
@aure aure requested a review from maksutovic March 15, 2024 18:31
Copy link
Collaborator

@maksutovic maksutovic left a comment

Choose a reason for hiding this comment

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

Overall this is really cool, I especially love the array color extension -- but there are some important questions I'd like answered, specifically why are we rendering these as UIImages as opposed to doing the drawing directly in Canvas or CoreGraphics.

// Default: 2048
var fftSize = 4096/2

// how/why can the sample rate be edited? Shouldn't this come from the node/engine?
Copy link
Collaborator

Choose a reason for hiding this comment

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

yes full agree sample rate needs to come from engine

@available(iOS 17.0, *)
struct SpectrogramSlice: View, Identifiable {
static var counterSinceStart = 0
let id: Int
Copy link
Collaborator

Choose a reason for hiding this comment

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

we probably need an edge case for hound, id is a perfectly acceptable variable name

@available(iOS 17.0, *)
struct SpectrogramSlice: View, Identifiable {
static var counterSinceStart = 0
let id: Int
Copy link
Collaborator

Choose a reason for hiding this comment

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

why Int over UUID?

var frequencyChosen = 0.0
var points: [CGPoint] = []

for index in 1 ... (fftFloats.count / 2) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

this for loop is unsafe, there should be some guards prior to avoid division by zero etc...

if frequencyForBin > Double(spectrogramMaxFreq) { continue }
frequencyChosen = frequencyForBin

if frequencyForBin > 8000 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

magic number, should be a constant


// unused method drawing into a Canvas. Might be useful in the future
// when doing more energy efficent drawing
func createSpectrumSlice() -> some View {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I haven't run the code, but I don't understand what we gain from representing the spectrogram as a series of UIImage over drawing in Canvas or even CoreGraphics. I imagine that the current UIImage route is expensive and will never look as nice. At the very least I'd like an explanation why this strategy was employed, and its pros/cons, and what help you need to do this directly in Canvas.

/// a central feature in your app. Furthermore it's not scientificicly correct, when displaying
/// white noise, it will not show a uniform distribution.

@available(iOS 17.0, *)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Unclear what API requires iOS 17, this should be explained.

// Choose a higher value when you want to analyze low frequencies,
// choose a lower value when you want fast response and high frame rate on display.
// Default: 2048
var fftSize = 4096/2
Copy link
Collaborator

Choose a reason for hiding this comment

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

this probably should be an enum and document in the enum the tradeoffs of the different fft sizes

Copy link
Contributor

Choose a reason for hiding this comment

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


/// Model for the SpectrogramFlatView. Makes connection to the audio node and receives FFT data
@available(iOS 17.0, *)
class SpectrogramFlatModel: ObservableObject {
Copy link
Collaborator

Choose a reason for hiding this comment

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

if this code does indeed require iOS 17, then no reason to use newly deprecated ObservableObject and should use @Observable macro

// The lowest human bass voice in choral music is reaching down to C1 (32.7 Hz).
// don't go lower than 6.0, it just doesn't make sense and the display gets terribly distorted
// don't use 0 as it breaks the display because log10(0) is undefined and this error not handled
var minFreq: CGFloat = 48.0
Copy link
Collaborator

Choose a reason for hiding this comment

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

do these values change? otherwise move to lets.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, totally. Moved it not only to let but also to SpectrogramFFTMetaData so it can be opened up as public parameter in future. Ideally the minFreq and maxFreq can be configured on runtime. 3868d03

@mahal
Copy link
Contributor

mahal commented Mar 15, 2024

@maksutovic thank you so much for the thorough review and the totally legit questions. Will work on it and push it into the fork.

I really need to step up in linting and integrate it better into Xcode.
@mahal
Copy link
Contributor

mahal commented Mar 18, 2024

Added swiftlint directive to disable the warning from Hound about the var called id. With recent versions of swiftlint, the newly added lines will show up as a warning: Superflous Disable Command :-)

@mahal
Copy link
Contributor

mahal commented Mar 18, 2024

@maksutovic I've added more detail at the top of the class describing the dataflow and history of the class.

Drawing is done using UIGraphicsImageRenderer which is Core Graphics, with context.fill primitives. These then are cached as UImage and layouted onto the view.

I was struggling and couldn't find a good example on how to animate a Canvas from right to left as well as drawing async on it.

I did my fair share of Core Animation development with raskinformac.com but I'm pretty new to SwiftUI. So it didn’t took me long to figure out the bottleneck of the existing SpectrogramView using instruments and stuff, but also couldn't implement what I had in mind using ImageRenderer objectwillchange or an animated Canvas. Or all the other possibilites listed.

@mahal
Copy link
Contributor

mahal commented Mar 18, 2024

@aure @maksutovic You may have another look at this PR. I made the suggested changes as well as added some comments to the code and this PR.

// static Int instead of a UUID as identifier. While debugging it's practical
// to see the order and therefore time the slice was created.
// Furthermore for the sake of premature performance optimisation:
// Incrementing an Int could be supposedly faster than creating UUID.
Copy link
Collaborator

Choose a reason for hiding this comment

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

great thanks

@@ -97,7 +97,7 @@ class SpectrogramFlatModel: ObservableObject {
gradientUIColors: SpectrogramFlatView.gradientUIColors,
sliceWidth: sliceSize.width,
sliceHeight: sliceSize.height,
fftReadingsAsTupels: points,
fftReadingsFrequencyAmplitudePairs: points,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Love amplitude pairs, thank you!

Copy link
Collaborator

@maksutovic maksutovic left a comment

Choose a reason for hiding this comment

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

Thanks so much @mahal great updates, looking forward to using this, and iterating on it!

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.

3 participants