Skip to content

Applications: Add SampleEditor audio editing application#26509

Open
macsplit wants to merge 1 commit intoSerenityOS:masterfrom
macsplit:sample-editor-basic
Open

Applications: Add SampleEditor audio editing application#26509
macsplit wants to merge 1 commit intoSerenityOS:masterfrom
macsplit:sample-editor-basic

Conversation

@macsplit
Copy link
Contributor

Part 1 of 2. This PR contains the core logic for SampleEditor, refactored to remove redundant abstractions as suggested in #26402.

Screenshot_20251227_224454

Changes from original PR:

  • Removed clipboard operations (copy/cut/paste) - will be added in Part 2
  • Integrated SampleNullBlock into SampleBlock as a concrete implementation using a static factory method (SampleBlock::create_null())
  • Removed SampleFile abstraction - SampleSourceFile now inherits directly from RefCounted since it was the only implementation

The application supports:

  • Opening and playing WAV/FLAC files
  • Waveform visualization with zoom controls
  • Selection management (select all, clear selection)
  • Saving audio files

Part 2 will add clipboard operations and editing capabilities (cut/copy/paste with reference-based architecture).

@github-actions github-actions bot added the 👀 pr-needs-review PR needs review from a maintainer or community member label Dec 27, 2025
@LucasChollet
Copy link
Member

FYI, CI is red

@macsplit macsplit force-pushed the sample-editor-basic branch 3 times, most recently from 135235f to 4d5a791 Compare January 9, 2026 17:28
@BuggieBot
Copy link
Member

Hello!

One or more of the commit messages in this PR do not match the SerenityOS code submission policy, please check the lint_commits CI job for more details on which commits were flagged and why.
Please do not close this PR and open another, instead modify your commit message(s) with git commit --amend and force push those changes to update this PR.

@macsplit macsplit force-pushed the sample-editor-basic branch 4 times, most recently from 929c23f to c19dba6 Compare January 11, 2026 00:53
@macsplit
Copy link
Contributor Author

Okay, finally it's passing.

SampleEditor is a graphical audio sample editor supporting WAV
and FLAC file formats. It provides essential audio editing
functionality including playback, waveform visualization, and
selection management.

Key features:
- WAV and FLAC format support for loading and saving
- Real-time audio playback with configurable start positions
- Reference-based architecture for memory-efficient editing
- Zoom controls for waveform inspection
- Keyboard shortcuts for all common operations

The application uses a block-based design where audio content
is represented as file references. This will allow efficient
editing of large files without loading everything into RAM.

This is Part 1 of a two-part implementation. Part 2 will add
clipboard operations (copy/cut/paste).
@macsplit macsplit force-pushed the sample-editor-basic branch from c19dba6 to d161831 Compare February 1, 2026 00:54
Copy link
Member

@LucasChollet LucasChollet left a comment

Choose a reason for hiding this comment

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

I started to read the code and left a few comments. I also tested the app at the same time, and I was a bit disappointed...
It seems that you're parsing the audio file on every frame of the render loop, which makes the application unusable with the two test files that I got. Can you fix that?

Image Image

Also, the class hierarchy is quite difficult to understand, what is a SampleBlock vs SampleFileBlock vs SampleBuffer vs SampleSourceFile. They all have common methods while not having a clear goal. Let's talk about it on Discord.

It would also be nice to find a way to split SampleWidget a bit.

@@ -0,0 +1,21 @@
serenity_component(
SampleEditor
REQUIRED
Copy link
Member

Choose a reason for hiding this comment

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

I know the app is great and all, but let's use RECOMMENDED instead 😄

Comment on lines +37 to +40
auto source_file = TRY(try_make_ref_counted<SampleSourceFile>(path));
size_t length = source_file->length();
auto file_block = TRY(try_make_ref_counted<SampleFileBlock>(
source_file, (size_t)0, (size_t)length - 1));
Copy link
Member

Choose a reason for hiding this comment

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

It seems that you're always creating a SampleSourceFile to pass it to a SampleFileBlock.
Please make SampleFileBlock's constructor take a path and create the SourceFile itself.

The code will be a bit neater but will also make it easier to understand the class hierarchy.

Comment on lines +63 to +65
m_new_action = GUI::Action::create(
"&New", { Mod_Ctrl, Key_N },
TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/new.png"sv)),
Copy link
Member

Choose a reason for hiding this comment

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

No required action: Seems like we could use a make_new_action factory in the project, multiple place would benefit.

#include <AK/String.h>
#include <AK/Types.h>

typedef struct SampleFormat {
Copy link
Member

Choose a reason for hiding this comment

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

Again, this is not C, let's use a struct with no typedef

RenderStruct rendered_sample_within_buffer(FixedArray<Audio::Sample>& buffer, size_t at);
String m_filename;
FixedArray<Audio::Sample> m_buffer;
StringView m_format_name;
Copy link
Member

Choose a reason for hiding this comment

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

Seems unused.

@macsplit
Copy link
Contributor Author

macsplit commented Feb 9, 2026

Okay will look through this feedback

@github-actions
Copy link

github-actions bot commented Mar 3, 2026

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions!

@github-actions github-actions bot added the stale label Mar 3, 2026
Copy link
Collaborator

@kleinesfilmroellchen kleinesfilmroellchen left a comment

Choose a reason for hiding this comment

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

Haven’t looked at the GUI code too closely, but the audio code obviously. There’s a lot of things to fix here still.

TRY(Core::System::unveil("/etc", "r"));
TRY(Core::System::unveil("/res", "r"));
TRY(Core::System::unveil("/home", "rwc"));
TRY(Core::System::unveil("/home/anon", "rwc"));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Huh, I thought we didn’t have those anymore. If you want to properly unveil with arbitrary user file access, I think the pattern was FileSystemAccessClient (and remove /home from the unveil list).

(Doesn’t FSAC have a mode where it gives you an open fd without you having to open the file yourself?)

window->set_main_widget(main_widget);
TRY(main_widget->initialize_menu_and_toolbar(window));

TRY(Core::System::unveil("/", "r"));
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems unnecessary.

TRY(Core::System::unveil(nullptr, nullptr));

if (arguments.argc > 1) {
TRY(main_widget->open(arguments.strings[1]));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Note: the application should still start even if the file provided is inaccessible or invalid.

(Sidenote: you can also specifically unveil this path as rw, so FSAC doesn’t need to be invoked on program startup)

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2025, Lee Hanken
Copy link
Collaborator

Choose a reason for hiding this comment

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

Copyright date is wrong here and many other files

Copy link
Collaborator

Choose a reason for hiding this comment

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

Note for this and the following sample-related code: If it contains no GUI code, it can go into LibDSP. I will need that code for Piano too :)

Comment on lines +71 to +74
double val = buffer[at].left;
double square = val * val;
double mod = AK::abs(val);

Copy link
Collaborator

Choose a reason for hiding this comment

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

Please improve the variable names val and mod.

size_t end = max(size, at + window);

size_t samples = max(window, 25);
size_t increment = window / samples;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Make sure that this is not zero, otherwise the loop below is infinite.


RenderStruct value = { 0, 0, 0, 0 };

if (count_plus) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

use > 0, this is not C code golf


void SampleSourceFile::begin_loading_samples() { m_stream_position = 0; }

FixedArray<Audio::Sample> SampleSourceFile::load_more_samples()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why are you not calling this function above in SampleFileBlock?

}

m_current_audio_buffer.swap(buffer);
MUST(m_audio_connection->async_enqueue(m_current_audio_buffer));
Copy link
Collaborator

Choose a reason for hiding this comment

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

The enqueue API is intended for noninteractive players, which SampleEditor isn’t. You should instead be reaching directly into the underlying buffer API, which gives you better latency on user actions. I believe that Piano already does so, so you should check the code there (instead of the other multimedia apps).

@github-actions github-actions bot removed the stale label Mar 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

👀 pr-needs-review PR needs review from a maintainer or community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants