hi-ogawa / toy-metronome

https://metronome-hiro18181.vercel.app
0 stars 0 forks source link

random ideas #2

Closed hi-ogawa closed 1 year ago

hi-ogawa commented 1 year ago

nih-plugin ideas

pipewire ideas

hi-ogawa commented 1 year ago

experimenting with clap

# build clap examples https://github.com/robbert-vdh/nih-plug/
cargo xtask bundle sine # gain_gui_egui etc...

# build clap-host https://github.com/free-audio/clap-host
cmake --preset ninja-system
cmake --build --preset ninja-system

# run plugin
CLAP_HOST_FORCE_PLUGIN=../nih-plug/target/bundled/sine.clap ./builds/ninja-system/host/Debug/clap-host 
hi-ogawa commented 1 year ago

experimenting with nih-plug

# run plugin as jack app
cargo build -p gain_gui_egui
./target/debug/gain_gui_egui -b jack
screenshot with pipewire gui ![image](https://user-images.githubusercontent.com/4232207/206899143-1e1c8f57-19d2-41b1-8581-d57930670dee.png)
hi-ogawa commented 1 year ago

EDIT: implemented in https://github.com/hi-ogawa/nih-plug-examples

egui piano widget

image

// TODO:
// - [x] render keyboard
// - [ ] handle event
//   - click
//   - key input
// - [ ] manage state (both external and internal?)

pub fn piano_ui(ui: &mut egui::Ui) -> egui::Response {
    let padding = 1.0;
    let key_size = egui::vec2(12.0, 42.0);
    let key_size_black = egui::vec2(12.0, 22.0);
    let num_white_keys = 7;

    let (response, painter) = ui.allocate_painter(
        key_size * egui::vec2(num_white_keys as f32, 1.0),
        egui::Sense::click(),
    );

    // TODO
    if response.clicked() {}

    for i in 0..7 {
        let rect = egui::Rect::from_min_size(
            response.rect.min + egui::vec2((i as f32) * key_size.x + padding, 0.0),
            key_size - 2.0 * egui::vec2(padding, padding),
        );
        painter.rect_filled(rect, egui::Rounding::none(), egui::Color32::WHITE);
    }

    for i in (0..2).chain(3..6) {
        let rect = egui::Rect::from_min_size(
            response.rect.min + egui::vec2(((i as f32) + 0.5) * key_size.x + padding, 0.0),
            key_size_black - 2.0 * egui::vec2(padding, padding),
        );
        painter.rect_filled(rect, egui::Rounding::none(), egui::Color32::BLACK);
    }

    response
}
fn generate_note_rects() -> Vec<(usize, egui::Rect)> {
    // TODO: parameters
    let padding = 1.0;
    let key_size = egui::vec2(20.0, 80.0);

    // define Rect to represent each key in "local" coordinates
    let mut note_rects: Vec<(usize, egui::Rect)> = vec![];

    for i in 0..(14 * 2) {
        // no black key between E-F and B-C
        if i % 14 == 5 || i % 14 == 13 {
            continue;
        }
        let pos = egui::pos2(0.5 * (i as f32) * (key_size.x + 2.0 * padding), 0.0);
        let size = if i % 2 == 0 {
            key_size
        } else {
            key_size * egui::vec2(1.0, 0.5)
        };
        let rect = egui::Rect::from_min_size(pos, size);
        note_rects.push((i, rect));
    }

    note_rects.sort_by_key(|(i, _)| i % 2); // black key has higher z
    note_rects
}

pub fn piano_ui(ui: &mut egui::Ui) -> egui::Response {
    let note_rects = generate_note_rects();
    let paint_rect = note_rects
        .iter()
        .fold(egui::Rect::NOTHING, |acc, &(_, rect)| acc.union(rect));

    let (mut response, painter) =
        ui.allocate_painter(paint_rect.size(), egui::Sense::click_and_drag());

    for &(i, rect) in &note_rects {
        let rect = rect.translate(response.rect.min.to_vec2());
        let color = if i % 2 == 0 {
            egui::Color32::WHITE
        } else {
            egui::Color32::BLACK
        };
        painter.rect_filled(rect, egui::Rounding::from(1.0), color);

        if let Some(pointer_pos) = response.interact_pointer_pos() {
            if rect.contains(pointer_pos) {
                response.mark_changed();
                dbg!("yeah!", i); // TODO
            }
        }
    }
    response
}
hi-ogawa commented 1 year ago

Closing since enough interesting things are explored already: