diff --git a/app/static/js/services/aiGateway.js b/app/static/js/services/aiGateway.js index 80737b8..99d22c1 100644 --- a/app/static/js/services/aiGateway.js +++ b/app/static/js/services/aiGateway.js @@ -120,29 +120,111 @@ const AIGateway = (function() { } }; + const REARRANGE_SCENARIOS = [ + { + id: 'arpeggio', + keywords: ['arpeggio', 'arp', 'broken chord', 'broken chords', 'shimmering sequence'], + description: 'Arpeggio variation', + technique: 'Subdivide sustained chord notes (longer than 0.5 beats) into sequential 0.25-beat arpeggiated interval steps using octave/triad jumps. Create a driving, shimmering sequence.' + }, + { + id: 'harmonies', + keywords: ['harmony', 'harmonize', 'harmonies', '3rd', '3rds', 'duet', 'chord voicing', 'voicing'], + description: 'Add parallel harmonies', + technique: 'For each source pitch P, insert parallel harmonizing notes at pitch P+3 or P+4 (Major/Minor 3rds) matching the same start_beat. Create richer, fuller texture resembling two instruments playing in duet.' + }, + { + id: 'syncopation', + keywords: ['syncopation', 'syncopate', 'syncopated', 'off-beat', 'off beat', 'funk', 'latin', 'rnb', 'r&b'], + description: 'Syncopation / off-beat feel', + technique: 'Shift start_beat alignment off strong beats (0.0, 1.0, 2.0, 3.0) onto off-beats (0.5, 1.5, 2.5, 3.5). Create a rhythmic, syncopated feel.' + }, + { + id: 'walking_bass', + keywords: ['walking bass', 'bassline', 'bass line', 'walking'], + description: 'Walking bass line', + technique: 'Lower pitches to bass register (36-48 range). Build continuous quarter-note steps (1.0 beat duration) following the chord outline. Create an improvisational walking bassline.' + }, + { + id: 'jazz_swing', + keywords: ['jazz', 'swing', 'bebop', 'bop', 'jazz swing'], + description: 'Jazz Swing style', + technique: 'Apply Jazz Swing characteristics: off-beat syncopation, 7th/9th chord extensions, rhythmic variations, and swung eighth notes (triplet feel). Preserve the core melodic outline while adding jazz harmony.' + }, + { + id: 'synthwave', + keywords: ['synthwave', '80s', 'retro', 'synth wave', 'outrun', 'retrowave'], + description: 'Synthwave / 80s style', + technique: 'Transform into 80s Synthwave style: driving 8th-note bass arpeggios, gated reverb snare, analog synth lead with portamento, and pulsating chord pads. Use octave jumps in the bass.' + }, + { + id: 'cinematic', + keywords: ['cinematic', 'orchestral', 'epic', 'film score', 'movie', 'hollywood', 'symphonic'], + description: 'Cinematic Orchestral style', + technique: 'Transform into dramatic Cinematic Orchestral style: swelling dynamics, brass stabs on downbeats, string ostinato patterns, taiko percussion hits on strong beats. Layer multiple octaves for epic width.' + }, + { + id: 'simplify', + keywords: ['simplify', 'minimal', 'strip', 'downbeat', 'reduce', 'sparse'], + description: 'Simplify / strip down', + technique: 'Keep only the downbeat notes (start_beat at 0.0, 1.0, 2.0, 3.0, etc.). Strip out embellishments, passing tones, and grace notes. Reduce chord voicings to root and 5th only.' + }, + { + id: 'passing_tones', + keywords: ['passing tone', 'chromatic', 'chromaticism', 'smooth', 'approach note'], + description: 'Add passing tones / chromaticism', + technique: 'Insert passing tones and chromatic approach notes between chord tones to smooth out the melodic progression. Use half-step and whole-step approach notes targeting chord tones on strong beats.' + } + ]; + + function detectRearrangeScenario(prompt) { + if (!prompt) return null; + const lower = prompt.toLowerCase(); + const matched = []; + for (const sc of REARRANGE_SCENARIOS) { + const hits = sc.keywords.filter(kw => lower.includes(kw.toLowerCase())); + if (hits.length > 0) { + matched.push({ scenario: sc, hitCount: hits.length }); + } + } + matched.sort((a, b) => b.hitCount - a.hitCount); + return matched.length > 0 ? matched[0].scenario : null; + } + function buildRearrangeMessage(prompt, sourceContext) { const notesJson = JSON.stringify(sourceContext.notes, null, 2); + const scenario = detectRearrangeScenario(prompt); + const techniqueSection = scenario + ? `\nDETECTED SCENARIO: ${scenario.description}\nSPECIFIC TECHNIQUE REQUIRED: ${scenario.technique}\n` + : ''; + + const rules = [ + '1. Use the rearrange_midi_melody tool to return the rearranged notes.', + '2. PRESERVE the core melodic outline and overall structure unless the user explicitly asks for a complete transformation.', + '3. Keep the total duration (' + sourceContext.total_beats + ' beats) the same unless user requests a different length.', + '4. The rearranged_notes array MUST contain notes with pitch (0-127), start_beat (0.0 to ' + sourceContext.total_beats + '.0), duration_beats, and velocity (0.0-1.0).', + '5. Start beats should remain within 0-' + sourceContext.total_beats + ' range.', + '6. You may add, remove, or modify notes to achieve the requested style.', + '7. ALWAYS return a rearrange_title describing what was created (e.g. "Jazz Swing Variation of Piano Lead").' + ]; + return [ - { role: 'system', content: `You are a professional music arranger. Your task is to rearrange/re-harmonize the provided MIDI notes based on the user's request. + { role: 'system', content: `You are a professional Music Composer & Arranger. SOURCE MIDI CONTEXT: - Track: ${sourceContext.track_name} - Item: ${sourceContext.item_name} - Duration: ${sourceContext.duration_bars} bars (${sourceContext.total_beats} beats) - BPM: ${sourceContext.bpm} +- Time Signature: 4/4 - Total notes: ${sourceContext.total_notes} -SOURCE NOTES (pitch, note_name, start_beat, duration_beats, velocity): +ORIGINAL MELODY NOTES (pitch, note_name, start_beat, duration_beats, velocity): ${notesJson} - -RULES: -1. Use the rearrange_midi_melody tool to return the rearranged notes. -2. PRESERVE the core melodic outline and overall structure unless the user explicitly asks for a complete transformation. -3. Keep the total duration (${sourceContext.total_beats} beats) the same unless user requests a different length. -4. The rearranged_notes array MUST contain notes with pitch (0-127), start_beat (0.0 to ${sourceContext.total_beats}.0), duration_beats, and velocity (0.0-1.0). -5. Start beats should remain within 0-${sourceContext.total_beats} range. -6. You may add, remove, or modify notes to achieve the requested style.` }, - { role: 'user', content: `User request: ${prompt}\n\nRearrange the source MIDI notes above according to this request. Return the result via the rearrange_midi_melody tool.` } +${techniqueSection} +STRICT REARRANGE RULES: +${rules.join('\n')}` }, + { role: 'user', content: `${prompt}\n\nRearrange the source MIDI notes above according to this request. Return the result via the rearrange_midi_melody tool.` } ]; } @@ -362,6 +444,8 @@ Ví dụ: "Hãy chọn và copy từ bar 4 đến bar 12 của track 1 sau đó return { DEFAULT_TOOLS, REARRANGE_TOOL_SPEC, + REARRANGE_SCENARIOS, + detectRearrangeScenario, buildRearrangeMessage, callLLM, extractFunctionCalls, diff --git a/app/static/js/services/promptTemplateManager.js b/app/static/js/services/promptTemplateManager.js index fe950d8..6bd1e73 100644 --- a/app/static/js/services/promptTemplateManager.js +++ b/app/static/js/services/promptTemplateManager.js @@ -38,6 +38,124 @@ const PromptTemplateManager = (function() { is_user_defined: false, is_favorite: false, created_at: "2026-07-23T16:00:00Z" + }, + // ── Rearrange & Variation Scenarios ── + { + id: "preset_rearrange_arpeggio", + name: "Arpeggio Variation", + keywords: ["arpeggio", "arp", "broken chord", "arpeggiate"], + category: "Rearrange / Variation", + default_bars: 8, + default_bpm: 120, + default_scale: "C Major", + system_instruction_template: "You are a professional arranger. Rearrange the source MIDI notes into an arpeggiated variation. Subdivide sustained chord notes into sequential 0.25-beat arpeggiated steps with octave/triad jumps. Create a driving, shimmering sequence while preserving the underlying chord progression.", + is_user_defined: false, + is_favorite: false, + created_at: "2026-07-28T11:00:00Z" + }, + { + id: "preset_rearrange_harmonies", + name: "Add 3rd/4th Harmonies", + keywords: ["harmony", "harmonize", "harmonies", "3rd", "duet", "chord voicing"], + category: "Rearrange / Variation", + default_bars: 8, + default_bpm: 120, + default_scale: "C Major", + system_instruction_template: "You are a professional arranger. Add parallel harmonizing notes to the source melody. For each source pitch P, insert notes at P+3 or P+4 (Major/Minor 3rds) at the same start_beat. Create a richer, fuller texture resembling two instruments playing in duet.", + is_user_defined: false, + is_favorite: false, + created_at: "2026-07-28T11:00:00Z" + }, + { + id: "preset_rearrange_syncopation", + name: "Syncopation / Off-beat", + keywords: ["syncopation", "syncopate", "syncopated", "off-beat", "off beat", "funk", "latin"], + category: "Rearrange / Variation", + default_bars: 8, + default_bpm: 120, + default_scale: "C Major", + system_instruction_template: "You are a professional arranger. Apply syncopation to the source melody. Shift start_beat alignment off strong beats (0.0, 1.0, 2.0, 3.0) onto off-beats (0.5, 1.5, 2.5, 3.5). Preserve the core melodic outline while creating a rhythmic, syncopated feel.", + is_user_defined: false, + is_favorite: false, + created_at: "2026-07-28T11:00:00Z" + }, + { + id: "preset_rearrange_walking_bass", + name: "Walking Bass Line", + keywords: ["walking bass", "bassline", "walking"], + category: "Rearrange / Variation", + default_bars: 8, + default_bpm: 120, + default_scale: "C Major", + system_instruction_template: "You are a professional arranger. Transform the source melody into a walking bass line. Lower pitches to bass register (36-48 range). Build continuous quarter-note steps following the chord outline. Create an improvisational walking bassline matching the original chord progression.", + is_user_defined: false, + is_favorite: false, + created_at: "2026-07-28T11:00:00Z" + }, + { + id: "preset_rearrange_jazz", + name: "Jazz Swing Rearrangement", + keywords: ["jazz", "swing", "jazz swing", "bebop"], + category: "Rearrange / Variation", + default_bars: 8, + default_bpm: 120, + default_scale: "C Major", + system_instruction_template: "You are a professional Jazz arranger. Rearrange the source melody into a rhythmic Jazz Swing style. Apply: off-beat syncopation, 7th/9th chord extensions, swung eighth notes (triplet feel), and rhythmic variations while preserving the core melodic outline.", + is_user_defined: false, + is_favorite: false, + created_at: "2026-07-28T11:00:00Z" + }, + { + id: "preset_rearrange_synthwave", + name: "Synthwave / 80s Style", + keywords: ["synthwave", "80s", "retro", "synth wave", "retrowave", "outrun"], + category: "Rearrange / Variation", + default_bars: 8, + default_bpm: 120, + default_scale: "A Minor", + system_instruction_template: "You are a Synthwave producer. Transform the source melody into an 80s Synthwave style. Apply: driving 8th-note bass arpeggios, analog synth lead with portamento, pulsating chord pads, and octave jumps in the bass.", + is_user_defined: false, + is_favorite: false, + created_at: "2026-07-28T11:00:00Z" + }, + { + id: "preset_rearrange_cinematic", + name: "Cinematic Orchestral", + keywords: ["cinematic", "orchestral", "epic", "film score", "symphonic", "hollywood"], + category: "Rearrange / Variation", + default_bars: 8, + default_bpm: 130, + default_scale: "C Minor", + system_instruction_template: "You are a cinematic composer. Transform the source melody into a dramatic Cinematic Orchestral arrangement. Apply: swelling dynamics, brass stabs on downbeats, string ostinato patterns, taiko percussion hits. Layer multiple octaves for epic width.", + is_user_defined: false, + is_favorite: false, + created_at: "2026-07-28T11:00:00Z" + }, + { + id: "preset_rearrange_simplify", + name: "Simplify / Strip Down", + keywords: ["simplify", "minimal", "strip down", "sparse", "reduce"], + category: "Rearrange / Variation", + default_bars: 8, + default_bpm: 120, + default_scale: "C Major", + system_instruction_template: "You are a professional arranger. Simplify the source melody: keep only downbeat notes (start_beat at 0.0, 1.0, 2.0, 3.0). Strip out embellishments, passing tones, and grace notes. Reduce chord voicings to root and 5th only.", + is_user_defined: false, + is_favorite: false, + created_at: "2026-07-28T11:00:00Z" + }, + { + id: "preset_rearrange_chromatic", + name: "Passing Tones & Chromaticism", + keywords: ["passing tone", "chromatic", "chromaticism", "smooth", "approach note"], + category: "Rearrange / Variation", + default_bars: 8, + default_bpm: 120, + default_scale: "C Major", + system_instruction_template: "You are a professional arranger. Add passing tones and chromatic approach notes to the source melody. Insert half-step and whole-step approach notes targeting chord tones on strong beats. Smooth out the melodic progression while preserving the harmonic framework.", + is_user_defined: false, + is_favorite: false, + created_at: "2026-07-28T11:00:00Z" } ]; diff --git a/wiki.md b/wiki.md index bdd0e9b..29fe446 100644 --- a/wiki.md +++ b/wiki.md @@ -539,6 +539,12 @@ - **Ghi chú/Test (nếu có):** `npm run build` pass. Ctrl+Click+Drag section/MIDI item → copy đến vị trí mới, item không bị selected. --- +### [2026-07-28 11:02] Task: AI Rearrange Scenario Detection (spec 45_SCENARIAO_AI.md) +- **Tóm tắt thay đổi:** Thêm 9 rearrange scenarios vào `aiGateway.js` (arpeggio, harmonies, syncopation, walking bass, jazz swing, synthwave, cinematic, simplify, passing tones) với keyword mapping → `detectRearrangeScenario()`. `buildRearrangeMessage` tự động inject specific technique rules dựa trên scenario detect được. Thêm 9 rearrange presets vào `promptTemplateManager.js` với category "Rearrange / Variation". +- **Các file ảnh hưởng:** `app/static/js/services/aiGateway.js`, `app/static/js/services/promptTemplateManager.js` +- **Ghi chú/Test (nếu có):** `node --check` pass. `npx babel` compile pass. Preset Manager hiển thị 12 presets (3 cũ + 9 rearrange). Gõ "arpeggio" → AI nhận dạng scenario → inject technique "Subdivide sustained chord notes into 0.25-beat arpeggiated steps". +--- + ### [2026-07-28 10:52] Task: MIDI Rearrange Feature (spec 41_AIMIDI_FIX.md) - **Tóm tắt thay đổi:** Tạo `midiExtractor.js` — `extractSelectedMIDIContext` trích xuất notes + note names từ selected MIDI item. Thêm `REARRANGE_TOOL_SPEC` + `buildRearrangeMessage` trong `aiGateway.js`. `handleAISend` phát hiện selected MIDI item → dùng rearrange flow riêng (source notes → AI → `rearrange_midi_melody` → A/B track bên dưới source). Handler `rearrangeMidiMelody` tạo track `[AI Rearrange] ` + MIDI item ngay dưới source track. - **Các file ảnh hưởng:** `app/static/js/services/midiExtractor.js` (NEW), `app/static/js/services/aiGateway.js`, `app/static/js/services/dawCommandDispatcher.js`, `app/static/js/app.jsx`, `app/templates/index.html`