VoiceLine.h header

#include <ew/core/VoiceLine.h>

Namespace ew::core

VoiceLine struct

struct ew::core::VoiceLine

A line of dialogue to be voiced, and the takes recorded against it.

Members

ew::core::foundation::ContentId ew::core::voice::VoiceLine::id

Stable identity.

ew::core::foundation::ContentId ew::core::voice::VoiceLine::speakerId

The character who speaks it, by entity id. Null for narration, which is a real case rather than missing data.

QString ew::core::voice::VoiceLine::text

The line as it currently reads. The truth every take is measured against.

ew::core::foundation::ContentId ew::core::voice::VoiceLine::sourceId

The object this line was taken from – a branching scene, a document – so the line list can send a director back to the script. Null for a line typed straight into the line list.

ew::core::foundation::BranchNodeId ew::core::voice::VoiceLine::sourceNodeId

The dialogue node this line was taken from, for a line pulled out of a branching scene; null for one typed straight in or taken from a document.

This is the line's identity across edits, and the reason it is here. A take, a casting decision and an approved translation are all attached to a line that outlives every rewrite of its words, so re-pulling a scene has to recognise the same line however the text has changed. Matching on the text instead loses all of it the moment somebody fixes a typo.

A rewrite is not thereby hidden: the take carries a digest of the words it was recorded against, so it shows as RecordingStatus::NeedsPickup – marked rather than thrown away. A node deleted and recreated gets a new id and is correctly a new line.

std::vector<VoiceTake> ew::core::voice::VoiceLine::takes

The takes, in the order they were recorded.

VoiceTake struct

struct ew::core::VoiceTake

One recorded attempt at a line.

A take points at a Media asset rather than carrying audio itself: MediaKind::Audio and its assetPath already exist, and a second way to hold a sound file would be a second thing to keep working.

Members

ew::core::foundation::ContentId ew::core::voice::VoiceTake::mediaId

The audio asset. Null while a take is logged but its file has not been imported yet, which is a real state on a recording day.

QString ew::core::voice::VoiceTake::recordedDigest

The digest of the line text AS IT READ WHEN THIS TAKE WAS RECORDED.

This is what makes a pickup detectable. The actor read a particular sentence; if the writer later rewrites it, the recording no longer matches the script, and nothing else in the project would notice. Stored per take rather than per line because two takes of the same line can predate and postdate an edit.

int ew::core::voice::VoiceTake::number = 1

The take number the studio used, as it appears on the recording sheet.

bool ew::core::voice::VoiceTake::selected = false

Whether this is the take chosen for use. At most one per line, and the editor enforces it.

QString ew::core::voice::VoiceTake::note

The director's note ("warmer", "too fast"), free text.

Enumerations

enum class RecordingStatus { NotRecorded, Recorded, Approved, NeedsPickup }

Where a voice line stands.

Derived, never stored – the same rule K1 established for translation status, and for the same reason: a stored status is a second copy of the truth that drifts from it silently. The script is the truth; this reports on it.

Functions

std::optional< RecordingStatus > ew::core::voice::recordingStatusFromToken(QStringView token)

Parses a recording status from its token; std::nullopt if unrecognized or absent.

Nothing in the product parses one today – a status is derived from the takes and the line text, so it could not be applied from a file even if something read one back. It is kept for two reasons that are checkable rather than aspirational: it is the inverse the round-trip test asserts toToken against, and tools/ci/check-enum-tables.ps1 only examines enums whose header declares a fromToken, so removing it would silently drop RecordingStatus from the check that every enumerator has a token – at the same moment those tokens became an exported contract.

const VoiceTake * ew::core::voice::selectedTake(const VoiceLine &line)

The take chosen for line, or null when none is.

RecordingStatus ew::core::voice::statusFor(const VoiceLine &line)

Where line stands, computed from its takes and its current text.

bool ew::core::voice::takeIsStale(const VoiceLine &line, const VoiceTake &take)

Whether take was recorded against wording line no longer has.

The one place the digest comparison lives. statusFor asks it about the CHOSEN take to decide whether the whole line needs a pickup; the Voice panel asks it about EACH take to mark which one is stale, since a status of "needs pickup" with nothing on screen saying which take is at fault leaves the reader hunting.

Those are two questions, but they must not be two RULES. This subsystem has already shipped that bug once: the Voice panel and the studio line list each had their own status labeller and had drifted to different words for the same state (B-03). A second copy of a comparison agrees with the first only until somebody edits one.

QString ew::core::voice::toToken(RecordingStatus status)

Returns the stable serialization token for status.

Status is derived rather than stored, so this is not for round-tripping through a project file. Its consumer is the studio line list (ew::app::voice::toLineListCsv), whose "Status token" column carries this while the neighbouring "Status" column carries the translated label. The token is what downstream tooling may key on, so rewording one is a breaking change in a way that rewording the label is not.