Model emotional arc and production directions
This commit is contained in:
@@ -186,6 +186,46 @@ public extension SongProject {
|
|||||||
maqam = ManualAutoValue(mode: .auto)
|
maqam = ManualAutoValue(mode: .auto)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func emotionalArcPoint(for pointID: String) -> EmotionalArcPoint? {
|
||||||
|
emotionalArc.first { $0.id == pointID }
|
||||||
|
}
|
||||||
|
|
||||||
|
func emotionalArcPoints(forSectionID sectionID: String) -> [EmotionalArcPoint] {
|
||||||
|
emotionalArc.filter { $0.sectionID == sectionID }
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func upsertEmotionalArcPoint(_ point: EmotionalArcPoint) {
|
||||||
|
if let index = emotionalArc.firstIndex(where: { $0.id == point.id }) {
|
||||||
|
emotionalArc[index] = point
|
||||||
|
} else {
|
||||||
|
emotionalArc.append(point)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func removeEmotionalArcPoint(id pointID: String) {
|
||||||
|
emotionalArc.removeAll { $0.id == pointID }
|
||||||
|
}
|
||||||
|
|
||||||
|
func productionDirection(for directionID: String) -> ProductionDirection? {
|
||||||
|
productionDirections.first { $0.id == directionID }
|
||||||
|
}
|
||||||
|
|
||||||
|
func productionDirections(forSectionID sectionID: String) -> [ProductionDirection] {
|
||||||
|
productionDirections.filter { $0.sectionID == sectionID }
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func upsertProductionDirection(_ direction: ProductionDirection) {
|
||||||
|
if let index = productionDirections.firstIndex(where: { $0.id == direction.id }) {
|
||||||
|
productionDirections[index] = direction
|
||||||
|
} else {
|
||||||
|
productionDirections.append(direction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func removeProductionDirection(id directionID: String) {
|
||||||
|
productionDirections.removeAll { $0.id == directionID }
|
||||||
|
}
|
||||||
|
|
||||||
var selectedInstrumentTracks: [InstrumentTrack] {
|
var selectedInstrumentTracks: [InstrumentTrack] {
|
||||||
instruments.filter(\.selected)
|
instruments.filter(\.selected)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -307,4 +307,62 @@ final class SongProjectModelTests: XCTestCase {
|
|||||||
XCTAssertEqual(project.scale, ManualAutoValue(mode: .manual, value: "minor"))
|
XCTAssertEqual(project.scale, ManualAutoValue(mode: .manual, value: "minor"))
|
||||||
XCTAssertEqual(project.maqam, ManualAutoValue(mode: .auto))
|
XCTAssertEqual(project.maqam, ManualAutoValue(mode: .auto))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testEmotionalArcAndProductionDirectionsAreModeled() {
|
||||||
|
var project = SongProject(
|
||||||
|
id: "project-9",
|
||||||
|
title: "Arc",
|
||||||
|
idea: "Build from intimate to explosive"
|
||||||
|
)
|
||||||
|
let verseArc = EmotionalArcPoint(
|
||||||
|
id: "arc-verse",
|
||||||
|
sectionID: "verse",
|
||||||
|
emotion: "intimate",
|
||||||
|
energy: 3,
|
||||||
|
direction: "restrained"
|
||||||
|
)
|
||||||
|
let chorusArc = EmotionalArcPoint(
|
||||||
|
id: "arc-chorus",
|
||||||
|
sectionID: "chorus",
|
||||||
|
emotion: "euphoric",
|
||||||
|
energy: 9,
|
||||||
|
direction: "wide and bright"
|
||||||
|
)
|
||||||
|
let verseProduction = ProductionDirection(
|
||||||
|
id: "prod-verse",
|
||||||
|
sectionID: "verse",
|
||||||
|
text: "keep drums minimal"
|
||||||
|
)
|
||||||
|
let chorusProduction = ProductionDirection(
|
||||||
|
id: "prod-chorus",
|
||||||
|
sectionID: "chorus",
|
||||||
|
text: "add layered harmonies and bigger percussion"
|
||||||
|
)
|
||||||
|
|
||||||
|
project.upsertEmotionalArcPoint(verseArc)
|
||||||
|
project.upsertEmotionalArcPoint(chorusArc)
|
||||||
|
project.upsertEmotionalArcPoint(
|
||||||
|
EmotionalArcPoint(
|
||||||
|
id: "arc-verse",
|
||||||
|
sectionID: "verse",
|
||||||
|
emotion: "focused",
|
||||||
|
energy: 4,
|
||||||
|
direction: "slightly warmer"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
project.upsertProductionDirection(verseProduction)
|
||||||
|
project.upsertProductionDirection(chorusProduction)
|
||||||
|
|
||||||
|
XCTAssertEqual(project.emotionalArc.count, 2)
|
||||||
|
XCTAssertEqual(project.emotionalArcPoint(for: "arc-verse")?.emotion, "focused")
|
||||||
|
XCTAssertEqual(project.emotionalArcPoints(forSectionID: "chorus"), [chorusArc])
|
||||||
|
XCTAssertEqual(project.productionDirections(forSectionID: "verse"), [verseProduction])
|
||||||
|
XCTAssertEqual(project.productionDirection(for: "prod-chorus"), chorusProduction)
|
||||||
|
|
||||||
|
project.removeEmotionalArcPoint(id: "arc-chorus")
|
||||||
|
project.removeProductionDirection(id: "prod-verse")
|
||||||
|
|
||||||
|
XCTAssertNil(project.emotionalArcPoint(for: "arc-chorus"))
|
||||||
|
XCTAssertNil(project.productionDirection(for: "prod-verse"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ requirement is missing and blocks implementation, record it in
|
|||||||
- [x] Model multiple vocalists and section assignments.
|
- [x] Model multiple vocalists and section assignments.
|
||||||
- [x] Model language/dialect and Arabic pronunciation settings.
|
- [x] Model language/dialect and Arabic pronunciation settings.
|
||||||
- [x] Model BPM, key, scale and maqam with Manual/Auto state.
|
- [x] Model BPM, key, scale and maqam with Manual/Auto state.
|
||||||
- [ ] Model emotional arc and production directions.
|
- [x] Model emotional arc and production directions.
|
||||||
- [ ] Add serialization tests.
|
- [ ] Add serialization tests.
|
||||||
|
|
||||||
## Phase 2 --- Project Persistence & Versions
|
## Phase 2 --- Project Persistence & Versions
|
||||||
|
|||||||
Reference in New Issue
Block a user