1use ara2_bridge_sys::*;
50
51pub use ara2_bridge_sys::build_araarchivingcontroller_vtable;
53pub use ara2_bridge_sys::build_araaudioaccesscontroller_vtable;
54pub use ara2_bridge_sys::build_aracontentaccesscontroller_vtable;
55pub use ara2_bridge_sys::build_aramodelupdatecontroller_vtable;
56pub use ara2_bridge_sys::build_araplaybackcontroller_vtable;
57
58pub trait DocumentController {
81 fn destroy(&mut self);
86
87 fn get_factory(&self) -> *const ARAFactory;
93
94 fn begin_editing(&mut self);
102
103 fn end_editing(&mut self);
108
109 fn notify_model_updates(&mut self);
115
116 fn update_document_properties(&mut self, properties: &ARADocumentProperties);
118
119 fn create_audio_source(
128 &mut self,
129 host_ref: ARAAudioSourceHostRef,
130 properties: &ARAAudioSourceProperties,
131 ) -> ARAAudioSourceRef;
132
133 fn update_audio_source_properties(
138 &mut self,
139 source: ARAAudioSourceRef,
140 properties: &ARAAudioSourceProperties,
141 );
142
143 fn update_audio_source_content(
148 &mut self,
149 source: ARAAudioSourceRef,
150 range: Option<&ARAContentTimeRange>,
151 flags: ARAContentUpdateFlags,
152 );
153
154 fn enable_audio_source_samples_access(&mut self, source: ARAAudioSourceRef, enable: ARABool);
160
161 fn deactivate_audio_source_for_undo_history(
166 &mut self,
167 source: ARAAudioSourceRef,
168 deactivate: ARABool,
169 );
170
171 fn destroy_audio_source(&mut self, source: ARAAudioSourceRef);
173
174 fn request_audio_source_content_analysis(
183 &mut self,
184 source: ARAAudioSourceRef,
185 count: ARASize,
186 content_types: *const ARAContentType,
187 ) -> ARABool;
188
189 fn is_audio_source_content_available(
193 &self,
194 source: ARAAudioSourceRef,
195 content_type: ARAContentType,
196 ) -> ARABool;
197
198 fn create_musical_context(
200 &mut self,
201 host_ref: ARAMusicalContextHostRef,
202 properties: &ARAMusicalContextProperties,
203 ) -> ARAMusicalContextRef;
204
205 fn update_musical_context_properties(
207 &mut self,
208 ctx: ARAMusicalContextRef,
209 properties: &ARAMusicalContextProperties,
210 );
211
212 fn update_musical_context_content(
214 &mut self,
215 ctx: ARAMusicalContextRef,
216 range: Option<&ARAContentTimeRange>,
217 flags: ARAContentUpdateFlags,
218 );
219
220 fn destroy_musical_context(&mut self, ctx: ARAMusicalContextRef);
222
223 fn create_region_sequence(
225 &mut self,
226 host_ref: ARARegionSequenceHostRef,
227 properties: &ARARegionSequenceProperties,
228 ) -> ARARegionSequenceRef;
229
230 fn update_region_sequence_properties(
232 &mut self,
233 seq: ARARegionSequenceRef,
234 properties: &ARARegionSequenceProperties,
235 );
236
237 fn destroy_region_sequence(&mut self, seq: ARARegionSequenceRef);
239
240 fn create_playback_region(
246 &mut self,
247 host_ref: ARAPlaybackRegionHostRef,
248 audio_modification_ref: ARAAudioModificationRef,
249 properties: &ARAPlaybackRegionProperties,
250 ) -> ARAPlaybackRegionRef;
251
252 fn update_playback_region_properties(
254 &mut self,
255 region: ARAPlaybackRegionRef,
256 properties: &ARAPlaybackRegionProperties,
257 );
258
259 fn destroy_playback_region(&mut self, region: ARAPlaybackRegionRef);
261
262 fn store_objects_to_archive(
270 &mut self,
271 archive_writer_host_ref: ARAArchiveWriterHostRef,
272 filter: *const ARAStoreObjectsFilter,
273 ) -> ARABool;
274
275 fn restore_objects_from_archive(
283 &mut self,
284 archive_reader_host_ref: ARAArchiveReaderHostRef,
285 filter: *const ARARestoreObjectsFilter,
286 ) -> ARABool;
287}
288
289struct ControllerState {
294 controller: Box<dyn DocumentController>,
295}
296
297pub fn build_document_controller_instance(
325 controller: Box<dyn DocumentController>,
326) -> *const ARADocumentControllerInstance {
327 let state = Box::into_raw(Box::new(ControllerState { controller }));
328 let vtable = Box::into_raw(Box::new(build_vtable()));
329 let instance = Box::new(ARADocumentControllerInstance {
330 structSize: std::mem::size_of::<ARADocumentControllerInstance>() as ARASize,
331 documentControllerRef: state as ARADocumentControllerRef,
332 documentControllerInterface: vtable,
333 });
334 Box::into_raw(instance)
335}
336
337fn build_vtable() -> ARADocumentControllerInterface {
338 let mut v: ARADocumentControllerInterface = unsafe { std::mem::zeroed() };
339 v.structSize = std::mem::size_of::<ARADocumentControllerInterface>() as ARASize;
340 v.destroyDocumentController = Some(destroy_dc);
341 v.getFactory = Some(get_factory_cb);
342 v.beginEditing = Some(begin_editing_cb);
343 v.endEditing = Some(end_editing_cb);
344 v.notifyModelUpdates = Some(notify_model_updates_cb);
345 v.updateDocumentProperties = Some(update_doc_props_cb);
346 v.createAudioSource = Some(create_audio_source_cb);
347 v.updateAudioSourceProperties = Some(update_audio_source_props_cb);
348 v.updateAudioSourceContent = Some(update_audio_source_content_cb);
349 v.enableAudioSourceSamplesAccess = Some(enable_audio_access_cb);
350 v.deactivateAudioSourceForUndoHistory = Some(deactivate_audio_undo_cb);
351 v.destroyAudioSource = Some(destroy_audio_source_cb);
352 v.requestAudioSourceContentAnalysis = Some(request_audio_analysis_cb);
353 v.isAudioSourceContentAvailable = Some(is_audio_content_avail_cb);
354 v.createMusicalContext = Some(create_musical_ctx_cb);
355 v.updateMusicalContextProperties = Some(update_musical_ctx_props_cb);
356 v.updateMusicalContextContent = Some(update_musical_ctx_content_cb);
357 v.destroyMusicalContext = Some(destroy_musical_ctx_cb);
358 v.createRegionSequence = Some(create_region_seq_cb);
359 v.updateRegionSequenceProperties = Some(update_region_seq_props_cb);
360 v.destroyRegionSequence = Some(destroy_region_seq_cb);
361 v.createPlaybackRegion = Some(create_playback_region_cb);
362 v.updatePlaybackRegionProperties = Some(update_playback_region_props_cb);
363 v.destroyPlaybackRegion = Some(destroy_playback_region_cb);
364 v.storeObjectsToArchive = Some(store_objects_cb);
365 v.restoreObjectsFromArchive = Some(restore_objects_cb);
366 v
367}
368
369unsafe fn state<'a>(r: ARADocumentControllerRef) -> &'a mut ControllerState {
370 &mut *(r as *mut ControllerState)
371}
372
373unsafe extern "C" fn destroy_dc(r: ARADocumentControllerRef) {
374 drop(Box::from_raw(r as *mut ControllerState));
375}
376
377unsafe extern "C" fn get_factory_cb(r: ARADocumentControllerRef) -> *const ARAFactory {
378 state(r).controller.get_factory()
379}
380
381unsafe extern "C" fn begin_editing_cb(r: ARADocumentControllerRef) {
382 state(r).controller.begin_editing();
383}
384
385unsafe extern "C" fn end_editing_cb(r: ARADocumentControllerRef) {
386 state(r).controller.end_editing();
387}
388
389unsafe extern "C" fn notify_model_updates_cb(r: ARADocumentControllerRef) {
390 state(r).controller.notify_model_updates();
391}
392
393unsafe extern "C" fn update_doc_props_cb(
394 r: ARADocumentControllerRef,
395 p: *const ARADocumentProperties,
396) {
397 state(r).controller.update_document_properties(&*p);
398}
399
400unsafe extern "C" fn create_audio_source_cb(
401 r: ARADocumentControllerRef,
402 h: ARAAudioSourceHostRef,
403 p: *const ARAAudioSourceProperties,
404) -> ARAAudioSourceRef {
405 state(r).controller.create_audio_source(h, &*p)
406}
407
408unsafe extern "C" fn update_audio_source_props_cb(
409 r: ARADocumentControllerRef,
410 s: ARAAudioSourceRef,
411 p: *const ARAAudioSourceProperties,
412) {
413 state(r).controller.update_audio_source_properties(s, &*p);
414}
415
416unsafe extern "C" fn update_audio_source_content_cb(
417 r: ARADocumentControllerRef,
418 s: ARAAudioSourceRef,
419 range: *const ARAContentTimeRange,
420 flags: ARAContentUpdateFlags,
421) {
422 let opt = if range.is_null() { None } else { Some(&*range) };
423 state(r)
424 .controller
425 .update_audio_source_content(s, opt, flags);
426}
427
428unsafe extern "C" fn enable_audio_access_cb(
429 r: ARADocumentControllerRef,
430 s: ARAAudioSourceRef,
431 enable: ARABool,
432) {
433 state(r)
434 .controller
435 .enable_audio_source_samples_access(s, enable);
436}
437
438unsafe extern "C" fn deactivate_audio_undo_cb(
439 r: ARADocumentControllerRef,
440 s: ARAAudioSourceRef,
441 deactivate: ARABool,
442) {
443 state(r)
444 .controller
445 .deactivate_audio_source_for_undo_history(s, deactivate);
446}
447
448unsafe extern "C" fn destroy_audio_source_cb(r: ARADocumentControllerRef, s: ARAAudioSourceRef) {
449 state(r).controller.destroy_audio_source(s);
450}
451
452unsafe extern "C" fn request_audio_analysis_cb(
453 r: ARADocumentControllerRef,
454 s: ARAAudioSourceRef,
455 count: ARASize,
456 types: *const ARAContentType,
457) {
458 state(r)
459 .controller
460 .request_audio_source_content_analysis(s, count, types);
461}
462
463unsafe extern "C" fn is_audio_content_avail_cb(
464 r: ARADocumentControllerRef,
465 s: ARAAudioSourceRef,
466 ct: ARAContentType,
467) -> ARABool {
468 state(r).controller.is_audio_source_content_available(s, ct)
469}
470
471unsafe extern "C" fn create_musical_ctx_cb(
472 r: ARADocumentControllerRef,
473 h: ARAMusicalContextHostRef,
474 p: *const ARAMusicalContextProperties,
475) -> ARAMusicalContextRef {
476 state(r).controller.create_musical_context(h, &*p)
477}
478
479unsafe extern "C" fn update_musical_ctx_props_cb(
480 r: ARADocumentControllerRef,
481 c: ARAMusicalContextRef,
482 p: *const ARAMusicalContextProperties,
483) {
484 state(r)
485 .controller
486 .update_musical_context_properties(c, &*p);
487}
488
489unsafe extern "C" fn update_musical_ctx_content_cb(
490 r: ARADocumentControllerRef,
491 c: ARAMusicalContextRef,
492 range: *const ARAContentTimeRange,
493 flags: ARAContentUpdateFlags,
494) {
495 let opt = if range.is_null() { None } else { Some(&*range) };
496 state(r)
497 .controller
498 .update_musical_context_content(c, opt, flags);
499}
500
501unsafe extern "C" fn destroy_musical_ctx_cb(r: ARADocumentControllerRef, c: ARAMusicalContextRef) {
502 state(r).controller.destroy_musical_context(c);
503}
504
505unsafe extern "C" fn create_region_seq_cb(
506 r: ARADocumentControllerRef,
507 h: ARARegionSequenceHostRef,
508 p: *const ARARegionSequenceProperties,
509) -> ARARegionSequenceRef {
510 state(r).controller.create_region_sequence(h, &*p)
511}
512
513unsafe extern "C" fn update_region_seq_props_cb(
514 r: ARADocumentControllerRef,
515 s: ARARegionSequenceRef,
516 p: *const ARARegionSequenceProperties,
517) {
518 state(r)
519 .controller
520 .update_region_sequence_properties(s, &*p);
521}
522
523unsafe extern "C" fn destroy_region_seq_cb(r: ARADocumentControllerRef, s: ARARegionSequenceRef) {
524 state(r).controller.destroy_region_sequence(s);
525}
526
527unsafe extern "C" fn create_playback_region_cb(
528 r: ARADocumentControllerRef,
529 m: ARAAudioModificationRef,
530 h: ARAPlaybackRegionHostRef,
531 p: *const ARAPlaybackRegionProperties,
532) -> ARAPlaybackRegionRef {
533 state(r).controller.create_playback_region(h, m, &*p)
534}
535
536unsafe extern "C" fn update_playback_region_props_cb(
537 r: ARADocumentControllerRef,
538 reg: ARAPlaybackRegionRef,
539 p: *const ARAPlaybackRegionProperties,
540) {
541 state(r)
542 .controller
543 .update_playback_region_properties(reg, &*p);
544}
545
546unsafe extern "C" fn destroy_playback_region_cb(
547 r: ARADocumentControllerRef,
548 reg: ARAPlaybackRegionRef,
549) {
550 state(r).controller.destroy_playback_region(reg);
551}
552
553unsafe extern "C" fn store_objects_cb(
554 r: ARADocumentControllerRef,
555 wr: ARAArchiveWriterHostRef,
556 f: *const ARAStoreObjectsFilter,
557) -> ARABool {
558 state(r).controller.store_objects_to_archive(wr, f)
559}
560
561unsafe extern "C" fn restore_objects_cb(
562 r: ARADocumentControllerRef,
563 rr: ARAArchiveReaderHostRef,
564 f: *const ARARestoreObjectsFilter,
565) -> ARABool {
566 state(r).controller.restore_objects_from_archive(rr, f)
567}
568
569pub trait PlaybackRegionHost {
578 fn notify_content_analysis_completed(
580 &mut self,
581 region: ARAPlaybackRegionRef,
582 content: *mut std::ffi::c_void,
583 );
584
585 fn request_playback(&mut self, region: ARAPlaybackRegionRef);
587}
588
589pub trait ModelUpdateController {
594 fn notify_audio_source_content_changed(
596 &mut self,
597 source: ARAAudioSourceRef,
598 content: *const std::ffi::c_void,
599 );
600
601 fn notify_model_update(&mut self);
603
604 fn notify_restored_from_archive(&mut self);
606}
607
608pub trait ArchiveReaderHost {
613 fn read(&mut self, buffer: &mut [u8]) -> usize;
616
617 fn size(&self) -> usize;
619}
620
621pub trait ArchiveWriterHost {
626 fn write(&mut self, data: &[u8]);
628}
629
630#[cfg(test)]
635mod tests {
636 use super::*;
637
638 struct TestPlugin {
640 destroyed: bool,
641 editing: bool,
642 }
643
644 impl DocumentController for TestPlugin {
645 fn destroy(&mut self) {
646 self.destroyed = true;
647 }
648 fn get_factory(&self) -> *const ARAFactory {
649 std::ptr::null()
650 }
651 fn begin_editing(&mut self) {
652 self.editing = true;
653 }
654 fn end_editing(&mut self) {
655 self.editing = false;
656 }
657 fn notify_model_updates(&mut self) {}
658 fn update_document_properties(&mut self, _: &ARADocumentProperties) {}
659 fn create_audio_source(
660 &mut self,
661 _: ARAAudioSourceHostRef,
662 _: &ARAAudioSourceProperties,
663 ) -> ARAAudioSourceRef {
664 std::ptr::null_mut()
665 }
666 fn update_audio_source_properties(
667 &mut self,
668 _: ARAAudioSourceRef,
669 _: &ARAAudioSourceProperties,
670 ) {
671 }
672 fn update_audio_source_content(
673 &mut self,
674 _: ARAAudioSourceRef,
675 _: Option<&ARAContentTimeRange>,
676 _: ARAContentUpdateFlags,
677 ) {
678 }
679 fn enable_audio_source_samples_access(&mut self, _: ARAAudioSourceRef, _: ARABool) {}
680 fn deactivate_audio_source_for_undo_history(&mut self, _: ARAAudioSourceRef, _: ARABool) {}
681 fn destroy_audio_source(&mut self, _: ARAAudioSourceRef) {}
682 fn request_audio_source_content_analysis(
683 &mut self,
684 _: ARAAudioSourceRef,
685 _: ARASize,
686 _: *const ARAContentType,
687 ) -> ARABool {
688 1
689 }
690 fn is_audio_source_content_available(
691 &self,
692 _: ARAAudioSourceRef,
693 _: ARAContentType,
694 ) -> ARABool {
695 1
696 }
697 fn create_musical_context(
698 &mut self,
699 _: ARAMusicalContextHostRef,
700 _: &ARAMusicalContextProperties,
701 ) -> ARAMusicalContextRef {
702 std::ptr::null_mut()
703 }
704 fn update_musical_context_properties(
705 &mut self,
706 _: ARAMusicalContextRef,
707 _: &ARAMusicalContextProperties,
708 ) {
709 }
710 fn update_musical_context_content(
711 &mut self,
712 _: ARAMusicalContextRef,
713 _: Option<&ARAContentTimeRange>,
714 _: ARAContentUpdateFlags,
715 ) {
716 }
717 fn destroy_musical_context(&mut self, _: ARAMusicalContextRef) {}
718 fn create_region_sequence(
719 &mut self,
720 _: ARARegionSequenceHostRef,
721 _: &ARARegionSequenceProperties,
722 ) -> ARARegionSequenceRef {
723 std::ptr::null_mut()
724 }
725 fn update_region_sequence_properties(
726 &mut self,
727 _: ARARegionSequenceRef,
728 _: &ARARegionSequenceProperties,
729 ) {
730 }
731 fn destroy_region_sequence(&mut self, _: ARARegionSequenceRef) {}
732 fn create_playback_region(
733 &mut self,
734 _: ARAPlaybackRegionHostRef,
735 _: ARAAudioModificationRef,
736 _: &ARAPlaybackRegionProperties,
737 ) -> ARAPlaybackRegionRef {
738 std::ptr::null_mut()
739 }
740 fn update_playback_region_properties(
741 &mut self,
742 _: ARAPlaybackRegionRef,
743 _: &ARAPlaybackRegionProperties,
744 ) {
745 }
746 fn destroy_playback_region(&mut self, _: ARAPlaybackRegionRef) {}
747 fn store_objects_to_archive(
748 &mut self,
749 _: ARAArchiveWriterHostRef,
750 _: *const ARAStoreObjectsFilter,
751 ) -> ARABool {
752 1
753 }
754 fn restore_objects_from_archive(
755 &mut self,
756 _: ARAArchiveReaderHostRef,
757 _: *const ARARestoreObjectsFilter,
758 ) -> ARABool {
759 1
760 }
761 }
762
763 #[test]
764 fn test_plugin_lifecycle() {
765 let mut plugin = TestPlugin {
766 destroyed: false,
767 editing: false,
768 };
769 assert!(!plugin.destroyed);
770 plugin.begin_editing();
771 assert!(plugin.editing);
772 plugin.end_editing();
773 assert!(!plugin.editing);
774 plugin.destroy();
775 assert!(plugin.destroyed);
776 }
777
778 #[test]
779 fn test_build_instance_creates_vtable() {
780 let plugin = Box::new(TestPlugin {
781 destroyed: false,
782 editing: false,
783 });
784 let instance = build_document_controller_instance(plugin);
785 assert!(unsafe { (*(*instance).documentControllerInterface).structSize } > 0);
787 }
788}