Skip to content

Commit 11c74b0

Browse files
committed
sdk+skills
1 parent e09a067 commit 11c74b0

46 files changed

Lines changed: 3496 additions & 235 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

SDK/dotnet/src/ManagedCode.Tps/Internal/CompiledScriptNormalizer.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ private static CompiledSegment NormalizeSegment(
3636
TargetWpm = segment.TargetWpm,
3737
Emotion = segment.Emotion,
3838
Speaker = segment.Speaker,
39+
Archetype = segment.Archetype,
3940
Timing = segment.Timing,
4041
BackgroundColor = segment.BackgroundColor,
4142
TextColor = segment.TextColor,
@@ -63,6 +64,7 @@ private static CompiledBlock NormalizeBlock(
6364
TargetWpm = block.TargetWpm,
6465
Emotion = block.Emotion,
6566
Speaker = block.Speaker,
67+
Archetype = block.Archetype,
6668
IsImplicit = block.IsImplicit,
6769
StartWordIndex = block.StartWordIndex,
6870
EndWordIndex = block.EndWordIndex,

SDK/dotnet/src/ManagedCode.Tps/Internal/TpsContentCompiler.cs

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,41 @@ private static bool HandleClosingTag(
253253
return new InlineScope(tag.Name, DeliveryMode: tag.Name);
254254
}
255255

256+
if (TpsSpec.ArticulationStyles.Contains(tag.Name, StringComparer.OrdinalIgnoreCase))
257+
{
258+
return new InlineScope(tag.Name, ArticulationStyle: tag.Name);
259+
}
260+
261+
if (string.Equals(tag.Name, TpsSpec.Tags.Energy, StringComparison.Ordinal))
262+
{
263+
if (tag.Argument is null || !int.TryParse(tag.Argument, out var energyLevel) || energyLevel < TpsSpec.EnergyLevelMin || energyLevel > TpsSpec.EnergyLevelMax)
264+
{
265+
diagnostics.Add(TpsSupport.CreateDiagnostic(
266+
TpsSpec.DiagnosticCodes.InvalidEnergyLevel,
267+
$"Energy level must be an integer between {TpsSpec.EnergyLevelMin} and {TpsSpec.EnergyLevelMax}.",
268+
absoluteOffset,
269+
absoluteOffset + tag.Raw.Length,
270+
lineStarts));
271+
return null;
272+
}
273+
return new InlineScope(tag.Name, EnergyLevel: energyLevel);
274+
}
275+
276+
if (string.Equals(tag.Name, TpsSpec.Tags.Melody, StringComparison.Ordinal))
277+
{
278+
if (tag.Argument is null || !int.TryParse(tag.Argument, out var melodyLevel) || melodyLevel < TpsSpec.MelodyLevelMin || melodyLevel > TpsSpec.MelodyLevelMax)
279+
{
280+
diagnostics.Add(TpsSupport.CreateDiagnostic(
281+
TpsSpec.DiagnosticCodes.InvalidMelodyLevel,
282+
$"Melody level must be an integer between {TpsSpec.MelodyLevelMin} and {TpsSpec.MelodyLevelMax}.",
283+
absoluteOffset,
284+
absoluteOffset + tag.Raw.Length,
285+
lineStarts));
286+
return null;
287+
}
288+
return new InlineScope(tag.Name, MelodyLevel: melodyLevel);
289+
}
290+
256291
if (TpsSpec.Emotions.Contains(tag.Name, StringComparer.OrdinalIgnoreCase))
257292
{
258293
return new InlineScope(tag.Name, InlineEmotion: tag.Name);
@@ -465,6 +500,9 @@ private static ActiveInlineState ResolveActiveState(List<InlineScope> scopes, In
465500
string? inlineEmotion = null;
466501
string? volumeLevel = null;
467502
string? deliveryMode = null;
503+
string? articulationStyle = (string?)null;
504+
int? energyLevel = null;
505+
int? melodyLevel = null;
468506
string? phoneticGuide = null;
469507
string? pronunciationGuide = null;
470508
string? stressGuide = null;
@@ -502,6 +540,9 @@ private static ActiveInlineState ResolveActiveState(List<InlineScope> scopes, In
502540

503541
volumeLevel = scope.VolumeLevel ?? volumeLevel;
504542
deliveryMode = scope.DeliveryMode ?? deliveryMode;
543+
articulationStyle = scope.ArticulationStyle ?? articulationStyle;
544+
if (scope.EnergyLevel is int scopeEnergy) energyLevel = scopeEnergy;
545+
if (scope.MelodyLevel is int scopeMelody) melodyLevel = scopeMelody;
505546
phoneticGuide = scope.PhoneticGuide ?? phoneticGuide;
506547
pronunciationGuide = scope.PronunciationGuide ?? pronunciationGuide;
507548
stressGuide = scope.StressGuide ?? stressGuide;
@@ -516,6 +557,9 @@ private static ActiveInlineState ResolveActiveState(List<InlineScope> scopes, In
516557
highlight,
517558
volumeLevel,
518559
deliveryMode,
560+
articulationStyle,
561+
energyLevel,
562+
melodyLevel,
519563
phoneticGuide,
520564
pronunciationGuide,
521565
stressGuide,
@@ -530,7 +574,7 @@ private static bool IsSpokenWord(WordSeed word) =>
530574
string.Equals(word.Kind, "word", StringComparison.Ordinal) && !string.IsNullOrEmpty(word.CleanText);
531575
}
532576

533-
internal sealed record InheritedFormattingState(int TargetWpm, string Emotion, string? Speaker, IReadOnlyDictionary<string, int> SpeedOffsets);
577+
internal sealed record InheritedFormattingState(int TargetWpm, string Emotion, string? Speaker, IReadOnlyDictionary<string, int> SpeedOffsets, string? Archetype = null);
534578

535579
internal sealed record ContentCompilationResult(List<WordSeed> Words, List<PhraseSeed> Phrases);
536580

@@ -558,6 +602,9 @@ internal sealed record InlineScope(
558602
string? InlineEmotion = null,
559603
string? VolumeLevel = null,
560604
string? DeliveryMode = null,
605+
string? ArticulationStyle = null,
606+
int? EnergyLevel = null,
607+
int? MelodyLevel = null,
561608
string? PhoneticGuide = null,
562609
string? PronunciationGuide = null,
563610
string? StressGuide = null,
@@ -574,6 +621,9 @@ internal sealed record ActiveInlineState(
574621
bool Highlight,
575622
string? VolumeLevel,
576623
string? DeliveryMode,
624+
string? ArticulationStyle,
625+
int? EnergyLevel,
626+
int? MelodyLevel,
577627
string? PhoneticGuide,
578628
string? PronunciationGuide,
579629
string? StressGuide,
@@ -601,6 +651,12 @@ internal sealed class TokenAccumulator
601651

602652
public string? DeliveryMode { get; private set; }
603653

654+
public string? ArticulationStyle { get; private set; }
655+
656+
public int? EnergyLevel { get; private set; }
657+
658+
public int? MelodyLevel { get; private set; }
659+
604660
public string? PhoneticGuide { get; private set; }
605661

606662
public string? PronunciationGuide { get; private set; }
@@ -625,6 +681,9 @@ public void Apply(ActiveInlineState state, char character)
625681
InlineEmotionHint = state.InlineEmotion ?? InlineEmotionHint;
626682
VolumeLevel = state.VolumeLevel ?? VolumeLevel;
627683
DeliveryMode = state.DeliveryMode ?? DeliveryMode;
684+
ArticulationStyle = state.ArticulationStyle ?? ArticulationStyle;
685+
if (state.EnergyLevel is int stateEnergy) EnergyLevel = stateEnergy;
686+
if (state.MelodyLevel is int stateMelody) MelodyLevel = stateMelody;
628687
PhoneticGuide = state.PhoneticGuide ?? PhoneticGuide;
629688
PronunciationGuide = state.PronunciationGuide ?? PronunciationGuide;
630689
StressGuide = state.StressGuide ?? StressGuide;
@@ -658,6 +717,9 @@ public WordMetadata BuildWordMetadata(int inheritedWpm)
658717
InlineEmotionHint = InlineEmotionHint,
659718
VolumeLevel = VolumeLevel,
660719
DeliveryMode = DeliveryMode,
720+
ArticulationStyle = ArticulationStyle,
721+
EnergyLevel = EnergyLevel,
722+
MelodyLevel = MelodyLevel,
661723
PhoneticGuide = PhoneticGuide,
662724
PronunciationGuide = PronunciationGuide,
663725
StressText = _stressText.Length > 0 ? _stressText.ToString() : null,

SDK/dotnet/src/ManagedCode.Tps/Internal/TpsParser.cs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,24 @@ private static void PushContentLine(
314314
continue;
315315
}
316316

317+
if (normalized.StartsWith(TpsSpec.ArchetypePrefix, StringComparison.OrdinalIgnoreCase))
318+
{
319+
var archetypeValue = TpsSupport.NormalizeValue(normalized[TpsSpec.ArchetypePrefix.Length..]);
320+
if (archetypeValue is not null && TpsSpec.Archetypes.Contains(archetypeValue, StringComparer.OrdinalIgnoreCase))
321+
{
322+
header = header with { Archetype = archetypeValue.ToLowerInvariant() };
323+
}
324+
else
325+
{
326+
diagnostics.Add(TpsSupport.CreateDiagnostic(
327+
TpsSpec.DiagnosticCodes.UnknownArchetype,
328+
$"Archetype '{archetypeValue ?? ""}' is not a known vocal archetype.",
329+
tokenStart, tokenEnd, lineStarts,
330+
"Use one of: Friend, Motivator, Educator, Coach, Storyteller, Entertainer."));
331+
}
332+
continue;
333+
}
334+
317335
if (TpsSupport.IsTimingToken(normalized))
318336
{
319337
header = header with { Timing = normalized };
@@ -380,14 +398,17 @@ private static ParsedSegmentInternal CreateSegment(ParsedHeader header, IReadOnl
380398
var emotion = TpsSupport.ResolveEmotion(header.Emotion);
381399
var palette = TpsSupport.ResolvePalette(emotion);
382400
var blocks = new List<TpsBlock>();
401+
var targetWpm = header.TargetWpm
402+
?? (header.Archetype is not null && TpsSpec.ArchetypeRecommendedWpm.TryGetValue(header.Archetype, out var archetypeWpm) ? archetypeWpm : TpsSupport.ResolveBaseWpm(metadata));
383403
return new ParsedSegmentInternal(
384404
new TpsSegment
385405
{
386406
Id = $"segment-{index}",
387407
Name = header.Name,
388-
TargetWpm = header.TargetWpm ?? TpsSupport.ResolveBaseWpm(metadata),
408+
TargetWpm = targetWpm,
389409
Emotion = emotion,
390410
Speaker = header.Speaker,
411+
Archetype = header.Archetype,
391412
Timing = header.Timing,
392413
BackgroundColor = palette.Background,
393414
TextColor = palette.Text,
@@ -411,7 +432,8 @@ private static ParsedBlockInternal CreateBlock(ParsedHeader header, int blockInd
411432
Name = header.Name,
412433
TargetWpm = header.TargetWpm,
413434
Emotion = header.Emotion,
414-
Speaker = header.Speaker
435+
Speaker = header.Speaker,
436+
Archetype = header.Archetype
415437
});
416438

417439
private static ContentSection? CreateContentSection(IReadOnlyList<LineRecord> lines)
@@ -465,6 +487,7 @@ private static TpsSegment FreezeSegment(TpsSegment segment) =>
465487
TargetWpm = segment.TargetWpm,
466488
Emotion = segment.Emotion,
467489
Speaker = segment.Speaker,
490+
Archetype = segment.Archetype,
468491
Timing = segment.Timing,
469492
BackgroundColor = segment.BackgroundColor,
470493
TextColor = segment.TextColor,
@@ -481,7 +504,8 @@ private static TpsBlock FreezeBlock(TpsBlock block) =>
481504
Content = block.Content,
482505
TargetWpm = block.TargetWpm,
483506
Emotion = block.Emotion,
484-
Speaker = block.Speaker
507+
Speaker = block.Speaker,
508+
Archetype = block.Archetype
485509
};
486510

487511
private static (int Index, int Length)? FindFrontMatterClosing(string source)
@@ -561,7 +585,8 @@ internal sealed record ParsedHeader(
561585
int? TargetWpm = null,
562586
string? Emotion = null,
563587
string? Timing = null,
564-
string? Speaker = null);
588+
string? Speaker = null,
589+
string? Archetype = null);
565590

566591
internal sealed record LineRecord(string Text, int StartOffset);
567592

SDK/dotnet/src/ManagedCode.Tps/Models/Contracts.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ public sealed class TpsSegment
9494
[JsonPropertyName("speaker")]
9595
public string? Speaker { get; init; }
9696

97+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
98+
[JsonPropertyName("archetype")]
99+
public string? Archetype { get; init; }
100+
97101
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
98102
[JsonPropertyName("timing")]
99103
public string? Timing { get; init; }
@@ -139,6 +143,10 @@ public sealed class TpsBlock
139143
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
140144
[JsonPropertyName("speaker")]
141145
public string? Speaker { get; init; }
146+
147+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
148+
[JsonPropertyName("archetype")]
149+
public string? Archetype { get; init; }
142150
}
143151

144152
public sealed record WordMetadata
@@ -185,6 +193,18 @@ public sealed record WordMetadata
185193
[JsonPropertyName("deliveryMode")]
186194
public string? DeliveryMode { get; init; }
187195

196+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
197+
[JsonPropertyName("articulationStyle")]
198+
public string? ArticulationStyle { get; init; }
199+
200+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
201+
[JsonPropertyName("energyLevel")]
202+
public int? EnergyLevel { get; init; }
203+
204+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
205+
[JsonPropertyName("melodyLevel")]
206+
public int? MelodyLevel { get; init; }
207+
188208
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
189209
[JsonPropertyName("phoneticGuide")]
190210
public string? PhoneticGuide { get; init; }
@@ -306,6 +326,10 @@ public sealed class CompiledBlock : ICompiledTimeRange
306326
[JsonPropertyName("speaker")]
307327
public string? Speaker { get; init; }
308328

329+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
330+
[JsonPropertyName("archetype")]
331+
public string? Archetype { get; init; }
332+
309333
[JsonPropertyName("isImplicit")]
310334
public required bool IsImplicit { get; init; }
311335

@@ -374,6 +398,10 @@ public sealed class CompiledSegment : ICompiledTimeRange
374398
[JsonPropertyName("speaker")]
375399
public string? Speaker { get; init; }
376400

401+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
402+
[JsonPropertyName("archetype")]
403+
public string? Archetype { get; init; }
404+
377405
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
378406
[JsonPropertyName("timing")]
379407
public string? Timing { get; init; }

SDK/dotnet/src/ManagedCode.Tps/TpsRuntime.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ private static SegmentCandidate CompileSegment(
5858
DocumentAnalysis analysis)
5959
{
6060
var segmentEmotion = TpsSupport.ResolveEmotion(parsedSegment.Segment.Emotion);
61-
var inherited = new InheritedFormattingState(parsedSegment.Segment.TargetWpm ?? baseWpm, segmentEmotion, parsedSegment.Segment.Speaker, speedOffsets);
61+
var inherited = new InheritedFormattingState(parsedSegment.Segment.TargetWpm ?? baseWpm, segmentEmotion, parsedSegment.Segment.Speaker, speedOffsets, parsedSegment.Segment.Archetype);
6262
var blocks = BuildBlocks(parsedSegment).Select(entry => CompileBlock(entry, inherited, analysis)).ToList();
6363
var compiledBlocks = new List<CompiledBlock>();
6464
var compiledWords = new List<CompiledWord>();
@@ -70,6 +70,7 @@ private static SegmentCandidate CompileSegment(
7070
TargetWpm = inherited.TargetWpm,
7171
Emotion = segmentEmotion,
7272
Speaker = parsedSegment.Segment.Speaker,
73+
Archetype = parsedSegment.Segment.Archetype,
7374
Timing = parsedSegment.Segment.Timing,
7475
BackgroundColor = parsedSegment.Segment.BackgroundColor,
7576
TextColor = parsedSegment.Segment.TextColor,
@@ -95,7 +96,8 @@ private static IEnumerable<BlockDefinition> BuildBlocks(ParsedSegmentInternal pa
9596
Content = leadingContent,
9697
TargetWpm = parsedSegment.Segment.TargetWpm,
9798
Emotion = parsedSegment.Segment.Emotion,
98-
Speaker = parsedSegment.Segment.Speaker
99+
Speaker = parsedSegment.Segment.Speaker,
100+
Archetype = parsedSegment.Segment.Archetype
99101
},
100102
true,
101103
parsedSegment.LeadingContent);
@@ -111,7 +113,8 @@ private static IEnumerable<BlockDefinition> BuildBlocks(ParsedSegmentInternal pa
111113
Content = parsedSegment.DirectContent?.Text ?? string.Empty,
112114
TargetWpm = parsedSegment.Segment.TargetWpm,
113115
Emotion = parsedSegment.Segment.Emotion,
114-
Speaker = parsedSegment.Segment.Speaker
116+
Speaker = parsedSegment.Segment.Speaker,
117+
Archetype = parsedSegment.Segment.Archetype
115118
},
116119
true,
117120
parsedSegment.DirectContent);
@@ -125,11 +128,15 @@ private static IEnumerable<BlockDefinition> BuildBlocks(ParsedSegmentInternal pa
125128

126129
private static BlockCandidate CompileBlock(BlockDefinition definition, InheritedFormattingState inherited, DocumentAnalysis analysis)
127130
{
131+
var blockArchetype = definition.Block.Archetype ?? inherited.Archetype;
132+
var blockTargetWpm = definition.Block.TargetWpm
133+
?? (blockArchetype is not null && TpsSpec.ArchetypeRecommendedWpm.TryGetValue(blockArchetype, out var archetypeWpm) ? archetypeWpm : inherited.TargetWpm);
128134
var blockInherited = new InheritedFormattingState(
129-
definition.Block.TargetWpm ?? inherited.TargetWpm,
135+
blockTargetWpm,
130136
TpsSupport.ResolveEmotion(definition.Block.Emotion, inherited.Emotion),
131137
definition.Block.Speaker ?? inherited.Speaker,
132-
inherited.SpeedOffsets);
138+
inherited.SpeedOffsets,
139+
blockArchetype);
133140

134141
var content = new TpsContentCompiler().Compile(
135142
definition.Content?.Text ?? string.Empty,
@@ -148,6 +155,7 @@ private static BlockCandidate CompileBlock(BlockDefinition definition, Inherited
148155
TargetWpm = blockInherited.TargetWpm,
149156
Emotion = blockInherited.Emotion,
150157
Speaker = blockInherited.Speaker,
158+
Archetype = blockArchetype,
151159
IsImplicit = definition.IsImplicit,
152160
Phrases = phrases,
153161
Words = words

0 commit comments

Comments
 (0)