Skip to content

Commit

Permalink
Saving and loading subgraph presets working E2E!
Browse files Browse the repository at this point in the history
 * Build out all the backend stuff (migrations, models, routes) for saving and loading subgraph presets with tags, matching existing preset systems
 * Wire up graph editor UI actions to save and load presets
   * Using existing generic preset saver/picker components
 * Add global toaster to the app
 * Fixed a couple of bugs with the previously subgraph/subgraph portal implementation
 * Build out code for saving + loading serialized subgraphs
   * Handle storing the state of all VCs, FCs, and child subgraphs for the subgraph being saved
   * Handle updating all the entity IDs to random IDs to avoid conflicts
   * Handle creating all of the entities into the new context
   * Handle wiring up subgraph portals with accurate inputs/outputs
   * Handle connecting everything up
   * Handle updating special-case FC states (subgraph portal, graph editor) to correct IDs as well
 * Everything I've tested so far is working as expected
  • Loading branch information
Ameobea committed Feb 12, 2024
1 parent 010e725 commit eee5198
Show file tree
Hide file tree
Showing 50 changed files with 1,715 additions and 799 deletions.
1,241 changes: 628 additions & 613 deletions backend/Cargo.lock

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ version = "0.1.0"
authors = ["Casey Primozic <me@ameo.link>"]
edition = "2021"

# enable debug info in release mode
[profile.release]
debug = true

[dependencies]
dotenv = "0.15.0"

diesel = { version = "2.0", features = ["mysql", "chrono"] }

hex = "0.4"

fxhash = "0.2"

itertools = "0.12"

lazy_static = "1.4.0"
Expand All @@ -31,10 +37,12 @@ serde_derive = "1.0"

sha2 = "0.10"

uuid = { version = "1.0", features = ["serde"] }

tokio = { version = "1.24", features = ["macros", "rt-multi-thread"] }

scrypt = "0.11.0"
base64 = "0.21.0"
base64 = "0.21"

rust-s3 = { version = "0.33", features = [] }
aws-region = { version = "0.25.0", features = ["serde"] }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
DROP TABLE subgraph_preset_tags_join;
DROP TABLE subgraph_preset_tags;
DROP TABLE subgraph_presets;
15 changes: 15 additions & 0 deletions backend/migrations/2024-02-11-211258_subgraph-presets/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
CREATE TABLE subgraph_presets (
id BIGINT NOT NULL AUTO_INCREMENT UNIQUE,
user_id BIGINT REFERENCES users(id) ON DELETE CASCADE,
title TEXT NOT NULL,
description TEXT NOT NULL,
content LONGTEXT NOT NULL,
PRIMARY KEY (id)
);

CREATE TABLE subgraph_preset_tags (
id BIGINT NOT NULL AUTO_INCREMENT UNIQUE,
subgraph_preset_id BIGINT NOT NULL REFERENCES subgraph_presets(id) ON DELETE CASCADE,
tag_id BIGINT NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
PRIMARY KEY (id)
);
3 changes: 2 additions & 1 deletion backend/src/db_util/login.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::convert::TryFrom;

use base64::Engine;
use diesel::{prelude::*, QueryResult};
use scrypt::{
password_hash::{
Expand Down Expand Up @@ -38,7 +39,7 @@ pub fn generate_login_token() -> String {
let mut rng = OsRng;
let mut bytes = [0u8; 64];
rng.fill_bytes(&mut bytes);
base64::encode(&bytes)
base64::engine::general_purpose::STANDARD.encode(&bytes)
}

pub async fn get_user_by_username(
Expand Down
3 changes: 2 additions & 1 deletion backend/src/db_util/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::HashMap;

use diesel::{prelude::*, QueryResult};
use fxhash::FxHashMap;
use itertools::Itertools;
use rocket::serde::json::Json;

Expand Down Expand Up @@ -70,7 +71,7 @@ pub async fn build_tags_with_counts(
String::from("DB error loading preset tags from DB")
})?;

let mut counts_by_tag: HashMap<String, i64> = HashMap::new();
let mut counts_by_tag: FxHashMap<String, i64> = FxHashMap::default();
for looper_preset_tag in all_looper_preset_tags {
let tag = looper_preset_tag.tag.clone();
let count = counts_by_tag.entry(tag).or_insert(0);
Expand Down
4 changes: 4 additions & 0 deletions backend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ async fn main() {
routes::get_wavetable_preset_by_id,
routes::create_wavetable_preset,
routes::get_wavetable_preset_tags,
routes::get_subgraph_presets,
routes::get_subgraph_preset_by_id,
routes::create_subgraph_preset,
routes::get_subgraph_preset_tags,
])
.attach(CorsFairing);

Expand Down
20 changes: 0 additions & 20 deletions backend/src/models/looper_preset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,6 @@ pub struct SerializedLooperInstState {
pub active_module_ix: usize,
}

#[derive(Serialize, Queryable)]
#[serde(rename_all = "camelCase")]
pub struct LooperPresetDescriptor {
pub id: i64,
pub name: String,
pub description: String,
pub tags: Vec<String>,
pub user_id: Option<i64>,
pub user_name: Option<String>,
}

#[derive(Insertable)]
#[diesel(table_name = looper_presets)]
pub struct NewLooperPreset {
Expand All @@ -55,12 +44,3 @@ pub struct NewLooperPresetTag {
pub looper_preset_id: i64,
pub tag_id: i64,
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SaveLooperPresetRequest {
pub name: String,
pub description: String,
pub tags: Vec<String>,
pub serialized_looper_inst_state: SerializedLooperInstState,
}
21 changes: 21 additions & 0 deletions backend/src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,28 @@ pub mod looper_preset;
pub mod midi_composition;
pub mod private_sample_libraries;
pub mod remote_samples;
pub mod subgraph_presets;
pub mod synth_preset;
pub mod tags;
pub mod user;
pub mod wavetable_preset;

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SaveGenericPresetRequest<T> {
pub name: String,
pub description: String,
pub tags: Vec<String>,
pub preset: T,
}

#[derive(Serialize, Queryable)]
#[serde(rename_all = "camelCase")]
pub struct GenericPresetDescriptor {
pub id: i64,
pub name: String,
pub description: String,
pub tags: Vec<String>,
pub user_id: Option<i64>,
pub user_name: Option<String>,
}
32 changes: 32 additions & 0 deletions backend/src/models/subgraph_presets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use uuid::Uuid;

use crate::schema::{subgraph_preset_tags, subgraph_presets};

#[derive(Insertable)]
#[diesel(table_name = subgraph_presets)]
pub struct NewSubgraphPreset {
pub user_id: Option<i64>,
pub title: String,
pub description: String,
pub content: String,
}

#[derive(Insertable)]
#[diesel(table_name = subgraph_preset_tags)]
pub struct NewSubgraphPresetTag {
pub subgraph_preset_id: i64,
pub tag_id: i64,
}

#[derive(Serialize, Deserialize)]
pub struct SerializedSubgraphPreset {
pub fcs: Vec<serde_json::Map<String, serde_json::Value>>,
pub vcs: Vec<serde_json::Map<String, serde_json::Value>>,
pub intra_conns: Vec<(
serde_json::Map<String, serde_json::Value>,
serde_json::Map<String, serde_json::Value>,
)>,
pub subgraphs: Vec<(Uuid, serde_json::Map<String, serde_json::Value>)>,
pub base_subgraph_id: Uuid,
pub connnecting_subgraph_id: Uuid,
}
16 changes: 7 additions & 9 deletions backend/src/routes/looper_preset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,18 @@ use crate::{
login::get_logged_in_user_id,
},
models::{
looper_preset::{
LooperPresetDescriptor, NewLooperPreset, NewLooperPresetTag, SaveLooperPresetRequest,
SerializedLooperInstState,
},
looper_preset::{NewLooperPreset, NewLooperPresetTag, SerializedLooperInstState},
tags::{EntityIdTag, TagCount},
user::MaybeLoginToken,
GenericPresetDescriptor, SaveGenericPresetRequest,
},
WebSynthDbConn,
};

#[get("/looper_presets")]
pub async fn get_looper_presets(
conn: WebSynthDbConn,
) -> Result<Json<Vec<LooperPresetDescriptor>>, String> {
) -> Result<Json<Vec<GenericPresetDescriptor>>, String> {
use crate::schema::{looper_presets, looper_presets_tags, tags, users};

let (looper_presets, preset_tags) = conn
Expand Down Expand Up @@ -64,7 +62,7 @@ pub async fn get_looper_presets(
.map(|tag| tag.tag.clone())
.collect_vec();

LooperPresetDescriptor {
GenericPresetDescriptor {
id,
name,
description,
Expand Down Expand Up @@ -115,15 +113,15 @@ pub async fn get_looper_preset_by_id(
#[post("/looper_preset", data = "<looper_preset>")]
pub async fn create_looper_preset(
conn: WebSynthDbConn,
looper_preset: Json<SaveLooperPresetRequest>,
looper_preset: Json<SaveGenericPresetRequest<SerializedLooperInstState>>,
login_token: MaybeLoginToken,
) -> Result<Json<i64>, String> {
use crate::schema::{looper_presets, looper_presets_tags};

let user_id = get_logged_in_user_id(&conn, login_token).await;

let SaveLooperPresetRequest {
serialized_looper_inst_state,
let SaveGenericPresetRequest {
preset: serialized_looper_inst_state,
name,
description,
tags,
Expand Down
7 changes: 4 additions & 3 deletions backend/src/routes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::collections::HashMap;

use diesel::{self, prelude::*};
use fxhash::FxHashMap;
use itertools::Itertools;
use rocket::serde::json::Json;

Expand Down Expand Up @@ -33,6 +32,8 @@ pub use self::{looper_preset::*, midi_composition::*, remote_samples::*};
pub mod login;
mod wavetable_preset;
pub use self::wavetable_preset::*;
mod subgraph_preset;
pub use self::subgraph_preset::*;

#[get("/")]
pub fn index() -> &'static str { "Application successfully started!" }
Expand Down Expand Up @@ -289,7 +290,7 @@ pub async fn get_synth_presets(
)?;

// build a mapping of voice preset id to voice preset
let mut voice_presets_by_id: HashMap<i64, SynthVoicePresetEntry> = HashMap::new();
let mut voice_presets_by_id: FxHashMap<i64, SynthVoicePresetEntry> = FxHashMap::default();
for (id_, title_, description_, body_, user_id_) in voice_presets_ {
let body_ = serde_json::from_str(&body_).map_err(|err| -> String {
error!("Error parsing voice preset entry stored in DB: {:?}", err);
Expand Down
Loading

0 comments on commit eee5198

Please sign in to comment.