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

Refactoring notes and hooking up the preference synth #32

Merged
merged 3 commits into from
Jan 3, 2025
Merged

Conversation

leouofa
Copy link
Owner

@leouofa leouofa commented Jan 3, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for piano instrument selection
    • Introduced dynamic synthesizer type based on user preferences
  • Improvements

    • Enhanced instrument loading with visual spinner
    • Optimized instrument initialization process
    • Improved user experience for instrument selection
  • Performance

    • Streamlined piano sampler singleton management
    • Implemented more efficient instrument loading mechanism

Copy link

vercel bot commented Jan 3, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
gridbeat ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 3, 2025 5:00am

Copy link

coderabbitai bot commented Jan 3, 2025

Warning

Rate limit exceeded

@leouofa has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 1 minutes and 45 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 463ffc6 and 29e0c0d.

📒 Files selected for processing (4)
  • app/notes/page.tsx (1 hunks)
  • components/Grid.tsx (3 hunks)
  • components/LoadingSpinner.tsx (1 hunks)
  • hooks/useInstrument.ts (1 hunks)

Walkthrough

This pull request introduces a comprehensive update to the application's synthesizer management system. The changes focus on enhancing the flexibility of instrument selection by implementing a dynamic synthesizer configuration based on user preferences. A new PianoSamplerSingleton is introduced to manage piano sample loading, and the NotesPage component is modified to support different instrument types, including piano and polyphonic synthesizers. The default preferences are updated to use "piano" as the default synthesizer type.

Changes

File Change Summary
app/notes/page.tsx - Added instrument and isLoading state variables
- Updated useEffect to initialize instrument based on user preferences
- Modified playNote function to use new instrument state
components/Grid.tsx - Removed PianoSamplerSingleton class definition
- Updated loading state rendering
contexts/PreferencesContext.tsx - Changed default synthType from "poly" to "piano"
utils/PianoSamplerSingleton.ts - Added new singleton class for managing piano sampler
- Implemented getInstance() method for asynchronous sampler loading

Sequence Diagram

sequenceDiagram
    participant User
    participant NotesPage
    participant PreferencesContext
    participant PianoSamplerSingleton
    participant Tone

    User->>PreferencesContext: Set synthType
    NotesPage->>PreferencesContext: Retrieve synthType
    alt synthType is "piano"
        NotesPage->>PianoSamplerSingleton: getInstance()
        PianoSamplerSingleton->>Tone: Create Sampler
    else synthType is "poly"
        NotesPage->>Tone: Create PolySynth
    end
    NotesPage->>NotesPage: Update instrument state
    NotesPage->>User: Render notes interface
Loading

Possibly related PRs

Poem

🎹 A Rabbit's Musical Tale 🎵

In bytes of code, a synth takes flight,
From poly waves to piano's might,
With preferences dancing free,
Our melody finds its key,
A digital tune, pure delight! 🐰🎶


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@leouofa leouofa changed the title Note upgrades Refactoring notes and hooking up the preference synth Jan 3, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
utils/PianoSamplerSingleton.ts (2)

3-58: Consider avoiding a static-only class.

This file defines a class that contains only static members. A static-only class can sometimes be replaced by simpler exports of functions or objects, which may be clearer. If it’s essential to have class semantics (like enforcing the Singleton pattern), that’s fine, but bear in mind that this approach might appear more complex than necessary.

Here’s an example refactor that avoids a class while still preserving the Singleton behavior:

-import * as Tone from "tone";

-export class PianoSamplerSingleton {
-  private static instance: Tone.Sampler | null = null;
-  private static isLoading = false;
-  private static loadPromise: Promise<void> | null = null;
-
-  static async getInstance(): Promise<Tone.Sampler> {
-    if (!this.instance && !this.loadPromise) {
-      this.isLoading = true;
-      this.loadPromise = new Promise((resolve) => {
-        const sampler = new Tone.Sampler({
-          // ...
-        }).toDestination();
-      });
-    }
-    await this.loadPromise;
-    return this.instance!;
-  }
-}

+import * as Tone from "tone";

+let instance: Tone.Sampler | null = null;
+let isLoading = false;
+let loadPromise: Promise<void> | null = null;

+export async function getPianoSampler(): Promise<Tone.Sampler> {
+  if (!instance && !loadPromise) {
+    isLoading = true;
+    loadPromise = new Promise((resolve) => {
+      const sampler = new Tone.Sampler({
+        // ...
+      }).toDestination();
+    });
+  }
+  await loadPromise;
+  return instance!;
+}
🧰 Tools
🪛 Biome (1.9.4)

[error] 3-58: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)


[error] 9-9: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 9-9: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 10-10: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 11-11: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 48-48: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 49-49: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 55-55: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 56-56: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


9-11: Using this in static methods may be confusing for some readers.

The code uses this.instance, this.isLoading, etc. in static context, which can be slightly misleading since this typically references an instance. For clarity, you could replace this with the class name, e.g. PianoSamplerSingleton.instance, to make it obvious that these refer to static properties.

Here’s a minimal diff suggestion:

-static async getInstance(): Promise<Tone.Sampler> {
-  if (!this.instance && !this.loadPromise) {
-    this.isLoading = true;
+static async getInstance(): Promise<Tone.Sampler> {
+  if (!PianoSamplerSingleton.instance && !PianoSamplerSingleton.loadPromise) {
+    PianoSamplerSingleton.isLoading = true;
     ...
-            this.instance = sampler;
-            this.isLoading = false;
+            PianoSamplerSingleton.instance = sampler;
+            PianoSamplerSingleton.isLoading = false;
     ...
-  await this.loadPromise;
-  return this.instance!;
+  await PianoSamplerSingleton.loadPromise;
+  return PianoSamplerSingleton.instance!;

Also applies to: 48-49, 55-56

🧰 Tools
🪛 Biome (1.9.4)

[error] 9-9: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 9-9: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 10-10: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 11-11: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

app/notes/page.tsx (1)

93-121: Loading spinner logic is user-friendly.

Providing real-time feedback while the instrument loads is great. Users get a clear indication that data is being fetched or processed. Additionally, reusing a consistent spinner style throughout the app would offer a cohesive look and feel.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 436b973 and 463ffc6.

📒 Files selected for processing (4)
  • app/notes/page.tsx (1 hunks)
  • components/Grid.tsx (2 hunks)
  • contexts/PreferencesContext.tsx (1 hunks)
  • utils/PianoSamplerSingleton.ts (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
utils/PianoSamplerSingleton.ts

[error] 3-58: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)


[error] 9-9: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 9-9: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 10-10: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 11-11: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 48-48: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 49-49: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 55-55: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 56-56: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

🔇 Additional comments (5)
contexts/PreferencesContext.tsx (1)

13-13: Default synth type changed to "piano".

This addition changes the default synthesizer to "piano", which could be fine if you want everyone to start with the piano instrument. Ensure that existing users who already have local preferences stored still correctly load whatever was in their previous settings.

To confirm, you can run this shell script to search for all references to “defaultPreferences” or “gridbeat-preferences” and check if there are any assumptions about "poly" as the default:

✅ Verification successful

Let me search for any references to "poly" or synth type related code to ensure we have a complete picture.


Existing user preferences are safely preserved when loading from localStorage

The code correctly handles the transition from any previous synth type to the new default "piano". Here's why:

  • The PreferencesContext.tsx uses a spread operator to merge stored preferences with defaults:
{
  ...defaultPreferences,
  ...parsedPreferences,
}

This means that any existing user's stored synth type in localStorage will override the new default.

  • The application properly supports both "poly" and "piano" synth types as evidenced by:
    • The type definition in types/index.ts: export type SynthType = "piano" | "poly";
    • The implementation in Grid.tsx and notes/page.tsx that handles both types correctly
    • The menu UI in Menu.tsx that allows switching between both options
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
rg -A 3 'defaultPreferences|gridbeat-preferences'

Length of output: 1854


Script:

#!/bin/bash
# Search for any references to "poly" or synth type related code
rg -i "synthtype|poly.*synth" -A 3

Length of output: 4076

components/Grid.tsx (2)

8-8: Kudos for extracting the sampler logic into a dedicated file!

Importing PianoSamplerSingleton from a shared location centralizes the logic and fosters reusability. It also ensures that all components rely on the same instance of the sampler. This promotes consistency across the application.


139-165: The new loading spinner enhances user experience.

Displaying a loading indicator while waiting for instrument initialization is a nice UI improvement. It provides immediate feedback and a more polished feel.

app/notes/page.tsx (2)

7-8: Nicely done importing hooks and Singleton.

The addition of the usePreferences hook and PianoSamplerSingleton integrates well with your app’s context + Tone.js usage. This fosters dynamic instrument selection based on user-set preferences.


17-81: Ensure consistent disposal of instruments.

When you dispose of newInstrument if synthType !== "piano", you’re preventing memory leaks for PolySynth instruments. This is good. However, if the user quickly switches from “piano” to “poly” and back again, consider verifying that the sampler instance remains correct and that it’s not re-initialized unnecessarily.

Here is a script to search usage and see if there are calls in quick succession:

✅ Verification successful

Let me gather more information about the PianoSamplerSingleton implementation to understand how it handles instances.


The piano sampler singleton implementation is correct and safe

After reviewing the implementation, I can confirm that the current approach is safe and efficient:

  1. The PianoSamplerSingleton properly manages a single instance with static fields and proper loading states
  2. Quick switches between synth types are handled correctly because:
    • The piano sampler is never disposed (it's reused)
    • The singleton ensures only one instance exists
    • The loading state is properly tracked with isLoading and loadPromise
    • Multiple concurrent requests for the instance are handled safely through the Promise

The current implementation in app/notes/page.tsx correctly disposes only non-piano instruments while letting the piano sampler persist, which is the desired behavior for optimal performance and resource management.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Searching for usage of setPreferences or changes to synthesizer in the code.
rg -A 5 'setPreferences|synthType'

Length of output: 4146


Script:

#!/bin/bash
# Search for PianoSamplerSingleton implementation
ast-grep --pattern 'class PianoSamplerSingleton {
  $$$
}'

# Also search for any direct references to this class
rg "PianoSamplerSingleton" -A 5

Length of output: 5614

@leouofa leouofa merged commit e4f5c1e into main Jan 3, 2025
3 checks passed
@leouofa leouofa deleted the note-upgrades branch January 3, 2025 05:25
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.

1 participant