Skip to content

Commit 0af071e

Browse files
committed
feat(octra): dynamically move transcription units
1 parent 67a5ea6 commit 0af071e

File tree

5 files changed

+146
-32
lines changed

5 files changed

+146
-32
lines changed

apps/octra/src/app/core/modals/tools-modal/tools-modal.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ <h5 class="modal-title" id="modal-title">
272272
</button>
273273
<div
274274
*ngIf="tools.audioCutting.status !== 'idle'"
275-
class="mb-2"
275+
class="my-2"
276276
>
277277
<div class="progress" style="height: 20px">
278278
<div

apps/octra/src/app/editors/2D-editor/2D-editor.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
[refreshOnInternChanges]="false"
5656
[annotation]="annotationStoreService.transcript!"
5757
(currentLevelChange)="onCurrentLevelChange($event)"
58+
(boundaryDragging)="onBoundaryDragged($event)"
5859
id="special"
5960
style="height: 400px; display: flex; flex: auto; flex-direction: column"
6061
>

apps/octra/src/app/editors/2D-editor/2D-editor.component.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,4 +1092,8 @@ export class TwoDEditorComponent
10921092
);
10931093
}
10941094
}
1095+
1096+
onBoundaryDragged($event: any) {
1097+
console.log($event);
1098+
}
10951099
}

libs/ngx-components/src/lib/components/audio/audio-viewer/audio-viewer.component.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ export class AudioViewerComponent implements OnInit, OnChanges, OnDestroy {
111111
this.subscrManager = new SubscriptionManager<Subscription>();
112112

113113
this.subscrManager.add(
114-
this.av.boundaryDragging.subscribe((status) => {
115-
if (status === 'stopped') {
114+
this.av.boundaryDragging.subscribe((event) => {
115+
if (event.status === 'stopped') {
116116
this.renderer.setStyle(
117117
this.konvaContainer?.nativeElement,
118118
'cursor',
@@ -253,7 +253,10 @@ export class AudioViewerComponent implements OnInit, OnChanges, OnDestroy {
253253
* triggers when the boundary was dragged.
254254
*/
255255
@Output()
256-
public get boundaryDragging(): Subject<'started' | 'stopped'> {
256+
public get boundaryDragging(): Subject<{
257+
status: 'started' | 'stopped' | 'dragging';
258+
shiftPressed?: boolean;
259+
}> {
257260
return this.av.boundaryDragging;
258261
}
259262

@@ -1939,11 +1942,11 @@ export class AudioViewerComponent implements OnInit, OnChanges, OnDestroy {
19391942
(this.settings.lineheight + this.settings.margin.top);
19401943
const sceneSegment = this.av.currentLevel.items.find(
19411944
(a: any) => a.id === segment.id
1942-
) as OctraAnnotationSegment |undefined;
1945+
) as OctraAnnotationSegment | undefined;
19431946
if (
19441947
sceneSegment === undefined ||
19451948
segment?.time === undefined ||
1946-
(this.av.currentLevel.type !== AnnotationLevelType.SEGMENT)
1949+
this.av.currentLevel.type !== AnnotationLevelType.SEGMENT
19471950
) {
19481951
console.error(`scenceSegment is undefined!`);
19491952
} else {
@@ -2170,7 +2173,6 @@ export class AudioViewerComponent implements OnInit, OnChanges, OnDestroy {
21702173
this.audioChunk.absolutePlayposition.clone();
21712174
this.audioChunk.selection.end =
21722175
this.audioChunk.absolutePlayposition.clone();
2173-
this.av.drawnSelection = this.audioChunk.selection.clone();
21742176
}
21752177

21762178
this.av
@@ -2509,11 +2511,12 @@ export class AudioViewerComponent implements OnInit, OnChanges, OnDestroy {
25092511
private onKeyDown = (event: KeyboardEvent) => {
25102512
const shortcutInfo = this.shortcutsManager.checkKeyEvent(event, Date.now());
25112513

2514+
this.av.shiftPressed =
2515+
event.keyCode === 16 || event.code?.includes('Shift') || event.key?.includes('Shift');
2516+
25122517
if (shortcutInfo !== undefined) {
25132518
const comboKey = shortcutInfo.shortcut;
25142519

2515-
this.av.shiftPressed = comboKey === 'SHIFT';
2516-
25172520
if (this.settings.shortcutsEnabled) {
25182521
if (this._focused && this.isDisabledKey(comboKey)) {
25192522
// key pressed is disabled by config
@@ -2606,7 +2609,9 @@ export class AudioViewerComponent implements OnInit, OnChanges, OnDestroy {
26062609
if (
26072610
this.av.currentLevel.type === AnnotationLevelType.SEGMENT
26082611
) {
2609-
const segment = this.av.currentLevel.items[segmentI] as OctraAnnotationSegment<ASRContext>;
2612+
const segment = this.av.currentLevel.items[
2613+
segmentI
2614+
] as OctraAnnotationSegment<ASRContext>;
26102615
if (
26112616
segmentI > -1 &&
26122617
segment.context?.asr?.isBlockedBy === undefined &&
@@ -3118,6 +3123,7 @@ export class AudioViewerComponent implements OnInit, OnChanges, OnDestroy {
31183123
};
31193124

31203125
private onKeyUp = (event: KeyboardEvent) => {
3126+
this.av.onKeyUp();
31213127
this.shortcutsManager.checkKeyEvent(event, Date.now());
31223128
};
31233129

libs/ngx-components/src/lib/components/audio/audio-viewer/audio-viewer.service.ts

Lines changed: 125 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
OctraAnnotation,
1212
OctraAnnotationAnyLevel,
1313
OctraAnnotationSegment,
14+
OctraAnnotationSegmentLevel,
1415
OLabel,
1516
} from '@octra/annotation';
1617
import { Subject } from 'rxjs';
@@ -27,7 +28,10 @@ import {
2728
providedIn: 'root',
2829
})
2930
export class AudioViewerService {
30-
get boundaryDragging(): Subject<'started' | 'stopped'> {
31+
get boundaryDragging(): Subject<{
32+
status: 'started' | 'stopped' | 'dragging';
33+
shiftPressed?: boolean;
34+
}> {
3135
return this._boundaryDragging;
3236
}
3337

@@ -60,7 +64,10 @@ export class AudioViewerService {
6064
protected mouseClickPos: SampleUnit | undefined;
6165
protected playcursor: PlayCursor | undefined;
6266

63-
private _boundaryDragging: Subject<'started' | 'stopped'>;
67+
private _boundaryDragging: Subject<{
68+
status: 'started' | 'stopped' | 'dragging';
69+
shiftPressed?: boolean;
70+
}>;
6471
currentLevelID?: number;
6572

6673
annotation?: OctraAnnotation<ASRContext, OctraAnnotationSegment>;
@@ -134,7 +141,10 @@ export class AudioViewerService {
134141
set dragableBoundaryNumber(value: number) {
135142
if (value > -1 && this._dragableBoundaryNumber === -1) {
136143
// started
137-
this._boundaryDragging.next('started');
144+
this._boundaryDragging.next({
145+
shiftPressed: this.shiftPressed,
146+
status: 'started',
147+
});
138148
}
139149
this._dragableBoundaryNumber = value;
140150
}
@@ -185,7 +195,10 @@ export class AudioViewerService {
185195
}
186196

187197
constructor(private multiThreadingService: MultiThreadingService) {
188-
this._boundaryDragging = new Subject<'started' | 'stopped'>();
198+
this._boundaryDragging = new Subject<{
199+
status: 'started' | 'stopped' | 'dragging';
200+
shiftPressed?: boolean;
201+
}>();
189202
}
190203

191204
public initialize(innerWidth: number, audioChunk: AudioChunk) {
@@ -233,7 +246,9 @@ export class AudioViewerService {
233246
this.audioChunk.startpos = this.mouseClickPos.clone();
234247
this.audioChunk.selection.start = absXInTime.clone();
235248
this.audioChunk.selection.end = absXInTime.clone();
236-
this._drawnSelection = this.audioChunk.selection.clone();
249+
if (!this.shiftPressed) {
250+
this._drawnSelection = this.audioChunk.selection.clone();
251+
}
237252

238253
if (this._dragableBoundaryNumber > -1) {
239254
const segmentBefore = (
@@ -286,23 +301,108 @@ export class AudioViewerService {
286301
)?.clone();
287302

288303
if (segment) {
289-
segment.time = this.audioTCalculator.absXChunktoSampleUnit(
290-
absX,
291-
this.audioChunk
292-
)!;
293-
294-
this.currentLevelChange.emit({
295-
type: 'change',
296-
items: [
297-
{
298-
instance: segment,
299-
},
300-
],
301-
});
302-
this.annotationChange.emit(this.annotation);
304+
if (!this.shiftPressed) {
305+
// move only this boundary
306+
segment.time = this.audioTCalculator.absXChunktoSampleUnit(
307+
absX,
308+
this.audioChunk
309+
)!;
310+
311+
this.currentLevelChange.emit({
312+
type: 'change',
313+
items: [
314+
{
315+
instance: segment,
316+
},
317+
],
318+
});
319+
this.annotationChange.emit(this.annotation);
320+
} else if (this.drawnSelection?.duration?.samples) {
321+
// move all segments with difference to left or right
322+
const oldSamplePosition = segment.time.samples;
323+
const newSamplePosition =
324+
this.audioTCalculator.absXChunktoSampleUnit(
325+
absX,
326+
this.audioChunk
327+
)?.samples;
328+
const diff = newSamplePosition! - oldSamplePosition;
329+
let changedItems: OctraAnnotationSegment[] = [];
330+
331+
if (diff > 0) {
332+
// shift to right
333+
for (const currentLevelElement of (this.annotation
334+
.currentLevel as OctraAnnotationSegmentLevel<OctraAnnotationSegment>)!
335+
.items) {
336+
if (
337+
currentLevelElement.time.samples >=
338+
segment.time.samples &&
339+
currentLevelElement.time.samples + diff <
340+
this.drawnSelection.end!.samples
341+
) {
342+
const newItem = currentLevelElement.clone(
343+
currentLevelElement.id
344+
);
345+
newItem.time = currentLevelElement.time.add(
346+
this.audioManager.createSampleUnit(diff)
347+
);
348+
this.annotation =
349+
this.annotation.changeCurrentItemById(
350+
currentLevelElement.id,
351+
newItem
352+
);
353+
changedItems.push(newItem);
354+
}
355+
}
356+
} else {
357+
// shift to left
358+
for (const currentLevelElement of (this.annotation
359+
.currentLevel as OctraAnnotationSegmentLevel<OctraAnnotationSegment>)!
360+
.items) {
361+
console.log(
362+
`move to ${currentLevelElement.time.samples + diff}`
363+
);
364+
if (
365+
currentLevelElement.time.samples <=
366+
segment.time.samples &&
367+
currentLevelElement.time.samples + diff >
368+
this.drawnSelection.start!.samples
369+
) {
370+
const newItem = currentLevelElement.clone(
371+
currentLevelElement.id
372+
);
373+
newItem.time = currentLevelElement.time.add(
374+
this.audioManager.createSampleUnit(diff)
375+
);
376+
this.annotation =
377+
this.annotation.changeCurrentItemById(
378+
currentLevelElement.id,
379+
newItem
380+
);
381+
changedItems.push(newItem);
382+
} else if (
383+
currentLevelElement.time.samples - diff <
384+
0
385+
) {
386+
changedItems = [];
387+
break;
388+
}
389+
}
390+
}
391+
392+
if (changedItems.length > 0) {
393+
this.currentLevelChange.emit({
394+
type: 'change',
395+
items: changedItems.map((a) => ({ instance: a })),
396+
});
397+
this.annotationChange.emit(this.annotation);
398+
}
399+
}
303400
}
304401

305-
this._boundaryDragging.next('stopped');
402+
this._boundaryDragging.next({
403+
shiftPressed: this.shiftPressed,
404+
status: 'stopped',
405+
});
306406
} else {
307407
// set selection
308408
this.audioChunk.selection.end = absXInTime.clone();
@@ -612,8 +712,11 @@ export class AudioViewerService {
612712

613713
if (this.mouseDown && this._dragableBoundaryNumber < 0) {
614714
// mouse down, nothing dragged
615-
this.audioChunk.selection.end = absXTime.clone();
616-
this._drawnSelection = this.audioChunk.selection.clone();
715+
if (!this.shiftPressed) {
716+
console.log('reset selection');
717+
this.audioChunk.selection.end = absXTime.clone();
718+
this._drawnSelection = this.audioChunk.selection.clone();
719+
}
617720
} else if (
618721
this.settings.boundaries.enabled &&
619722
this.mouseDown &&

0 commit comments

Comments
 (0)