├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ └── config.yml
└── workflows
│ └── build.yml
├── .gitignore
├── .swift-version
├── .swiftformat
├── .swiftpm
└── xcode
│ ├── package.xcworkspace
│ └── contents.xcworkspacedata
│ └── xcshareddata
│ └── xcschemes
│ └── DAWFileKit-CI.xcscheme
├── LICENSE
├── Package.swift
├── README.md
├── Sources
└── DAWFileKit
│ ├── Cubase
│ ├── Cubase.swift
│ └── TrackArchive
│ │ ├── Conversion
│ │ ├── TrackArchive Converting DAWMarkers.swift
│ │ └── TrackArchive Extract DAWMarkers.swift
│ │ ├── Errors
│ │ ├── TrackArchive EncodeError.swift
│ │ └── TrackArchive ParseError.swift
│ │ ├── Messages
│ │ ├── TrackArchive EncodeMessage.swift
│ │ └── TrackArchive ParseMessage.swift
│ │ ├── Protocols
│ │ ├── CubaseTrackArchiveMarker.swift
│ │ └── CubaseTrackArchiveTrack.swift
│ │ ├── TrackArchive CycleMarker.swift
│ │ ├── TrackArchive Helpers.swift
│ │ ├── TrackArchive Init.swift
│ │ ├── TrackArchive Main.swift
│ │ ├── TrackArchive Marker.swift
│ │ ├── TrackArchive MarkerTrack.swift
│ │ ├── TrackArchive OrphanTrack.swift
│ │ ├── TrackArchive Parse.swift
│ │ ├── TrackArchive TempoTrack.swift
│ │ ├── TrackArchive TimeType.swift
│ │ ├── TrackArchive TrackTimeDomain.swift
│ │ ├── TrackArchive xmlString.swift
│ │ └── TrackArchive.swift
│ ├── DAW Agnostic Types
│ ├── DAWMarker Comparable.swift
│ ├── DAWMarker Conversions.swift
│ ├── DAWMarker Storage Value.swift
│ ├── DAWMarker Storage.swift
│ ├── DAWMarker.swift
│ ├── DAWMarkerTrack.swift
│ └── DAWTrackType.swift
│ ├── FinalCutPro
│ ├── FCPXML
│ │ ├── Context
│ │ │ ├── FCPXML ElementContext Items.swift
│ │ │ ├── FCPXML ElementContext Tools.swift
│ │ │ ├── FCPXML ElementContext.swift
│ │ │ └── FCPXML FrameRateSource.swift
│ │ ├── Element Types
│ │ │ ├── ElementModelType
│ │ │ │ ├── FCPXML AnyElementModelType Static.swift
│ │ │ │ ├── FCPXML AnyElementModelType.swift
│ │ │ │ ├── FCPXML ElementModelType XML.swift
│ │ │ │ ├── FCPXML ElementModelType.swift
│ │ │ │ ├── FCPXMLElementModelTypeProtocol Static.swift
│ │ │ │ └── FCPXMLElementModelTypeProtocol.swift
│ │ │ └── ElementType
│ │ │ │ ├── FCPXML ElementType XML.swift
│ │ │ │ └── FCPXML ElementType.swift
│ │ ├── Errors
│ │ │ └── FCPXML ParseError.swift
│ │ ├── Extraction
│ │ │ ├── FCPXML Extract.swift
│ │ │ ├── FCPXML ExtractableChildren.swift
│ │ │ ├── FCPXML ExtractedElement.swift
│ │ │ ├── FCPXML Extraction.swift
│ │ │ ├── FCPXML ExtractionScope.swift
│ │ │ ├── FCPXMLExtractedElement.swift
│ │ │ ├── FCPXMLExtractedModelElement.swift
│ │ │ └── Presets
│ │ │ │ ├── FCPXML CaptionsExtractionPreset.swift
│ │ │ │ ├── FCPXML FrameDataPreset.swift
│ │ │ │ ├── FCPXML MarkersExtractionPreset.swift
│ │ │ │ ├── FCPXML RolesExtractionPreset.swift
│ │ │ │ └── FCPXMLExtractionPreset.swift
│ │ ├── FCPXML Properties.swift
│ │ ├── FCPXML init.swift
│ │ ├── FCPXML.swift
│ │ ├── Model
│ │ │ ├── Common
│ │ │ │ ├── Attributes
│ │ │ │ │ ├── FCPXML AudioLayout.swift
│ │ │ │ │ ├── FCPXML AudioRate.swift
│ │ │ │ │ ├── FCPXML ClipSourceEnable.swift
│ │ │ │ │ ├── FCPXML FrameSampling.swift
│ │ │ │ │ └── FCPXML TimecodeFormat.swift
│ │ │ │ └── Elements
│ │ │ │ │ ├── FCPXML AudioChannelSource.swift
│ │ │ │ │ ├── FCPXML AudioRoleSource.swift
│ │ │ │ │ ├── FCPXML ConformRate.swift
│ │ │ │ │ ├── FCPXML MediaRep.swift
│ │ │ │ │ ├── FCPXML Metadata Metadatum.swift
│ │ │ │ │ ├── FCPXML Metadata.swift
│ │ │ │ │ ├── FCPXML Text.swift
│ │ │ │ │ ├── FCPXML TimeMap TimePoint.swift
│ │ │ │ │ └── FCPXML TimeMap.swift
│ │ │ ├── FCPXML Root Version.swift
│ │ │ ├── FCPXML Root.swift
│ │ │ ├── Meta
│ │ │ │ └── AnyTimeline
│ │ │ │ │ └── FCPXML AnyTimeline.swift
│ │ │ ├── Protocols
│ │ │ │ ├── FCPXMLAttribute.swift
│ │ │ │ ├── FCPXMLElement Extensions.swift
│ │ │ │ ├── FCPXMLElement.swift
│ │ │ │ ├── Single Attributes
│ │ │ │ │ ├── FCPXMLElementDuration.swift
│ │ │ │ │ ├── FCPXMLElementFrameSampling.swift
│ │ │ │ │ ├── FCPXMLElementModDate.swift
│ │ │ │ │ ├── FCPXMLElementOffset.swift
│ │ │ │ │ ├── FCPXMLElementStart.swift
│ │ │ │ │ ├── FCPXMLElementTCFormat.swift
│ │ │ │ │ └── FCPXMLElementTCStart.swift
│ │ │ │ ├── Single Children
│ │ │ │ │ ├── FCPXMLElementAudioChannelSourceChildren.swift
│ │ │ │ │ ├── FCPXMLElementAudioRoleSourceChildren.swift
│ │ │ │ │ ├── FCPXMLElementBookmarkChild.swift
│ │ │ │ │ ├── FCPXMLElementMetadataChild.swift
│ │ │ │ │ ├── FCPXMLElementNoteChild.swift
│ │ │ │ │ ├── FCPXMLElementTextChildren.swift
│ │ │ │ │ └── FCPXMLElementTextStyleDefinitionChildren.swift
│ │ │ │ └── Story Elements
│ │ │ │ │ ├── FCPXMLElementAnchorableAttributes.swift
│ │ │ │ │ ├── FCPXMLElementAudioStartAndDuration.swift
│ │ │ │ │ ├── FCPXMLElementClipAttributes.swift
│ │ │ │ │ ├── FCPXMLElementClipAttributesOptionalDuration.swift
│ │ │ │ │ ├── FCPXMLElementMediaAttributes.swift
│ │ │ │ │ ├── FCPXMLElementMetaTimeline.swift
│ │ │ │ │ └── FCPXMLElementTimingParams.swift
│ │ │ ├── Resources
│ │ │ │ ├── FCPXML Asset.swift
│ │ │ │ ├── FCPXML Effect.swift
│ │ │ │ ├── FCPXML Format.swift
│ │ │ │ ├── FCPXML Locator.swift
│ │ │ │ ├── FCPXML Media Multicam Angle.swift
│ │ │ │ ├── FCPXML Media Multicam.swift
│ │ │ │ ├── FCPXML Media.swift
│ │ │ │ ├── FCPXML ObjectTracker TrackingShape.swift
│ │ │ │ └── FCPXML ObjectTracker.swift
│ │ │ ├── Roles
│ │ │ │ ├── FCPXML AudioRole.swift
│ │ │ │ ├── FCPXML CaptionRole.swift
│ │ │ │ ├── FCPXML VideoRole.swift
│ │ │ │ └── Meta
│ │ │ │ │ ├── FCPXML AncestorRoles.swift
│ │ │ │ │ ├── FCPXML AnyInterpolatedRole.swift
│ │ │ │ │ ├── FCPXML AnyRole.swift
│ │ │ │ │ ├── FCPXML RoleType.swift
│ │ │ │ │ ├── FCPXMLCollapsibleRole.swift
│ │ │ │ │ └── FCPXMLRole.swift
│ │ │ ├── Story
│ │ │ │ ├── Annotations
│ │ │ │ │ ├── FCPXML Caption.swift
│ │ │ │ │ ├── FCPXML Keyword.swift
│ │ │ │ │ └── FCPXML Marker.swift
│ │ │ │ ├── Clips
│ │ │ │ │ ├── FCPXML AssetClip.swift
│ │ │ │ │ ├── FCPXML Audio.swift
│ │ │ │ │ ├── FCPXML Audition.swift
│ │ │ │ │ ├── FCPXML Clip.swift
│ │ │ │ │ ├── FCPXML Gap.swift
│ │ │ │ │ ├── FCPXML RefClip.swift
│ │ │ │ │ ├── FCPXML Title.swift
│ │ │ │ │ ├── FCPXML Transition.swift
│ │ │ │ │ ├── FCPXML Video.swift
│ │ │ │ │ ├── MCClip
│ │ │ │ │ │ ├── FCPXML MCClip.swift
│ │ │ │ │ │ └── FCPXML MulticamSource.swift
│ │ │ │ │ └── SyncClip
│ │ │ │ │ │ ├── FCPXML SyncClip.swift
│ │ │ │ │ │ └── FCPXML SyncSource.swift
│ │ │ │ ├── FCPXML Sequence.swift
│ │ │ │ └── FCPXML Spine.swift
│ │ │ └── Structure
│ │ │ │ ├── FCPXML Event.swift
│ │ │ │ ├── FCPXML Library.swift
│ │ │ │ └── FCPXML Project.swift
│ │ ├── Occlusion
│ │ │ ├── FCPXML Element Occlusion.swift
│ │ │ └── FCPXML ElementOcclusion.swift
│ │ ├── Utilities
│ │ │ └── FCPXML Time Utilities.swift
│ │ └── XML
│ │ │ ├── FCPXML Attributes.swift
│ │ │ ├── FCPXML Clip Parsing.swift
│ │ │ ├── FCPXML Elements Parsing.swift
│ │ │ ├── FCPXML Metadata Parsing.swift
│ │ │ ├── FCPXML Resources Parsing.swift
│ │ │ ├── FCPXML Roles Parsing.swift
│ │ │ ├── FCPXML Root Parsing.swift
│ │ │ ├── FCPXML Time and Frame Rate Parsing.swift
│ │ │ └── XMLParsableAttributesKey.swift
│ └── FinalCutPro.swift
│ ├── MIDIFile
│ ├── Conversion
│ │ └── MIDIFile Converting DAWMarkers.swift
│ └── Errors
│ │ └── MIDIFile BuildError.swift
│ ├── ProTools
│ ├── ProTools.swift
│ └── SessionInfo
│ │ ├── Conversion
│ │ └── SessionInfo Extract DAWMarkers.swift
│ │ ├── Errors
│ │ └── SessionInfo ParseError.swift
│ │ ├── Messages
│ │ └── SessionInfo ParseMessage.swift
│ │ ├── Parse
│ │ ├── SessionInfo Parse Sections.swift
│ │ └── SessionInfo Parse.swift
│ │ ├── SessionInfo Clip.swift
│ │ ├── SessionInfo File.swift
│ │ ├── SessionInfo Init.swift
│ │ ├── SessionInfo Main.swift
│ │ ├── SessionInfo Marker.swift
│ │ ├── SessionInfo OrphanData.swift
│ │ ├── SessionInfo Plugin.swift
│ │ ├── SessionInfo TimeValue.swift
│ │ ├── SessionInfo TimeValueFormat.swift
│ │ ├── SessionInfo Track.swift
│ │ ├── SessionInfo Versions.swift
│ │ └── SessionInfo.swift
│ └── Utilities
│ ├── Utilities.swift
│ └── XML Utilities.swift
└── Tests
└── DAWFileKitTests
├── Cubase
├── Cubase TrackArchive BasicMarkers.swift
├── Cubase TrackArchive Helper Tests.swift
├── Cubase TrackArchive MusicalAndLinearTest.swift
├── Cubase TrackArchive RoundingTest.swift
├── Cubase TrackArchive init converting.swift
├── Cubase TrackArchive xmlString.swift
└── Resources
│ └── Cubase TrackArchive XML Exports
│ ├── BasicMarkers.xml
│ ├── MusicalAndLinearTest.xml
│ └── RoundingTest.xml
├── DAW Agnostic Types
├── DAWMarker Codable Tests.swift
├── DAWMarker Comparable Tests.swift
└── DAWMarker Conversions Tests.swift
├── DAWFileKitTests Constants.swift
├── FinalCutPro
├── FCPXMLTestCase.swift
├── File Tests
│ ├── FinalCutPro FCPXML 23.98.swift
│ ├── FinalCutPro FCPXML 24.swift
│ ├── FinalCutPro FCPXML 24With25Media.swift
│ ├── FinalCutPro FCPXML 25i.swift
│ ├── FinalCutPro FCPXML 29.97.swift
│ ├── FinalCutPro FCPXML 29.97d.swift
│ ├── FinalCutPro FCPXML 30.swift
│ ├── FinalCutPro FCPXML 50.swift
│ ├── FinalCutPro FCPXML 59.94.swift
│ ├── FinalCutPro FCPXML 60.swift
│ ├── FinalCutPro FCPXML Annotations.swift
│ ├── FinalCutPro FCPXML AudioOnly.swift
│ ├── FinalCutPro FCPXML AuditionMarkers.swift
│ ├── FinalCutPro FCPXML AuditionMarkers2.swift
│ ├── FinalCutPro FCPXML AuditionMarkers3.swift
│ ├── FinalCutPro FCPXML BasicMarkers.swift
│ ├── FinalCutPro FCPXML BasicMarkers_1HourProjectStart.swift
│ ├── FinalCutPro FCPXML ClipMetadata.swift
│ ├── FinalCutPro FCPXML Complex.swift
│ ├── FinalCutPro FCPXML CompoundClips.swift
│ ├── FinalCutPro FCPXML DisabledClips.swift
│ ├── FinalCutPro FCPXML Keywords.swift
│ ├── FinalCutPro FCPXML MulticamMarkers.swift
│ ├── FinalCutPro FCPXML MulticamMarkers2.swift
│ ├── FinalCutPro FCPXML Occlusion.swift
│ ├── FinalCutPro FCPXML Occlusion2.swift
│ ├── FinalCutPro FCPXML Occlusion3.swift
│ ├── FinalCutPro FCPXML RolesList.swift
│ ├── FinalCutPro FCPXML StandaloneAssetClip.swift
│ ├── FinalCutPro FCPXML StandaloneLibraryEventClip.swift
│ ├── FinalCutPro FCPXML StandaloneRefClip.swift
│ ├── FinalCutPro FCPXML SyncClip.swift
│ ├── FinalCutPro FCPXML SyncClipRoles.swift
│ ├── FinalCutPro FCPXML SyncClipRoles2.swift
│ ├── FinalCutPro FCPXML TitlesRoles.swift
│ ├── FinalCutPro FCPXML TransitionMarkers1.swift
│ ├── FinalCutPro FCPXML TransitionMarkers2.swift
│ └── FinalCutPro FCPXML TwoClipsMarkers.swift
├── Logic and Parsing
│ ├── FinalCutPro FCPXML Calculations.swift
│ ├── FinalCutPro FCPXML Element Init Tests.swift
│ ├── FinalCutPro FCPXML Format Info.swift
│ ├── FinalCutPro FCPXML Frame Data Tests.swift
│ ├── FinalCutPro FCPXML Library Tests.swift
│ ├── FinalCutPro FCPXML Roles Parsing.swift
│ ├── FinalCutPro FCPXML Root Version Tests.swift
│ └── FinalCutPro FCPXML Structure.swift
└── Resources
│ └── FCPXML Exports
│ ├── 23.98.fcpxml
│ ├── 24.fcpxml
│ ├── 24With25Media.fcpxml
│ ├── 25i.fcpxml
│ ├── 29.97.fcpxml
│ ├── 29.97d.fcpxml
│ ├── 30.fcpxml
│ ├── 50.fcpxml
│ ├── 59.94.fcpxml
│ ├── 60.fcpxml
│ ├── Annotations.fcpxml
│ ├── AudioOnly.fcpxml
│ ├── AuditionMarkers.fcpxml
│ ├── AuditionMarkers2.fcpxml
│ ├── AuditionMarkers3.fcpxml
│ ├── BasicMarkers.fcpxml
│ ├── BasicMarkers_1HourProjectStart.fcpxml
│ ├── ClipMetadata.fcpxml
│ ├── Complex.fcpxml
│ ├── CompoundClips.fcpxml
│ ├── DisabledClips.fcpxml
│ ├── Keywords.fcpxml
│ ├── MulticamMarkers.fcpxml
│ ├── MulticamMarkers2.fcpxml
│ ├── Occlusion.fcpxml
│ ├── Occlusion2.fcpxml
│ ├── Occlusion3.fcpxml
│ ├── RolesList.fcpxml
│ ├── StandaloneAssetClip.fcpxml
│ ├── StandaloneLibraryEventClip.fcpxml
│ ├── StandaloneRefClip.fcpxml
│ ├── Structure.fcpxml
│ ├── SyncClip.fcpxml
│ ├── SyncClipRoles.fcpxml
│ ├── SyncClipRoles2.fcpxml
│ ├── TitlesRoles.fcpxml
│ ├── TransitionMarkers1.fcpxml
│ ├── TransitionMarkers2.fcpxml
│ └── TwoClipsMarkers.fcpxml
└── ProTools
├── ProTools SessionText 2023.12 Markers.swift
├── ProTools SessionText EmptySession.swift
├── ProTools SessionText ExtendedChars.swift
├── ProTools SessionText FPPFinal.swift
├── ProTools SessionText NewLinesAndTabs.swift
├── ProTools SessionText OneOfEverything.swift
├── ProTools SessionText OrphanData.swift
├── ProTools SessionText Plugins.swift
├── ProTools SessionText SimpleTest.swift
├── ProTools SessionText Time Formats BarsBeats.swift
├── ProTools SessionText TracksOnly.swift
├── Resources
└── PT Session Text Exports
│ ├── SessionText_EmptySession_23-976fps_DefaultExportOptions_PT2020.3.txt
│ ├── SessionText_ExtendedChars_TextEditFormat_PT2023.3.txt
│ ├── SessionText_ExtendedChars_UTF8Format_PT2023.3.txt
│ ├── SessionText_FPPFinal_23-976fps_DefaultExportOptions_PT2020.3.txt
│ ├── SessionText_MarkerRulersAndTrackMarkers_PT2023.12.txt
│ ├── SessionText_NewLinesAndTabs_DefaultExportOptions_PT2023.6.txt
│ ├── SessionText_OneOfEverything_23-976fps_DefaultExportOptions_PT2020.3.txt
│ ├── SessionText_Plugins_23-976fps_DefaultExportOptions_PT2020.3.txt
│ ├── SessionText_SimpleTest_23-976fps_DefaultExportOptions_PT2020.3.txt
│ ├── SessionText_TimeFormats_BarsBeats_PT2022.9.txt
│ ├── SessionText_TimeFormats_BarsBeats_ShowSubframes_PT2022.9.txt
│ ├── SessionText_TimeFormats_FeetFrames_PT2022.9.txt
│ ├── SessionText_TimeFormats_FeetFrames_ShowSubframes_PT2022.9.txt
│ ├── SessionText_TimeFormats_MinSecs_PT2022.9.txt
│ ├── SessionText_TimeFormats_MinSecs_ShowSubframes_PT2022.9.txt
│ ├── SessionText_TimeFormats_Samples_PT2022.9.txt
│ ├── SessionText_TimeFormats_Samples_ShowSubframes_PT2022.9.txt
│ ├── SessionText_TimeFormats_Timecode_PT2022.9.txt
│ ├── SessionText_TimeFormats_Timecode_ShowSubframes_PT2022.9.txt
│ ├── SessionText_TracksOnly_OnlyTrackEDLs_PT2023.6.txt
│ └── SessionText_UnrecognizedSection_23-976fps_DefaultExportOptions_PT2020.3.txt
├── TimeValue Tests.swift
└── TimeValueFormat Tests.swift
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: orchetect
4 | # patreon: # Replace with a single Patreon username
5 | # open_collective: # Replace with a single Open Collective username
6 | # ko_fi: # Replace with a single Ko-fi username
7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | # liberapay: # Replace with a single Liberapay username
10 | # issuehunt: # Replace with a single IssueHunt username
11 | # otechie: # Replace with a single Otechie username
12 | # lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
14 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: Bug report
2 | description: Create a bug report about a reproducible problem.
3 | labels: bug
4 | body:
5 | - type: textarea
6 | id: bug-description
7 | attributes:
8 | label: Bug Description, Steps to Reproduce, Crash Logs, Screenshots, etc.
9 | description: "A clear and concise description of the bug and steps to reproduce. Include system details (OS version) and build environment particulars (Xcode version, etc.)."
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Feature request
4 | url: https://github.com/orchetect/DAWFileKit/discussions
5 | about: Suggest new features or improvements.
6 | - name: I need help setting up or troubleshooting
7 | url: https://github.com/orchetect/DAWFileKit/discussions
8 | about: Questions not answered in the documentation, discussions forum, or example projects.
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Custom
2 | [Dd]ev/
3 |
4 | # Xcode
5 |
6 | # macOS
7 | .DS_Store
8 |
9 | ## Build generated
10 | build/
11 | DerivedData/
12 |
13 | ## Various settings
14 | *.pbxuser
15 | !default.pbxuser
16 | *.mode1v3
17 | !default.mode1v3
18 | *.mode2v3
19 | !default.mode2v3
20 | *.perspectivev3
21 | !default.perspectivev3
22 | xcuserdata/
23 |
24 | ## Other
25 | *.moved-aside
26 | *.xccheckout
27 | *.xcscmblueprint
28 |
29 | ## Obj-C/Swift specific
30 | *.hmap
31 | *.ipa
32 | *.dSYM.zip
33 | *.dSYM
34 |
35 | ## Playgrounds
36 | timeline.xctimeline
37 | playground.xcworkspace
38 |
39 | ## SPM support in Xcode
40 | # .swiftpm - for shared CI schemes we need these checked in:
41 | # -> .swiftpm/xcode/package.xcworkspace
42 | # -> .swiftpm/xcode/xcshareddata/xcschemes/*.*
43 |
44 | # Swift Package Manager
45 | #
46 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
47 | Packages/
48 | Package.pins
49 | Package.resolved
50 | .build/
51 |
52 | # CocoaPods
53 | #
54 | # We recommend against adding the Pods directory to your .gitignore. However
55 | # you should judge for yourself, the pros and cons are mentioned at:
56 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
57 |
58 | Pods/
59 |
60 | # Carthage
61 | #
62 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
63 | # Carthage/Checkouts
64 |
65 | Carthage/Build
66 |
67 | # fastlane
68 | #
69 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
70 | # screenshots whenever they are needed.
71 | # For more information about the recommended setup visit:
72 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
73 |
74 | fastlane/report.xml
75 | fastlane/Preview.html
76 | fastlane/screenshots/**/*.png
77 | fastlane/test_output
78 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 5.8
--------------------------------------------------------------------------------
/.swiftformat:
--------------------------------------------------------------------------------
1 | --acronyms ID,URL,UUID
2 | --allman false
3 | --assetliterals visual-width
4 | --beforemarks
5 | --binarygrouping 8,8
6 | --categorymark "MARK: %c"
7 | --classthreshold 0
8 | --closingparen balanced
9 | --closurevoid remove
10 | --commas inline
11 | --conflictmarkers reject
12 | --decimalgrouping ignore
13 | --elseposition same-line
14 | --emptybraces spaced
15 | --enumthreshold 0
16 | --exponentcase lowercase
17 | --exponentgrouping disabled
18 | --extensionacl on-declarations
19 | --extensionlength 0
20 | --extensionmark "MARK: - %t + %c"
21 | --fractiongrouping disabled
22 | --fragment false
23 | --funcattributes prev-line
24 | --groupedextension "MARK: %c"
25 | --guardelse auto
26 | --header "\n {file}\n DAWFileKit • https://github.com/orchetect/DAWFileKit\n © {year} Steffan Andrews • Licensed under MIT License\n"
27 | --hexgrouping 4,8
28 | --hexliteralcase uppercase
29 | --ifdef no-indent
30 | --importgrouping alpha
31 | --indent 4
32 | --indentcase false
33 | --indentstrings true
34 | --lifecycle
35 | --lineaftermarks true
36 | --linebreaks lf
37 | --markcategories true
38 | --markextensions always
39 | --marktypes always
40 | --maxwidth 100
41 | --modifierorder
42 | --nevertrailing
43 | --nospaceoperators
44 | --nowrapoperators
45 | --octalgrouping 4,8
46 | --operatorfunc spaced
47 | --organizetypes actor,class,enum,struct
48 | --patternlet hoist
49 | --ranges spaced
50 | --redundanttype infer-locals-only
51 | --self remove
52 | --selfrequired
53 | --semicolons inline
54 | --shortoptionals always
55 | --smarttabs enabled
56 | --stripunusedargs always
57 | --structthreshold 0
58 | --tabwidth unspecified
59 | --trailingclosures
60 | --trimwhitespace nonblank-lines
61 | --typeattributes preserve
62 | --typemark "MARK: - %t"
63 | --varattributes preserve
64 | --voidtype void
65 | --wraparguments before-first
66 | --wrapcollections before-first
67 | --wrapconditions after-first
68 | --wrapparameters before-first
69 | --wrapreturntype preserve
70 | --wrapternary before-operators
71 | --wraptypealiases before-first
72 | --xcodeindentation enabled
73 | --yodaswap always
74 | --disable blankLinesAroundMark,consecutiveSpaces,preferKeyPath,redundantParens,sortDeclarations,sortedImports,unusedArguments
75 | --enable blankLinesBetweenImports,blockComments,isEmpty,wrapEnumCases
76 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Steffan Andrews - https://github.com/orchetect
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.8
2 | // (be sure to update the .swift-version file when this Swift version changes)
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "DAWFileKit",
8 | platforms: [
9 | .macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6)
10 | ],
11 | products: [
12 | .library(name: "DAWFileKit", targets: ["DAWFileKit"])
13 | ],
14 | dependencies: [
15 | .package(url: "https://github.com/orchetect/OTCore", from: "1.7.3"),
16 | .package(url: "https://github.com/orchetect/TimecodeKit", from: "2.3.3"),
17 | .package(url: "https://github.com/orchetect/MIDIKit", from: "0.10.0")
18 | ],
19 | targets: [
20 | .target(
21 | name: "DAWFileKit",
22 | dependencies: [
23 | "OTCore",
24 | "TimecodeKit",
25 | .product(name: "MIDIKitSMF", package: "MIDIKit")
26 | ]
27 | ),
28 | .testTarget(
29 | name: "DAWFileKitTests",
30 | dependencies: ["DAWFileKit"],
31 | resources: [
32 | .copy("Cubase/Resources/Cubase TrackArchive XML Exports"),
33 | .copy("ProTools/Resources/PT Session Text Exports"),
34 | .copy("FinalCutPro/Resources/FCPXML Exports")
35 | ]
36 | )
37 | ]
38 | )
39 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/Cubase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Cubase.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 | import TimecodeKit
9 |
10 | /// Collection of methods and structures related to Cubase.
11 | /// Do not instance; use methods within directly.
12 | public enum Cubase {
13 | public typealias PPQ = Double
14 | public typealias Tempo = Double
15 |
16 | /// `Timecode` setting for `.subFramesBase`.
17 | /// Cubase uses 80 subframes per frame.
18 | public static let timecodeSubFramesBase: Timecode.SubFramesBase = .max80SubFrames
19 |
20 | /// `Timecode` setting for `.upperLimit`.
21 | /// Cubase allows for up to 100 days, not confined to a 24-hour SMPTE timecode clock.
22 | public static let timecodeUpperLimit: Timecode.UpperLimit = .max100Days
23 |
24 | /// `Timecode` setting for `.stringFormat`.
25 | public static let timecodeStringFormat: Timecode.StringFormat = []
26 | }
27 |
28 | extension Cubase {
29 | /// `Timecode` struct template.
30 | public static func formTimecode(
31 | realTime: TimeInterval,
32 | at rate: TimecodeFrameRate
33 | ) throws -> Timecode {
34 | try Timecode(
35 | .realTime(seconds: realTime),
36 | at: rate,
37 | base: timecodeSubFramesBase,
38 | limit: timecodeUpperLimit
39 | )
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/TrackArchive/Errors/TrackArchive EncodeError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrackArchive EncodeError.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension Cubase.TrackArchive {
12 | public enum EncodeError: Error {
13 | case general(String)
14 | }
15 | }
16 |
17 | #endif
18 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/TrackArchive/Errors/TrackArchive ParseError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrackArchive ParseError.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension Cubase.TrackArchive {
12 | /// Cubase track archive XML parsing error.
13 | public enum ParseError: Error {
14 | case general(String)
15 | }
16 | }
17 |
18 | #endif
19 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/TrackArchive/Messages/TrackArchive EncodeMessage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrackArchive EncodeMessage.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension Cubase.TrackArchive {
12 | public enum EncodeMessage: Error {
13 | /// Info message.
14 | /// Can be disregarded and only useful for debugging.
15 | case info(String)
16 |
17 | /// Error message.
18 | /// Something was malformed or data format was not expected.
19 | case error(String)
20 | }
21 | }
22 |
23 | // MARK: - Extensions
24 |
25 | extension Collection where Element == Cubase.TrackArchive.EncodeMessage {
26 | /// Returns all `.info` cases as enum-unwrapped Strings.
27 | public var infos: [String] {
28 | reduce(into: [String]()) {
29 | switch $1 {
30 | case let .info(message):
31 | $0.append(message)
32 | default:
33 | break
34 | }
35 | }
36 | }
37 |
38 | /// Returns all `.error` cases as enum-unwrapped Strings.
39 | public var errors: [String] {
40 | reduce(into: [String]()) {
41 | switch $1 {
42 | case let .error(message):
43 | $0.append(message)
44 | default:
45 | break
46 | }
47 | }
48 | }
49 | }
50 |
51 | #endif
52 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/TrackArchive/Messages/TrackArchive ParseMessage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrackArchive ParseMessage.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension Cubase.TrackArchive {
12 | public enum ParseMessage: Error {
13 | /// Info message.
14 | /// Can be disregarded and only useful for debugging.
15 | case info(String)
16 |
17 | /// Error message.
18 | /// Something was malformed or data format was not expected.
19 | case error(String)
20 | }
21 | }
22 |
23 | // MARK: - Extensions
24 |
25 | extension Collection where Element == Cubase.TrackArchive.ParseMessage {
26 | /// Returns all `.info` cases as enum-unwrapped Strings.
27 | public var infos: [String] {
28 | reduce(into: [String]()) {
29 | switch $1 {
30 | case let .info(message):
31 | $0.append(message)
32 | default:
33 | break
34 | }
35 | }
36 | }
37 |
38 | /// Returns all `.error` cases as enum-unwrapped Strings.
39 | public var errors: [String] {
40 | reduce(into: [String]()) {
41 | switch $1 {
42 | case let .error(message):
43 | $0.append(message)
44 | default:
45 | break
46 | }
47 | }
48 | }
49 | }
50 |
51 | #endif
52 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/TrackArchive/Protocols/CubaseTrackArchiveMarker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CubaseTrackArchiveMarker.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | /// Protocol that DAWFileKit `Cubase.TrackArchive` markers conform to.
13 | public protocol CubaseTrackArchiveMarker {
14 | var name: String { get set }
15 |
16 | var startTimecode: Timecode { get set }
17 | var startRealTime: TimeInterval? { get set }
18 | }
19 |
20 | #endif
21 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/TrackArchive/Protocols/CubaseTrackArchiveTrack.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CubaseTrackArchiveTrack.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | /// Protocol that DAWFileKit `Cubase.TrackArchive` tracks conform to.
12 | public protocol CubaseTrackArchiveTrack {
13 | var name: String? { get set }
14 | }
15 |
16 | #endif
17 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/TrackArchive/TrackArchive CycleMarker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrackArchive CycleMarker.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | extension Cubase.TrackArchive {
13 | /// Represents a cycle marker event and its contents.
14 | public struct CycleMarker: CubaseTrackArchiveMarker {
15 | public var name: String = ""
16 |
17 | public var startTimecode: Timecode
18 | public var startRealTime: TimeInterval?
19 |
20 | public var lengthTimecode: Timecode
21 | public var lengthRealTime: TimeInterval?
22 |
23 | public init(
24 | name: String,
25 | startTimecode: Timecode,
26 | startRealTime: TimeInterval? = nil,
27 | lengthTimecode: Timecode,
28 | lengthRealTime: TimeInterval? = nil
29 | ) {
30 | self.name = name
31 |
32 | self.startTimecode = startTimecode
33 | self.startRealTime = startRealTime
34 |
35 | self.lengthTimecode = lengthTimecode
36 | self.lengthRealTime = lengthRealTime
37 | }
38 | }
39 | }
40 |
41 | #endif
42 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/TrackArchive/TrackArchive Init.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrackArchive Init.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import OTCore
11 | import TimecodeKit
12 |
13 | // MARK: - Init
14 |
15 | extension Cubase.TrackArchive {
16 | /// Parse Track Archive XML file contents exported from Cubase.
17 | public init(fileContent data: Data) throws {
18 | var dummy: [ParseMessage] = []
19 | try self.init(fileContent: data, messages: &dummy)
20 | }
21 |
22 | /// Parse Track Archive XML file contents exported from Cubase.
23 | public init(
24 | fileContent data: Data,
25 | messages: inout [ParseMessage]
26 | ) throws {
27 | let xmlDocument = try XMLDocument(data: data)
28 | let parsed = try Self.parse(fileContent: xmlDocument)
29 | self = parsed.trackArchive
30 | messages = parsed.messages
31 | }
32 | }
33 |
34 | extension Cubase.TrackArchive {
35 | /// Parse Track Archive XML file contents exported from Cubase.
36 | public init(fileContent xml: XMLDocument) throws {
37 | var dummy: [ParseMessage] = []
38 | try self.init(fileContent: xml, messages: &dummy)
39 | }
40 |
41 | /// Parse Track Archive XML file contents exported from Cubase.
42 | public init(
43 | fileContent xml: XMLDocument,
44 | messages: inout [ParseMessage]
45 | ) throws {
46 | let parsed = try Self.parse(fileContent: xml)
47 | self = parsed.trackArchive
48 | messages = parsed.messages
49 | }
50 | }
51 |
52 | extension Cubase.TrackArchive {
53 | /// Parse Track Archive XML file contents exported from Cubase.
54 | public init(fileContent xmlRoot: XMLElement) {
55 | var dummy: [ParseMessage] = []
56 | self.init(fileContent: xmlRoot, messages: &dummy)
57 | }
58 |
59 | /// Parse Track Archive XML file contents exported from Cubase.
60 | public init(
61 | fileContent xmlRoot: XMLElement,
62 | messages: inout [ParseMessage]
63 | ) {
64 | let parsed = Self.parse(fileContent: xmlRoot)
65 | self = parsed.trackArchive
66 | messages = parsed.messages
67 | }
68 | }
69 |
70 | #endif
71 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/TrackArchive/TrackArchive Main.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrackArchive Main.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | extension Cubase.TrackArchive {
13 | /// Contains the global session meta-data.
14 | public struct Main {
15 | public var startTimecode: Timecode? // 'Start'.'Time' (float, load as double)
16 | public var startTimeSeconds: TimeInterval? // 'Start'.'Time' (float, load as double)
17 |
18 | // public var startTimeDomain: ? // 'Start'.'Domain'.'Type' &
19 | // 'Start'.'Domain'.'Period'
20 | public var lengthTimecode: Timecode? // 'Length'.'Time' (float, load as double)
21 | // public var lengthTimeDomain: ? // 'Length'.'Domain'.'Type' &
22 | // 'Start'.'Domain'.'Period'
23 |
24 | public var frameRate: TimecodeFrameRate? // 'FrameType'
25 |
26 | // public var timeType: ? // 'TimeType'
27 | public var barOffset: Int? // 'BarOffset'
28 |
29 | public var sampleRate: Double? // 'SampleRate'
30 | public var bitDepth: Int? // 'SampleSize'
31 |
32 | // SampleFormatSize
33 |
34 | // 'RecordFile'
35 | // 'RecordFileType' ...
36 |
37 | // 'PanLaw'
38 | // 'VolumeMax'
39 |
40 | // 'HmtType'
41 | public var hmtDepth: Int? // 'HmtDepth' (percentage)
42 | }
43 | }
44 |
45 | #endif
46 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/TrackArchive/TrackArchive Marker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrackArchive Marker.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | extension Cubase.TrackArchive {
13 | /// Represents a marker event and its contents.
14 | public struct Marker: CubaseTrackArchiveMarker {
15 | public var name: String = ""
16 |
17 | public var startTimecode: Timecode
18 | public var startRealTime: TimeInterval?
19 |
20 | public init(
21 | name: String,
22 | startTimecode: Timecode,
23 | startRealTime: TimeInterval? = nil
24 | ) {
25 | self.name = name
26 |
27 | self.startTimecode = startTimecode
28 | self.startRealTime = startRealTime
29 | }
30 | }
31 | }
32 |
33 | #endif
34 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/TrackArchive/TrackArchive MarkerTrack.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrackArchive MarkerTrack.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension Cubase.TrackArchive {
12 | /// Represents a track and its contents.
13 | public struct MarkerTrack: CubaseTrackArchiveTrack {
14 | public var name: String?
15 |
16 | public var events: [CubaseTrackArchiveMarker] = []
17 |
18 | public init() { }
19 | }
20 | }
21 |
22 | #endif
23 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/TrackArchive/TrackArchive OrphanTrack.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrackArchive OrphanTrack.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension Cubase.TrackArchive {
12 | /// An orphan track that could not be parsed.
13 | public struct OrphanTrack: CubaseTrackArchiveTrack {
14 | public var name: String?
15 |
16 | public let rawXMLContent: String
17 | }
18 | }
19 |
20 | #endif
21 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/TrackArchive/TrackArchive TempoTrack.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrackArchive TempoTrack.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension Cubase.TrackArchive {
12 | /// Represents a cycle marker event and its contents.
13 | public struct TempoTrack: CubaseTrackArchiveTrack {
14 | public var name: String?
15 | public var events: [Event] = []
16 | }
17 | }
18 |
19 | extension Cubase.TrackArchive.TempoTrack {
20 | /// A tempo track event.
21 | public struct Event {
22 | public var startTimeAsPPQ: Cubase.PPQ
23 | public var tempo: Cubase.Tempo
24 | public var type: TempoEventType
25 | }
26 | }
27 |
28 | extension Cubase.TrackArchive.TempoTrack.Event {
29 | /// A tempo track event type.
30 | public enum TempoEventType {
31 | case jump
32 | case ramp
33 | }
34 | }
35 |
36 | #endif
37 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/TrackArchive/TrackArchive TimeType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrackArchive TimeType.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension Cubase.TrackArchive {
12 | /// Cubase Track Archive time type.
13 | public enum TimeType: Int {
14 | case secondsOrBarsAndBeats = 0 // seconds and bars+beats both show up as 0
15 | case timecode = 4 // and 13?
16 | case samples = 10
17 | case user = 11
18 | }
19 | }
20 |
21 | #endif
22 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/TrackArchive/TrackArchive TrackTimeDomain.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrackArchive TrackTimeDomain.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension Cubase.TrackArchive {
12 | /// Cubase Track Archive track time domain.
13 | public enum TrackTimeDomain: Int {
14 | /// Bars & beats timebase - computations are against PPQ base and tempo
15 | case musical = 0
16 |
17 | /// Time linear timebase - real / absolute time
18 | case linear = 1
19 | }
20 | }
21 |
22 | #endif
23 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Cubase/TrackArchive/TrackArchive.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrackArchive.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | // MARK: - Cubase.TrackArchive
13 |
14 | extension Cubase {
15 | /// Contains parsed data after reading a Cubase Track Archive XML file.
16 | public struct TrackArchive {
17 | // MARK: Contents
18 |
19 | /// Meta data contained in the main header of the data file.
20 | public var main = Main()
21 |
22 | /// Tempo track.
23 | /// (Essentially, a session can contain only one tempo track, but there is not a "tempo
24 | /// track" in the XML file; instead, tempo events are written to the first actual track.)
25 | public var tempoTrack = TempoTrack()
26 |
27 | /// Tracks listing.
28 | public var tracks: [CubaseTrackArchiveTrack]?
29 |
30 | // MARK: - Default init
31 |
32 | public init() { }
33 | }
34 | }
35 |
36 | // MARK: - Constants
37 |
38 | extension Cubase.TrackArchive {
39 | /// Static PPQ value used in Track Archive XML files (allegedly, until proven otherwise?)
40 | /// Changing PPQbase in Cubase preferences has no effect on this value.
41 | internal static let xmlPPQ = 480
42 |
43 | /// Array of file types for use with `NSOpenPanel` / `NSSavePanel`.
44 | public static let fileTypes = ["public.xml", "xml"]
45 |
46 | /// Frame rates and their numeric identifier as stored in the XML.
47 | internal static let frameRateTable: [Int: TimecodeFrameRate] =
48 | [
49 | 02: .fps24,
50 | 03: .fps25,
51 | 04: .fps29_97,
52 | 05: .fps30,
53 | 06: .fps29_97d,
54 | 07: .fps30d,
55 | 12: .fps23_976,
56 | 13: .fps24_98,
57 | 14: .fps50,
58 | 15: .fps59_94,
59 | 16: .fps60
60 | ]
61 |
62 | internal static let trackTypeTable: [String: CubaseTrackArchiveTrack.Type] = [
63 | "MMarkerTrackEvent": MarkerTrack.self
64 | // TODO: add additional track types in future
65 | ]
66 | }
67 |
68 | #endif
69 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/DAW Agnostic Types/DAWMarker Storage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DAWMarker Storage.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import TimecodeKit
8 |
9 | extension DAWMarker {
10 | public struct Storage: Equatable, Hashable, Codable {
11 | /// Time value.
12 | public let value: Value
13 |
14 | /// The original frame rate that was associated with the `timeStorage` value.
15 | public let frameRate: TimecodeFrameRate
16 |
17 | /// The original timecode subframes divisor.
18 | public let base: Timecode.SubFramesBase
19 |
20 | public init(
21 | value: Value,
22 | frameRate: TimecodeFrameRate,
23 | base: Timecode.SubFramesBase
24 | ) {
25 | self.value = value
26 | self.frameRate = frameRate
27 | self.base = base
28 | }
29 | }
30 | }
31 |
32 | extension DAWMarker.Storage: Sendable { }
33 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/DAW Agnostic Types/DAWMarker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DAWMarker.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 |
9 | /// DAW-agnostic timeline marker.
10 | public struct DAWMarker: Codable {
11 | // MARK: Contents
12 |
13 | /// The core time value storage.
14 | /// Regardless of type, the value must always represent time elapsed from zero (00:00:00:00).
15 | public var timeStorage: Storage? = nil
16 |
17 | /// Main text of the marker.
18 | public var name: String = ""
19 |
20 | /// Comment associated with marker. Not all DAWs support comments; mainly Pro Tools.
21 | public var comment: String?
22 |
23 | // MARK: init
24 |
25 | public init() { }
26 |
27 | public init(
28 | storage: Storage? = nil,
29 | name: String = "",
30 | comment: String? = nil
31 | ) {
32 | timeStorage = storage
33 | self.name = name
34 | self.comment = comment
35 | }
36 | }
37 |
38 | extension DAWMarker: Sendable { }
39 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/DAW Agnostic Types/DAWMarkerTrack.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DAWMarkerTrack.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2024 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 |
9 | /// DAW-agnostic timeline capable of containing markers only.
10 | public struct DAWMarkerTrack: Codable {
11 | // MARK: Contents
12 |
13 | /// Track name.
14 | public var name: String = ""
15 |
16 | public var trackType: DAWTrackType
17 |
18 | /// Markers contained in the track.
19 | public var markers: [DAWMarker]
20 |
21 | // MARK: init
22 |
23 | public init(
24 | trackType: DAWTrackType,
25 | name: String = "",
26 | markers: [DAWMarker] = []
27 | ) {
28 | self.trackType = trackType
29 | self.name = name
30 | self.markers = markers
31 | }
32 | }
33 |
34 | extension DAWMarkerTrack: Sendable { }
35 |
36 | // MARK: - Collection Methods
37 |
38 | extension Collection where Element == DAWMarkerTrack {
39 | public func first(trackNamed name: String, trackType: DAWTrackType) -> Element? {
40 | guard let index = firstIndex(trackNamed: name, trackType: trackType) else { return nil }
41 | return self[index]
42 | }
43 |
44 | public func firstIndex(trackNamed name: String, trackType: DAWTrackType) -> Index? {
45 | firstIndex {
46 | $0.name == name && $0.trackType == trackType
47 | }
48 | }
49 |
50 | public func first(trackNamed name: String) -> Element? {
51 | guard let index = firstIndex(trackNamed: name) else { return nil }
52 | return self[index]
53 | }
54 |
55 | public func firstIndex(trackNamed name: String) -> Index? {
56 | firstIndex { $0.name == name }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/DAW Agnostic Types/DAWTrackType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DAWTrackType.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2024 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 |
9 | public enum DAWTrackType: Equatable, Hashable, Codable {
10 | /// Ruler.
11 | /// Typically pinned to the GUI's top edge of the timeline.
12 | case ruler
13 |
14 | /// Track.
15 | /// A track used in a timeline. Usually reorder-able, deletable, and duplicatable.
16 | case track
17 | }
18 |
19 | extension DAWTrackType: Identifiable {
20 | public var id: Self { self }
21 | }
22 |
23 | extension DAWTrackType: Sendable { }
24 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Context/FCPXML ElementContext.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML ElementContext.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import OTCore
11 |
12 | extension FinalCutPro.FCPXML {
13 | /// Element context identity and value builder to produce strongly-typed information gathered
14 | /// from an element.
15 | public struct ElementContext {
16 | public let valueBuilder: ValueBuilder
17 |
18 | public init(
19 | value: @escaping ValueBuilder
20 | ) {
21 | valueBuilder = value
22 | }
23 |
24 | /// Returns the context value using the associated context value builder.
25 | ///
26 | /// - Parameters:
27 | /// - element: The element.
28 | /// - breadcrumbs: All ancestors traversed including resource jumps.
29 | /// Ordered nearest to furthest ancestor.
30 | /// - resources: The document's `resources` container element.
31 | /// If `nil`, the `resources` found in the document will be used if present.
32 | public func value(
33 | from element: XMLElement,
34 | breadcrumbs: [XMLElement],
35 | resources: XMLElement? // `resources` container element
36 | ) -> Value {
37 | let tools = Tools(
38 | element: element,
39 | breadcrumbs: breadcrumbs,
40 | resources: resources
41 | )
42 |
43 | let resources = resources
44 | ?? element.fcpRootResources
45 | ?? XMLElement(name: ElementType.resources.rawValue)
46 |
47 | return valueBuilder(
48 | element,
49 | breadcrumbs,
50 | resources,
51 | tools
52 | )
53 | }
54 | }
55 | }
56 |
57 | extension FinalCutPro.FCPXML.ElementContext {
58 | /// Context value builder closure for an element.
59 | ///
60 | /// - Parameters:
61 | /// - element: The element.
62 | /// - breadcrumbs: All ancestors traversed including resource jumps.
63 | /// Ordered nearest to furthest ancestor.
64 | /// - resources: The document's `resources` container element provided for convenience.
65 | /// - tools: Convenience methods for building context.
66 | public typealias ValueBuilder = (
67 | _ element: XMLElement,
68 | _ breadcrumbs: [XMLElement],
69 | _ resources: XMLElement, // `resources` container element
70 | _ tools: Tools
71 | ) -> Value
72 | }
73 |
74 | #endif
75 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Context/FCPXML FrameRateSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML FrameRateSource.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 | import OTCore
12 |
13 | extension FinalCutPro.FCPXML {
14 | /// Frame rate source for an extracted element.
15 | public enum FrameRateSource {
16 | /// Derive frame rate from main timeline.
17 | case mainTimeline
18 |
19 | /// Derive frame rate from the element's local timeline or closest parent timeline.
20 | case localToElement
21 |
22 | /// Provide an arbitrary frame rate to use.
23 | ///
24 | /// This is generally not recommended unless conversion to a different frame rate than the
25 | /// one used is desired.
26 | case rate(_ rate: TimecodeFrameRate)
27 | }
28 | }
29 |
30 | extension FinalCutPro.FCPXML.FrameRateSource: Sendable { }
31 |
32 | #endif
33 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Element Types/ElementModelType/FCPXML AnyElementModelType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML AnyElementModelType.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | // MARK: - AnyElementModelType
12 |
13 | extension FinalCutPro.FCPXML {
14 | public struct AnyElementModelType: Sendable {
15 | public var base: any FCPXMLElementModelTypeProtocol
16 |
17 | public var supportedElementTypes: Set {
18 | base.supportedElementTypes
19 | }
20 |
21 | public init(base: T) {
22 | self.base = base
23 | }
24 | }
25 | }
26 |
27 | #endif
28 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Element Types/ElementModelType/FCPXML ElementModelType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML ElementModelType.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | // MARK: - ElementModelType
12 |
13 | extension FinalCutPro.FCPXML {
14 | public struct ElementModelType: FCPXMLElementModelTypeProtocol {
15 | public var supportedElementTypes: Set {
16 | ModelType.supportedElementTypes
17 | }
18 |
19 | init() { }
20 | }
21 | }
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Element Types/ElementModelType/FCPXMLElementModelTypeProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementModelTypeProtocol.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | public protocol FCPXMLElementModelTypeProtocol
12 | where Self: Equatable, Self: Hashable, Self: Sendable
13 | {
14 | associatedtype ModelType: FCPXMLElement
15 | var supportedElementTypes: Set { get }
16 | }
17 |
18 | extension FCPXMLElementModelTypeProtocol {
19 | public var supportedElementTypes: Set {
20 | ModelType.supportedElementTypes
21 | }
22 | }
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Errors/FCPXML ParseError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML ParseError.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | extension FinalCutPro.FCPXML {
10 | /// Final Cut Pro FCPXML file parsing error.
11 | public enum ParseError: Error {
12 | case general(String)
13 | }
14 | }
15 |
16 | #endif
17 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Extraction/FCPXML Extract.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML Extraction.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import OTCore
11 | import TimecodeKit
12 |
13 | extension XMLElement {
14 | /// Extract the element with timeline context.
15 | /// This provides additional computed information such as absolute start timecode, occlusion,
16 | /// and more.
17 | ///
18 | /// > Note:
19 | /// > This can only contain as much context as is available in its XML scope.
20 | /// > Which means calling this on an element within a resource (media, multicam, etc.) will only
21 | /// > be able to provide context for the resource's scope and is not able to reach outside
22 | /// > to any parent timelines above it.
23 | /// >
24 | /// > If full context is required, do not use this method, but use
25 | /// > ``fcpExtract(types:scope:)`` instead.
26 | ///
27 | /// - Parameters:
28 | /// - constrainToLocalTimeline: If `true`, calculations for interior elements that involve the
29 | /// outermost timeline (such as absolute start timecode and occlusion) will be constrained to
30 | /// the initiating element's local timeline. If the element has no implicit local timeline,
31 | /// the local timeline of the first nested container will be used.
32 | public func fcpExtract(
33 | constrainToLocalTimeline: Bool = false
34 | ) async -> FinalCutPro.FCPXML.ExtractedElement {
35 | let scope = FinalCutPro.FCPXML.ExtractionScope(
36 | constrainToLocalTimeline: constrainToLocalTimeline,
37 | maxContainerDepth: nil,
38 | auditions: .active,
39 | mcClipAngles: .active,
40 | occlusions: .allCases,
41 | filteredTraversalTypes: [],
42 | excludedTraversalTypes: [],
43 | excludedExtractionTypes: [],
44 | traversalPredicate: { _ in false },
45 | extractionPredicate: nil
46 | )
47 |
48 | guard let elementType = fcpElementType,
49 | let extractedElement = await fcpExtract(
50 | types: [elementType],
51 | scope: scope
52 | )
53 | .first
54 | else {
55 | assertionFailure("Element extraction did not return self.")
56 | return FinalCutPro.FCPXML.ExtractedElement(
57 | element: self,
58 | breadcrumbs: Array(ancestorElements(includingSelf: false)),
59 | resources: nil
60 | )
61 | }
62 |
63 | return extractedElement
64 | }
65 | }
66 |
67 | #endif
68 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Extraction/FCPXML ExtractedElement.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML ExtractedElement.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 | import OTCore
12 |
13 | extension FinalCutPro.FCPXML {
14 | // TODO: XMLElement is not Sendable
15 |
16 | /// Extracted element and its context.
17 | public struct ExtractedElement: @unchecked Sendable {
18 | public let element: XMLElement
19 | public let breadcrumbs: [XMLElement]
20 | public let resources: XMLElement?
21 |
22 | init(
23 | element: XMLElement,
24 | breadcrumbs: [XMLElement],
25 | resources: XMLElement?
26 | ) {
27 | self.element = element
28 | self.breadcrumbs = breadcrumbs
29 | self.resources = resources
30 | }
31 |
32 | /// Return the a context value for the element.
33 | public func value(
34 | forContext contextKey: FinalCutPro.FCPXML.ElementContext
35 | ) -> Value {
36 | contextKey.value(from: element, breadcrumbs: breadcrumbs, resources: resources)
37 | }
38 | }
39 | }
40 |
41 | #endif
42 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Extraction/FCPXMLExtractedModelElement.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLExtractedModelElement.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 | import OTCore
12 |
13 | /// Protocol for extracted elements that adds contextual properties.
14 | public protocol FCPXMLExtractedModelElement: FCPXMLExtractedElement {
15 | /// Concrete model type associated with the extracted element.
16 | associatedtype Model: FCPXMLElement
17 | }
18 |
19 | // MARK: - Default Implementation
20 |
21 | extension FCPXMLExtractedModelElement {
22 | /// Returns the XML element wrapped in a model struct.
23 | public var model: Model {
24 | // this guard only necessary because this returns an Optional
25 | guard let model = Model(element: element) else {
26 | assertionFailure("Could not form \(Model.self) model struct.")
27 | return Model()
28 | }
29 | return model
30 | }
31 | }
32 |
33 | #endif
34 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Extraction/Presets/FCPXML CaptionsExtractionPreset.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML CaptionsExtractionPreset.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension FinalCutPro.FCPXML {
12 | /// FCPXML extraction preset that extracts closed captions.
13 | public struct CaptionsExtractionPreset: FCPXMLExtractionPreset {
14 | public init() { }
15 |
16 | public func perform(
17 | on extractable: XMLElement,
18 | scope: FinalCutPro.FCPXML.ExtractionScope
19 | ) async -> [FinalCutPro.FCPXML.ExtractedCaption] {
20 | let extracted = await extractable.fcpExtract(
21 | types: [.caption],
22 | scope: scope
23 | )
24 |
25 | let wrapped = extracted
26 | .map { ExtractedCaption($0) }
27 |
28 | return wrapped
29 | }
30 | }
31 | }
32 |
33 | extension FCPXMLExtractionPreset where Self == FinalCutPro.FCPXML.CaptionsExtractionPreset {
34 | /// FCPXML extraction preset that extracts closed captions.
35 | public static var captions: FinalCutPro.FCPXML.CaptionsExtractionPreset {
36 | FinalCutPro.FCPXML.CaptionsExtractionPreset()
37 | }
38 | }
39 |
40 | extension FinalCutPro.FCPXML {
41 | // TODO: XMLElement is not Sendable
42 |
43 | /// An extracted caption element with pertinent data.
44 | public struct ExtractedCaption: FCPXMLExtractedModelElement, @unchecked Sendable {
45 | public typealias Model = Caption
46 | public let element: XMLElement
47 | public let breadcrumbs: [XMLElement]
48 | public let resources: XMLElement?
49 |
50 | init(_ extractedElement: ExtractedElement) {
51 | element = extractedElement.element
52 | breadcrumbs = extractedElement.breadcrumbs
53 | resources = extractedElement.resources
54 | }
55 |
56 | /// Return the a context value for the element.
57 | public func value(
58 | forContext contextKey: FinalCutPro.FCPXML.ElementContext
59 | ) -> Value {
60 | contextKey.value(from: element, breadcrumbs: breadcrumbs, resources: resources)
61 | }
62 |
63 | // Convenience getters
64 |
65 | /// Caption name.
66 | public var name: String? {
67 | model.name
68 | }
69 |
70 | /// Caption note, if any.
71 | public var note: String? {
72 | model.note
73 | }
74 |
75 | /// Inherited roles from container(s).
76 | public var roles: [AnyInterpolatedRole] {
77 | value(forContext: .inheritedRoles)
78 | }
79 | }
80 | }
81 |
82 | #endif
83 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Extraction/Presets/FCPXML RolesExtractionPreset.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML RolesExtractionPreset.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import OTCore
11 |
12 | extension FinalCutPro.FCPXML {
13 | /// FCPXML extraction preset that extracts roles within a specified scope.
14 | /// Results are sorted by type, then by name.
15 | public struct RolesExtractionPreset: FCPXMLExtractionPreset {
16 | public var roleTypes: Set
17 |
18 | public init(
19 | roleTypes: Set
20 | ) {
21 | self.roleTypes = roleTypes
22 | }
23 |
24 | public func perform(
25 | on extractable: XMLElement,
26 | scope: FinalCutPro.FCPXML.ExtractionScope
27 | ) async -> [FinalCutPro.FCPXML.AnyRole] {
28 | // early return in case no types are specified
29 | guard !roleTypes.isEmpty else { return [] }
30 |
31 | let extracted = await extractable.fcpExtract(scope: scope) { element in
32 | element
33 | .value(forContext: .inheritedRoles)
34 | .filter(roleTypes: roleTypes)
35 | .map(\.wrapped)
36 | }
37 |
38 | let output = extracted
39 | .flatMap { $0 }
40 | .removingDuplicates()
41 | .sortedByRoleTypeThenByName()
42 |
43 | return output
44 | }
45 | }
46 | }
47 |
48 | extension FCPXMLExtractionPreset where Self == FinalCutPro.FCPXML.RolesExtractionPreset {
49 | /// FCPXML extraction preset that extracts roles within a specified scope.
50 | /// Results are sorted by type, then by name.
51 | public static func roles(
52 | roleTypes: Set = .allCases
53 | ) -> FinalCutPro.FCPXML.RolesExtractionPreset {
54 | FinalCutPro.FCPXML.RolesExtractionPreset(
55 | roleTypes: roleTypes
56 | )
57 | }
58 | }
59 |
60 | #endif
61 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Extraction/Presets/FCPXMLExtractionPreset.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLExtractionPreset.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | /// Protocol describing an element extraction preset for FCPXML.
12 | public protocol FCPXMLExtractionPreset where Self: Sendable {
13 | associatedtype Result
14 |
15 | func perform(
16 | on extractable: XMLElement,
17 | scope: FinalCutPro.FCPXML.ExtractionScope
18 | ) async -> Result
19 | }
20 |
21 | #endif
22 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/FCPXML init.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML init.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | extension FinalCutPro.FCPXML {
13 | /// Parse FCPXML/FCPXMLD file contents exported from Final Cut Pro.
14 | public init(fileContent data: Data) throws {
15 | let xmlDocument = try XMLDocument(data: data)
16 | self.init(fileContent: xmlDocument)
17 | }
18 |
19 | /// Initialize from FCPXML file that has been loaded into an `XMLDocument`.
20 | ///
21 | /// For fcpxml v1.10+ .fcpxmld bundles, load the .fcpxml file that is inside the bundle.
22 | public init(fileContent xml: XMLDocument) {
23 | self.xml = xml
24 | }
25 |
26 | // TODO: Add init for a new empty FCPXML file.
27 | }
28 |
29 | #endif
30 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/FCPXML.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension FinalCutPro {
12 | /// Final Cut Pro XML file (FCPXML/FCPXMLD)
13 | ///
14 | /// General structure when exporting from Final Cut Pro:
15 | ///
16 | /// ```xml
17 | ///
18 | ///
19 | ///
20 | ///
21 | ///
22 | ///
23 | ///
24 | ///
25 | ///
26 | ///
27 | ///
28 | ///
29 | ///
30 | ///
31 | ///
32 | ///
33 | /// ```
34 | ///
35 | /// > Note: Starting in FCPXML 1.9, the elements that describe how to organize and use media assets are optional.
36 | /// > The only required element in the `fcpxml` root element is the `resources` element.
37 | /// >
38 | /// > ```xml
39 | /// >
40 | /// > ...
41 | /// >
42 | /// > ...
43 | /// >
44 | /// > ...
45 | /// >
46 | /// >
47 | /// > ```
48 | ///
49 | /// > Final Cut Pro FCPXML 1.11 Reference:
50 | /// >
51 | /// > The root element in an FCPXML document is `fcpxml`, which can contain the following elements:
52 | /// > - A `resources` element, that contains descriptions of media assets and other resources.
53 | /// > - An optional `import-options` element, that controls how Final Cut Pro imports the FCPXML document.
54 | /// > - One of the following optional elements that describe how to organize and use media assets:
55 | /// > - a `library` element that contains a list of event elements;
56 | /// > - a series of `event` elements that contain story elements and project elements; or
57 | /// > - a combination of story elements and `project` elements.
58 | /// >
59 | /// > Note: Starting in FCPXML 1.9, the elements that describe how to organize and use media assets are optional.
60 | /// > The only required element in the `fcpxml` root element is the `resources` element.
61 | ///
62 | /// [Official FCPXML Apple docs](
63 | /// https://developer.apple.com/documentation/professional_video_applications/fcpxml_reference/
64 | /// )
65 | public struct FCPXML {
66 | /// The FCPXML document.
67 | public var xml: XMLDocument
68 | }
69 | }
70 |
71 | #endif
72 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Common/Attributes/FCPXML AudioLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML AudioLayout.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension FinalCutPro.FCPXML {
12 | /// `audioLayout` attribute value.
13 | public enum AudioLayout: String, Equatable, Hashable, CaseIterable, Sendable {
14 | case mono
15 | case stereo
16 | case surround
17 | }
18 | }
19 |
20 | extension FinalCutPro.FCPXML.AudioLayout: FCPXMLAttribute {
21 | public static let attributeName: String = "audioLayout"
22 | }
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Common/Attributes/FCPXML AudioRate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML AudioRate.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension FinalCutPro.FCPXML {
12 | /// `audioRate` attribute value.
13 | /// These are all of the rates that are selectable within Final Cut Pro 10.6.10.
14 | public enum AudioRate: Equatable, Hashable, CaseIterable, Sendable {
15 | case rate32kHz
16 | case rate44_1kHz
17 | case rate48kHz
18 | case rate88_2kHz
19 | case rate96kHz
20 | case rate176_4kHz
21 | case rate192kHz
22 | }
23 | }
24 |
25 | extension FinalCutPro.FCPXML.AudioRate: FCPXMLAttribute {
26 | public static let attributeName: String = "audioRate"
27 | }
28 |
29 | // `audioRate` attribute is used by two FCPXML elements: `asset` and `sequence`.
30 | // The two encode it differently however.
31 | // - `asset` encodes the value as "48000"
32 | // - `sequence` encodes the value as "48k"
33 |
34 | extension FinalCutPro.FCPXML.AudioRate {
35 | /// Attribute raw value for use in a `sequence` element.
36 | public var rawValueForSequence: String {
37 | switch self {
38 | case .rate32kHz: return "32k"
39 | case .rate44_1kHz: return "44.1k"
40 | case .rate48kHz: return "48k"
41 | case .rate88_2kHz: return "88.2k"
42 | case .rate96kHz: return "96k"
43 | case .rate176_4kHz: return "176.4k"
44 | case .rate192kHz: return "192k"
45 | }
46 | }
47 |
48 | /// Initialize using attribute's raw value from a `sequence` element.
49 | public init?(rawValueForSequence rawValue: String) {
50 | guard let match = Self.allCases
51 | .first(where: { $0.rawValueForSequence == rawValue })
52 | else { return nil }
53 | self = match
54 | }
55 | }
56 |
57 | extension FinalCutPro.FCPXML.AudioRate {
58 | /// Attribute raw value for use in an `asset` element.
59 | public var rawValueForAsset: String {
60 | switch self {
61 | case .rate32kHz: return "32000"
62 | case .rate44_1kHz: return "44100"
63 | case .rate48kHz: return "48000"
64 | case .rate88_2kHz: return "88200"
65 | case .rate96kHz: return "96000"
66 | case .rate176_4kHz: return "176400"
67 | case .rate192kHz: return "192000"
68 | }
69 | }
70 |
71 | /// Initialize using attribute's raw value from an `asset` element.
72 | public init?(rawValueForAsset rawValue: String) {
73 | guard let match = Self.allCases
74 | .first(where: { $0.rawValueForAsset == rawValue })
75 | else { return nil }
76 |
77 | self = match
78 | }
79 | }
80 |
81 | #endif
82 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Common/Attributes/FCPXML ClipSourceEnable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML ClipSourceEnable.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension FinalCutPro.FCPXML {
12 | /// Clip source enable value. (Used with `asset-clip` and `mc-clip`)
13 | public enum ClipSourceEnable: String, Equatable, Hashable, CaseIterable, Sendable {
14 | /// Audio and Video.
15 | case all
16 |
17 | /// Audio source.
18 | case audio
19 |
20 | /// Video source.
21 | case video
22 | }
23 | }
24 |
25 | extension FinalCutPro.FCPXML.ClipSourceEnable: FCPXMLAttribute {
26 | public static let attributeName: String = "srcEnable"
27 | }
28 |
29 | extension XMLElement {
30 | /// FCPXML: Returns value for attribute `srcEnable`. (Default: `.all`)
31 | /// Call on a `asset-clip` or `mc-clip` element only.
32 | public var fcpClipSourceEnable: FinalCutPro.FCPXML.ClipSourceEnable {
33 | get {
34 | let defaultValue: FinalCutPro.FCPXML.ClipSourceEnable = .all
35 |
36 | guard let value = stringValue(forAttributeNamed: FinalCutPro.FCPXML.ClipSourceEnable.attributeName)
37 | else { return defaultValue }
38 |
39 | return FinalCutPro.FCPXML.ClipSourceEnable(rawValue: value) ?? defaultValue
40 | }
41 | set {
42 | addAttribute(withName: FinalCutPro.FCPXML.ClipSourceEnable.attributeName,
43 | value: newValue.rawValue)
44 | }
45 | }
46 | }
47 |
48 | #endif
49 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Common/Attributes/FCPXML FrameSampling.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML FrameSampling.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension FinalCutPro.FCPXML {
12 | /// `frameSampling` attribute value.
13 | /// Used in `conform-rate` and `timeMap` elements.
14 | public enum FrameSampling: String, Equatable, Hashable, CaseIterable, Sendable {
15 | case floor
16 | case nearestNeighbor = "nearest-neighbor"
17 | case frameBlending = "frame-blending"
18 | case opticalFlowClassic = "optical-flow-classic"
19 | case opticalFlow = "optical-flow"
20 | case opticalFlowFRC = "optical-flow-frc"
21 | }
22 | }
23 |
24 | extension FinalCutPro.FCPXML.FrameSampling: FCPXMLAttribute {
25 | public static let attributeName: String = "frameSampling"
26 | }
27 |
28 | #endif
29 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Common/Attributes/FCPXML TimecodeFormat.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML TimecodeFormat.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | extension FinalCutPro.FCPXML {
13 | /// `tcFormat` attribute value.
14 | public enum TimecodeFormat: String, Equatable, Hashable, CaseIterable, Sendable {
15 | case dropFrame = "DF"
16 | case nonDropFrame = "NDF"
17 | }
18 | }
19 |
20 | extension FinalCutPro.FCPXML.TimecodeFormat: FCPXMLAttribute {
21 | public static let attributeName: String = "tcFormat"
22 | }
23 |
24 | extension FinalCutPro.FCPXML.TimecodeFormat {
25 | /// Returns `true` if format is drop-frame.
26 | public var isDrop: Bool {
27 | switch self {
28 | case .dropFrame: return true
29 | case .nonDropFrame: return false
30 | }
31 | }
32 | }
33 |
34 | #endif
35 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/FCPXMLAttribute.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLAttribute.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | public protocol FCPXMLAttribute {
13 | /// The XML attribute name.
14 | static var attributeName: String { get }
15 | }
16 |
17 | #endif
18 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/FCPXMLElement Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElement Extensions.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | extension FCPXMLElement {
13 | /// Returns the timecode frame rate for the local timeline.
14 | public func localTimecodeFrameRate() -> TimecodeFrameRate? {
15 | // `sequence` has a `format` attribute,
16 | // and a tcFormat attribute determining drop or non-drop frame timecode
17 | element._fcpTimecodeFrameRate()
18 | }
19 | }
20 |
21 | #endif
22 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/FCPXMLElement.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElement.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | /// Protocol which all FCPXML wrapper model objects conform.
13 | public protocol FCPXMLElement where Self: Equatable, Self: Hashable {
14 | /// The wrapped XML element object.
15 | var element: XMLElement { get }
16 |
17 | /// The FCPXML element type of the model instance.
18 | var elementType: FinalCutPro.FCPXML.ElementType { get }
19 |
20 | /// All FCPXML element types the model object is capable of handling.
21 | ///
22 | /// Most model objects only handle a single type.
23 | /// However some model objects are 'meta types' and can handle more than one, such as
24 | /// ``FinalCutPro/FCPXML/Marker`` which handles both `marker` and `chapter-marker`.
25 | static var supportedElementTypes: Set { get }
26 |
27 | /// Initialize a new empty element with defaults.
28 | init()
29 |
30 | /// Wrap a FCPXML element.
31 | /// Returns `nil` if the element does not match the model element type.
32 | init?(element: XMLElement)
33 | }
34 |
35 | extension FCPXMLElement /* : Equatable */ {
36 | public static func == (lhs: Self, rhs: O) -> Bool {
37 | lhs.element == rhs.element
38 | }
39 |
40 | public static func == (lhs: XMLElement, rhs: Self) -> Bool {
41 | lhs == rhs.element
42 | }
43 |
44 | public static func == (lhs: Self, rhs: XMLElement) -> Bool {
45 | lhs.element == rhs
46 | }
47 | }
48 |
49 | extension FCPXMLElement /* : Hashable */ {
50 | public func hash(into hasher: inout Hasher) {
51 | hasher.combine(element)
52 | }
53 | }
54 |
55 | // MARK: - Utilities
56 |
57 | extension FCPXMLElement {
58 | func _isElementTypeSupported(element: XMLElement? = nil) -> Bool {
59 | let e = element ?? self.element
60 | guard let et = e.fcpElementType,
61 | Self.supportedElementTypes.contains(et)
62 | else { return false }
63 | return true
64 | }
65 | }
66 |
67 | #endif
68 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Single Attributes/FCPXMLElementDuration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementDuration.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | public protocol FCPXMLElementRequiredDuration: FCPXMLElement {
13 | /// Local timeline duration. (Required)
14 | var duration: Fraction { get nonmutating set }
15 | }
16 |
17 | extension FCPXMLElementRequiredDuration {
18 | public var duration: Fraction {
19 | get { element.fcpDuration ?? .zero }
20 | nonmutating set { element.fcpDuration = newValue }
21 | }
22 |
23 | /// Returns the local timeline duration of the element as timecode.
24 | public func durationAsTimecode(
25 | frameRateSource: FinalCutPro.FCPXML.FrameRateSource = .localToElement
26 | ) -> Timecode? {
27 | element._fcpDurationAsTimecode(
28 | frameRateSource: frameRateSource,
29 | default: .zero
30 | )
31 | }
32 | }
33 |
34 | public protocol FCPXMLElementOptionalDuration: FCPXMLElement {
35 | /// Local timeline duration.
36 | var duration: Fraction? { get nonmutating set }
37 | }
38 |
39 | extension FCPXMLElementOptionalDuration {
40 | public var duration: Fraction? {
41 | get { element.fcpDuration }
42 | nonmutating set { element.fcpDuration = newValue }
43 | }
44 |
45 | /// Returns the start time of the element as timecode.
46 | public func durationAsTimecode(
47 | frameRateSource: FinalCutPro.FCPXML.FrameRateSource = .localToElement
48 | ) -> Timecode? {
49 | guard duration != nil else { return nil }
50 | return element._fcpDurationAsTimecode(
51 | frameRateSource: frameRateSource,
52 | default: nil
53 | )
54 | }
55 | }
56 |
57 | // MARK: - XML Utils
58 |
59 | extension XMLElement {
60 | func _fcpDurationAsTimecode(
61 | frameRateSource: FinalCutPro.FCPXML.FrameRateSource = .localToElement,
62 | default defaultDuration: Fraction? = .zero
63 | ) -> Timecode? {
64 | guard let dur = fcpDuration ?? defaultDuration else { return nil }
65 |
66 | return try? _fcpTimecode(
67 | fromRational: dur,
68 | frameRateSource: frameRateSource
69 | )
70 | }
71 | }
72 |
73 | #endif
74 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Single Attributes/FCPXMLElementFrameSampling.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementFrameSampling.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | public protocol FCPXMLElementFrameSampling: FCPXMLElement {
13 | /// Frame sampling. (Default: floor)
14 | var frameSampling: FinalCutPro.FCPXML.FrameSampling { get nonmutating set }
15 | }
16 |
17 | extension FCPXMLElementFrameSampling {
18 | private var _frameSamplingDefault: FinalCutPro.FCPXML.FrameSampling { .floor }
19 |
20 | public var frameSampling: FinalCutPro.FCPXML.FrameSampling {
21 | get {
22 | guard let value = element.stringValue(forAttributeNamed: "frameSampling")
23 | else { return _frameSamplingDefault }
24 |
25 | return FinalCutPro.FCPXML.FrameSampling(rawValue: value) ?? _frameSamplingDefault
26 | }
27 | nonmutating set {
28 | if newValue == _frameSamplingDefault {
29 | // can remove attribute if value is default
30 | element.removeAttribute(forName: "frameSampling")
31 | } else {
32 | element.addAttribute(withName: "frameSampling", value: newValue.rawValue)
33 | }
34 | }
35 | }
36 | }
37 |
38 | #endif
39 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Single Attributes/FCPXMLElementModDate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementModDate.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | public protocol FCPXMLElementOptionalModDate: FCPXMLElement {
13 | /// Modification date.
14 | var modDate: String? { get nonmutating set }
15 | }
16 |
17 | extension FCPXMLElementOptionalModDate {
18 | public var modDate: String? {
19 | get { element.stringValue(forAttributeNamed: "modDate") }
20 | nonmutating set { element.addAttribute(withName: "modDate", value: newValue) }
21 | }
22 | }
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Single Attributes/FCPXMLElementOffset.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementOffset.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | public protocol FCPXMLElementRequiredOffset: FCPXMLElement {
13 | /// Local timeline offset. (Required)
14 | var offset: Fraction { get nonmutating set }
15 | }
16 |
17 | extension FCPXMLElementRequiredOffset {
18 | public var offset: Fraction {
19 | get { element.fcpOffset ?? .zero }
20 | nonmutating set { element.fcpOffset = newValue }
21 | }
22 |
23 | /// Returns the local timeline offset of the element as timecode.
24 | public func offsetAsTimecode(
25 | frameRateSource: FinalCutPro.FCPXML.FrameRateSource = .localToElement
26 | ) -> Timecode? {
27 | element._fcpOffsetAsTimecode(
28 | frameRateSource: frameRateSource,
29 | default: .zero
30 | )
31 | }
32 | }
33 |
34 | public protocol FCPXMLElementOptionalOffset: FCPXMLElement {
35 | /// Local timeline offset.
36 | var offset: Fraction? { get nonmutating set }
37 | }
38 |
39 | extension FCPXMLElementOptionalOffset {
40 | public var offset: Fraction? {
41 | get { element.fcpOffset }
42 | nonmutating set { element.fcpOffset = newValue }
43 | }
44 |
45 | /// Returns the offset of the element as timecode.
46 | public func offsetAsTimecode(
47 | frameRateSource: FinalCutPro.FCPXML.FrameRateSource = .localToElement
48 | ) -> Timecode? {
49 | guard offset != nil else { return nil }
50 | return element._fcpOffsetAsTimecode(
51 | frameRateSource: frameRateSource,
52 | default: nil
53 | )
54 | }
55 | }
56 |
57 | // MARK: - XML Utils
58 |
59 | extension XMLElement {
60 | func _fcpOffsetAsTimecode(
61 | frameRateSource: FinalCutPro.FCPXML.FrameRateSource = .localToElement,
62 | default defaultOffset: Fraction? = .zero
63 | ) -> Timecode? {
64 | guard let dur = fcpOffset ?? defaultOffset else { return nil }
65 |
66 | return try? _fcpTimecode(
67 | fromRational: dur,
68 | frameRateSource: frameRateSource
69 | )
70 | }
71 | }
72 |
73 | #endif
74 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Single Attributes/FCPXMLElementStart.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementStart.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | public protocol FCPXMLElementRequiredStart: FCPXMLElement {
13 | /// Local timeline start. (Required)
14 | var start: Fraction { get nonmutating set }
15 | }
16 |
17 | extension FCPXMLElementRequiredStart {
18 | public var start: Fraction {
19 | get { element.fcpStart ?? .zero }
20 | nonmutating set { element.fcpStart = newValue }
21 | }
22 |
23 | /// Returns the start time of the element as timecode.
24 | public func startAsTimecode(
25 | frameRateSource: FinalCutPro.FCPXML.FrameRateSource = .localToElement
26 | ) -> Timecode? {
27 | element._fcpStartAsTimecode(
28 | frameRateSource: frameRateSource,
29 | default: .zero
30 | )
31 | }
32 | }
33 |
34 | public protocol FCPXMLElementOptionalStart: FCPXMLElement {
35 | /// Local timeline start.
36 | var start: Fraction? { get nonmutating set }
37 | }
38 |
39 | extension FCPXMLElementOptionalStart {
40 | public var start: Fraction? {
41 | get { element.fcpStart }
42 | nonmutating set { element.fcpStart = newValue }
43 | }
44 |
45 | /// Returns the start time of the element as timecode.
46 | public func startAsTimecode(
47 | frameRateSource: FinalCutPro.FCPXML.FrameRateSource = .localToElement
48 | ) -> Timecode? {
49 | guard start != nil else { return nil }
50 | return element._fcpStartAsTimecode(
51 | frameRateSource: frameRateSource,
52 | default: .zero
53 | )
54 | }
55 | }
56 |
57 | // MARK: - XML Utils
58 |
59 | extension XMLElement {
60 | func _fcpStartAsTimecode(
61 | frameRateSource: FinalCutPro.FCPXML.FrameRateSource = .localToElement,
62 | default defaultStart: Fraction? = .zero
63 | ) -> Timecode? {
64 | guard let dur = fcpStart ?? defaultStart else { return nil }
65 |
66 | return try? _fcpTimecode(
67 | fromRational: dur,
68 | frameRateSource: frameRateSource
69 | )
70 | }
71 | }
72 |
73 | #endif
74 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Single Attributes/FCPXMLElementTCFormat.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementTCFormat.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | public protocol FCPXMLElementOptionalTCFormat: FCPXMLElement {
13 | /// Local timeline timecode format.
14 | var tcFormat: FinalCutPro.FCPXML.TimecodeFormat? { get nonmutating set }
15 | }
16 |
17 | extension FCPXMLElementOptionalTCFormat {
18 | public var tcFormat: FinalCutPro.FCPXML.TimecodeFormat? {
19 | get { element.fcpTCFormat }
20 | nonmutating set { element.fcpTCFormat = newValue }
21 | }
22 | }
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Single Attributes/FCPXMLElementTCStart.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementTCStart.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | public protocol FCPXMLElementOptionalTCStart: FCPXMLElement {
13 | /// Local timeline origin time.
14 | var tcStart: Fraction? { get nonmutating set }
15 | }
16 |
17 | extension FCPXMLElementOptionalTCStart {
18 | public var tcStart: Fraction? {
19 | get { element.fcpTCStart }
20 | nonmutating set { element.fcpTCStart = newValue }
21 | }
22 |
23 | /// Returns the start time of the element as timecode.
24 | public func tcStartAsTimecode(
25 | frameRateSource: FinalCutPro.FCPXML.FrameRateSource = .localToElement
26 | ) -> Timecode? {
27 | element._fcpTCStartAsTimecode(
28 | frameRateSource: frameRateSource
29 | )
30 | }
31 | }
32 |
33 | // MARK: - XML Utils
34 |
35 | extension XMLElement {
36 | func _fcpTCStartAsTimecode(
37 | frameRateSource: FinalCutPro.FCPXML.FrameRateSource = .localToElement
38 | ) -> Timecode? {
39 | guard let tcStart = fcpTCStart else { return nil }
40 | return try? _fcpTimecode(
41 | fromRational: tcStart,
42 | frameRateSource: frameRateSource
43 | )
44 | }
45 | }
46 |
47 | #endif
48 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Single Children/FCPXMLElementAudioChannelSourceChildren.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementAudioChannelSourceChildren.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import OTCore
11 | import TimecodeKit
12 |
13 | public protocol FCPXMLElementAudioChannelSourceChildren: FCPXMLElement {
14 | /// Child `audio-channel-source` elements.
15 | var audioChannelSources: LazyFCPXMLChildrenSequence { get nonmutating set }
16 | }
17 |
18 | extension FCPXMLElementAudioChannelSourceChildren {
19 | public var audioChannelSources: LazyFCPXMLChildrenSequence {
20 | get { element.fcpAudioChannelSources }
21 | nonmutating set { element.fcpAudioChannelSources = newValue }
22 | }
23 | }
24 |
25 | extension XMLElement {
26 | /// FCPXML: Returns child `audio-channel-source` elements.
27 | /// Use on `clip` or `asset-clip` elements.
28 | public var fcpAudioChannelSources: LazyFCPXMLChildrenSequence {
29 | get { children(whereFCPElement: .audioChannelSource) }
30 | set { _updateChildElements(ofType: .audioChannelSource, with: newValue) }
31 | }
32 | }
33 | #endif
34 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Single Children/FCPXMLElementAudioRoleSourceChildren.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementAudioRoleSourceChildren.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import OTCore
11 | import TimecodeKit
12 |
13 | public protocol FCPXMLElementAudioRoleSourceChildren: FCPXMLElement {
14 | /// Child `audio-role-source` elements.
15 | var audioRoleSources: LazyFCPXMLChildrenSequence { get nonmutating set }
16 | }
17 |
18 | extension FCPXMLElementAudioRoleSourceChildren {
19 | public var audioRoleSources: LazyFCPXMLChildrenSequence {
20 | get { element.fcpAudioRoleSources }
21 | nonmutating set { element.fcpAudioRoleSources = newValue }
22 | }
23 | }
24 |
25 | extension XMLElement {
26 | /// FCPXML: Returns child `audio-role-source` elements.
27 | /// Use on `ref-clip`, `sync-source`, or `mc-source` elements.
28 | public var fcpAudioRoleSources: LazyFCPXMLChildrenSequence {
29 | get { children(whereFCPElement: .audioRoleSource) }
30 | set { _updateChildElements(ofType: .audioRoleSource, with: newValue) }
31 | }
32 | }
33 |
34 | #endif
35 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Single Children/FCPXMLElementBookmarkChild.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementBookmarkChild.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | public protocol FCPXMLElementBookmarkChild: FCPXMLElement {
13 | /// Security-scoped bookmark data in a base64-encoded string.
14 | /// Access the `stringValue` property on the returned element.
15 | var bookmark: XMLElement? { get nonmutating set }
16 |
17 | /// Security-scoped bookmark data.
18 | /// Returns the decoded ``bookmark`` base64-encoded string as `Data`.
19 | var bookmarkData: Data? { get nonmutating set }
20 | }
21 |
22 | extension FCPXMLElementBookmarkChild {
23 | public var bookmark: XMLElement? {
24 | get {
25 | element.firstChildElement(named: FinalCutPro.FCPXML.ElementType.bookmark.rawValue)
26 | }
27 | nonmutating set {
28 | element._updateChildElements(ofType: .bookmark, withChild: newValue)
29 | }
30 | }
31 |
32 | public var bookmarkData: Data? {
33 | get {
34 | guard let value = element
35 | .firstChildElement(whereFCPElementType: .bookmark)?
36 | .stringValue
37 | else { return nil }
38 |
39 | return Data(base64Encoded: value)
40 | }
41 | nonmutating set {
42 | let v = newValue?.base64EncodedString()
43 | element._updateChildElement(named: "bookmark", newStringValue: v)
44 | }
45 | }
46 | }
47 |
48 | #endif
49 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Single Children/FCPXMLElementMetadataChild.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementMetadataChild.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | public protocol FCPXMLElementMetadataChild: FCPXMLElement {
13 | /// Metadata for the element.
14 | var metadata: FinalCutPro.FCPXML.Metadata? { get nonmutating set }
15 | }
16 |
17 | extension FCPXMLElementMetadataChild {
18 | public var metadata: FinalCutPro.FCPXML.Metadata? {
19 | get { element.firstChild(whereFCPElement: .metadata) }
20 | nonmutating set { element._updateChildElements(ofType: .metadata, withChild: newValue) }
21 | }
22 | }
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Single Children/FCPXMLElementNoteChild.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementNoteChild.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | public protocol FCPXMLElementNoteChild: FCPXMLElement {
13 | /// Optional note text.
14 | var note: String? { get nonmutating set }
15 | }
16 |
17 | extension FCPXMLElementNoteChild {
18 | public var note: String? {
19 | get {
20 | element
21 | .firstChildElement(whereFCPElementType: .note)?
22 | .stringValue
23 | }
24 | nonmutating set {
25 | element
26 | ._updateFirstChildElement(ofType: .note, newStringValue: newValue)
27 | }
28 | }
29 | }
30 |
31 | #endif
32 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Single Children/FCPXMLElementTextChildren.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementTextChildren.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import OTCore
11 | import TimecodeKit
12 |
13 | public protocol FCPXMLElementTextChildren: FCPXMLElement {
14 | /// Child `text` elements.
15 | var texts: LazyFCPXMLChildrenSequence { get nonmutating set }
16 | }
17 |
18 | extension FCPXMLElementTextChildren {
19 | public var texts: LazyFCPXMLChildrenSequence {
20 | get { element.fcpTexts }
21 | nonmutating set { element.fcpTexts = newValue }
22 | }
23 | }
24 |
25 | extension XMLElement {
26 | /// FCPXML: Returns child `text` elements.
27 | public var fcpTexts: LazyFCPXMLChildrenSequence {
28 | get { children(whereFCPElement: .text) }
29 | set { _updateChildElements(ofType: .text, with: newValue) }
30 | }
31 | }
32 |
33 | #endif
34 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Single Children/FCPXMLElementTextStyleDefinitionChildren.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementTextStyleDefinitionChildren.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 | import OTCore
12 |
13 | public protocol FCPXMLElementTextStyleDefinitionChildren: FCPXMLElement {
14 | /// Child `text-style-def` elements.
15 | var fcpTextStyleDefinitions: LazyFilteredCompactMapSequence<[XMLNode], XMLElement> { get nonmutating set }
16 | }
17 |
18 | extension FCPXMLElementTextStyleDefinitionChildren {
19 | public var fcpTextStyleDefinitions: LazyFilteredCompactMapSequence<[XMLNode], XMLElement> {
20 | get { element.fcpTextStyleDefinitions }
21 | nonmutating set { element.fcpTextStyleDefinitions = newValue }
22 | }
23 | }
24 |
25 | extension XMLElement {
26 | // TODO: no model objects yet, so just return the bare XML
27 |
28 | /// FCPXML: Returns child `text-style-def` elements.
29 | public var fcpTextStyleDefinitions: LazyFilteredCompactMapSequence<[XMLNode], XMLElement> {
30 | get {
31 | childElements
32 | .filter(whereFCPElementType: .textStyleDef)
33 | }
34 | set {
35 | _updateChildElements(ofType: .textStyleDef, with: newValue)
36 | }
37 | }
38 | }
39 | #endif
40 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Story Elements/FCPXMLElementAnchorableAttributes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementAnchorableAttributes.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | /// FCPXML 1.11 DTD:
13 | ///
14 | /// ```xml
15 | ///
16 | ///
17 | ///
18 | ///
19 | ///
20 | ///
21 | ///
25 | /// ```
26 | public protocol FCPXMLElementAnchorableAttributes: FCPXMLElement {
27 | /// Lane.
28 | /// Specifies where the object is contained/anchored relative to its parent.
29 | ///
30 | /// - `0` = contained inside its parent (default)
31 | /// - `>0` = anchored above its parent
32 | /// - `<0` = anchored below its parent
33 | var lane: Int? { get nonmutating set }
34 |
35 | /// Offset within parent timeline. (Default: 0)
36 | var offset: Fraction? { get nonmutating set }
37 | }
38 |
39 | extension FCPXMLElementAnchorableAttributes {
40 | public var lane: Int? {
41 | get { element.fcpLane }
42 | nonmutating set { element.fcpLane = newValue }
43 | }
44 |
45 | public var offset: Fraction? {
46 | get { element.fcpOffset }
47 | nonmutating set { element.fcpOffset = newValue }
48 | }
49 | }
50 |
51 | extension FCPXMLElementAnchorableAttributes {
52 | /// Returns the offset of the element as timecode.
53 | public func offsetAsTimecode(
54 | frameRateSource: FinalCutPro.FCPXML.FrameRateSource = .localToElement
55 | ) -> Timecode? {
56 | guard let offset = offset else { return nil }
57 | return try? element._fcpTimecode(
58 | fromRational: offset,
59 | frameRateSource: frameRateSource
60 | )
61 | }
62 | }
63 |
64 | #endif
65 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Story Elements/FCPXMLElementAudioStartAndDuration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementAudioStartAndDuration.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | /// FCPXML 1.11 DTD:
13 | ///
14 | /// Use `audioStart` and `audioDuration` attributes to define J/L cuts (i.e., split edits) on
15 | /// composite A/V clips.
16 | public protocol FCPXMLElementAudioStartAndDuration: FCPXMLElement {
17 | var audioStart: Fraction? { get nonmutating set }
18 | var audioDuration: Fraction? { get nonmutating set }
19 | }
20 |
21 | extension FCPXMLElementAudioStartAndDuration {
22 | public var audioStart: Fraction? {
23 | get { element.fcpAudioStart }
24 | nonmutating set { element.fcpAudioStart = newValue }
25 | }
26 |
27 | public var audioDuration: Fraction? {
28 | get { element.fcpAudioDuration }
29 | nonmutating set { element.fcpAudioDuration = newValue }
30 | }
31 | }
32 |
33 | #endif
34 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Story Elements/FCPXMLElementClipAttributes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementClipAttributes.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | /// FCPXML 1.11 DTD:
13 | ///
14 | /// ```xml
15 | ///
16 | ///
17 | ///
18 | ///
25 | /// ```
26 | public protocol FCPXMLElementClipAttributes: FCPXMLElement,
27 | FCPXMLElementAnchorableAttributes,
28 | FCPXMLElementOptionalStart, FCPXMLElementRequiredDuration
29 | {
30 | /// Clip name.
31 | var name: String? { get nonmutating set }
32 |
33 | // FCPXMLElementStart
34 | /// Clip local timeline start time.
35 | var start: Fraction? { get nonmutating set }
36 |
37 | // FCPXMLElementRequiredDuration
38 | /// Clip duration.
39 | var duration: Fraction { get nonmutating set }
40 |
41 | /// Clip enabled state. (Default: `true`)
42 | var enabled: Bool { get nonmutating set }
43 | }
44 |
45 | extension FCPXMLElementClipAttributes {
46 | public var name: String? {
47 | get { element.fcpName }
48 | nonmutating set { element.fcpName = newValue }
49 | }
50 |
51 | // implemented by FCPXMLElementOptionalStart
52 | // public var start: Fraction?
53 |
54 | // implemented by FCPXMLElementRequiredDuration
55 | // public var duration: Fraction
56 |
57 | public var enabled: Bool {
58 | get { element.fcpGetEnabled(default: true) }
59 | nonmutating set { element.fcpSet(enabled: newValue, default: true) }
60 | }
61 | }
62 |
63 | #endif
64 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Story Elements/FCPXMLElementClipAttributesOptionalDuration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementClipAttributesOptionalDuration.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | /// FCPXML 1.11 DTD:
13 | ///
14 | /// ```xml
15 | ///
16 | ///
23 | /// ```
24 | public protocol FCPXMLElementClipAttributesOptionalDuration: FCPXMLElement,
25 | FCPXMLElementAnchorableAttributes,
26 | FCPXMLElementOptionalStart, FCPXMLElementOptionalDuration
27 | {
28 | /// Clip name.
29 | var name: String? { get nonmutating set }
30 |
31 | // FCPXMLElementStart
32 | /// Clip local timeline start time.
33 | var start: Fraction? { get nonmutating set }
34 |
35 | // FCPXMLElementOptionalDuration
36 | /// Clip duration.
37 | var duration: Fraction? { get nonmutating set }
38 |
39 | /// Clip enabled state. (Default: `true`)
40 | var enabled: Bool { get nonmutating set }
41 | }
42 |
43 | extension FCPXMLElementClipAttributesOptionalDuration {
44 | public var name: String? {
45 | get { element.fcpName }
46 | nonmutating set { element.fcpName = newValue }
47 | }
48 |
49 | // implemented by FCPXMLElementOptionalStart
50 | // public var start: Fraction?
51 |
52 | // implemented by FCPXMLElementOptionalDuration
53 | // public var duration: Fraction?
54 |
55 | public var enabled: Bool {
56 | get { element.fcpGetEnabled(default: true) }
57 | nonmutating set { element.fcpSet(enabled: newValue, default: true) }
58 | }
59 | }
60 |
61 | #endif
62 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Story Elements/FCPXMLElementMediaAttributes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementMediaAttributes.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | /// FCPXML 1.11 DTD:
13 | ///
14 | /// ```xml
15 | ///
16 | ///
17 | ///
18 | ///
19 | ///
25 | /// ```
26 | public protocol FCPXMLElementMediaAttributes: FCPXMLElement,
27 | FCPXMLElementOptionalDuration,
28 | FCPXMLElementOptionalTCStart,
29 | FCPXMLElementOptionalTCFormat
30 | {
31 | /// Format resource ID. (Required)
32 | var format: String { get nonmutating set }
33 |
34 | // FCPXMLElementOptionalDuration
35 | /// Local timeline duration.
36 | var duration: Fraction? { get nonmutating set }
37 |
38 | // FCPXMLElementTCStart
39 | /// Local timeline start time.
40 | var tcStart: Fraction? { get nonmutating set }
41 |
42 | // FCPXMLElementTCFormat
43 | /// Local timeline timecode format.
44 | var tcFormat: FinalCutPro.FCPXML.TimecodeFormat? { get nonmutating set }
45 | }
46 |
47 | extension FCPXMLElementMediaAttributes {
48 | public var format: String {
49 | get { element.fcpFormat ?? "" }
50 | nonmutating set { element.fcpFormat = newValue }
51 | }
52 |
53 | // implemented by FCPXMLElementOptionalDuration
54 | // public var duration: Fraction?
55 |
56 | // implemented by FCPXMLElementOptionalTCStart
57 | // public var tcStart: Fraction?
58 |
59 | // implemented by FCPXMLElementOptionalTCFormat
60 | // public var tcFormat: FinalCutPro.FCPXML.TimecodeFormat?
61 | }
62 |
63 | #endif
64 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Protocols/Story Elements/FCPXMLElementTimingParams.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLElementTimingParams.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import OTCore
11 | import TimecodeKit
12 |
13 | /// FCPXML 1.11 DTD:
14 | ///
15 | /// ```xml
16 | ///
17 | ///
18 | /// ```
19 | public protocol FCPXMLElementTimingParams: FCPXMLElement {
20 | /// Clip conform rate.
21 | ///
22 | /// > FCPXML 1.11 DTD:
23 | /// >
24 | /// > "A `conform-rate` defines how the clip's frame rate should be conformed to the sequence frame rate".
25 | var conformRate: FinalCutPro.FCPXML.ConformRate? { get nonmutating set }
26 |
27 | /// Clip time map.
28 | ///
29 | /// > FCPXML 1.11 DTD:
30 | /// >
31 | /// > "A `timeMap` is a container for `timept` elements that change the output speed of the clip's local timeline.
32 | /// > When present, a `timeMap` defines a new adjusted time range for the clip using the first and last `timept`
33 | /// > elements. All other time values are interpolated from the specified `timept` elements."
34 | var timeMap: FinalCutPro.FCPXML.TimeMap? { get nonmutating set }
35 | }
36 |
37 | extension FCPXMLElementTimingParams {
38 | public var conformRate: FinalCutPro.FCPXML.ConformRate? {
39 | get {
40 | element.firstChild(whereFCPElement: .conformRate)
41 | }
42 | nonmutating set {
43 | element._updateFirstChildElement(ofType: .conformRate, withChild: newValue)
44 | }
45 | }
46 |
47 | public var timeMap: FinalCutPro.FCPXML.TimeMap? {
48 | get {
49 | element.firstChild(whereFCPElement: .timeMap)
50 | }
51 | nonmutating set {
52 | element._updateFirstChildElement(ofType: .timeMap, withChild: newValue)
53 | }
54 | }
55 | }
56 |
57 | #endif
58 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Resources/FCPXML Locator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML Locator.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension FinalCutPro.FCPXML {
12 | /// Locator shared resource.
13 | ///
14 | /// > Final Cut Pro FCPXML 1.11 Reference:
15 | /// >
16 | /// > Describe a URL-based resource.
17 | /// >
18 | /// > Use the `locator` element to describe the location of data files associated with another
19 | /// > FCPXML element.
20 | /// >
21 | /// > See [`locator`](https://developer.apple.com/documentation/professional_video_applications/fcpxml_reference/locator).
22 | public struct Locator: FCPXMLElement {
23 | public let element: XMLElement
24 |
25 | public let elementType: ElementType = .locator
26 |
27 | public static let supportedElementTypes: Set = [.locator]
28 |
29 | public init() {
30 | element = XMLElement(name: elementType.rawValue)
31 | }
32 |
33 | public init?(element: XMLElement) {
34 | self.element = element
35 | guard _isElementTypeSupported(element: element) else { return nil }
36 | }
37 | }
38 | }
39 |
40 | // MARK: - Parameterized init
41 |
42 | extension FinalCutPro.FCPXML.Locator {
43 | public init(
44 | id: String,
45 | url: URL? = nil
46 | ) {
47 | self.init()
48 |
49 | self.id = id
50 | self.url = url
51 | }
52 | }
53 |
54 | // MARK: - Structure
55 |
56 | extension FinalCutPro.FCPXML.Locator {
57 | public enum Attributes: String {
58 | /// Required.
59 | /// Identifier.
60 | case id
61 |
62 | /// Required.
63 | /// Absolute URL or relative URL to library path.
64 | case url
65 | }
66 | }
67 |
68 | // MARK: - Attributes
69 |
70 | extension FinalCutPro.FCPXML.Locator {
71 | /// Required.
72 | /// Identifier.
73 | public var id: String {
74 | get { element.fcpID ?? "" }
75 | nonmutating set { element.fcpID = newValue }
76 | }
77 |
78 | /// Required.
79 | /// Absolute URL or relative URL to library path.
80 | public var url: URL? {
81 | get { element.getURL(forAttribute: Attributes.url.rawValue) }
82 | nonmutating set { element.set(url: newValue, forAttribute: Attributes.url.rawValue) }
83 | }
84 | }
85 |
86 | // MARK: - Children
87 |
88 | extension FinalCutPro.FCPXML.Locator: FCPXMLElementBookmarkChild { }
89 |
90 | // MARK: - Typing
91 |
92 | // Locator
93 | extension XMLElement {
94 | /// FCPXML: Returns the element wrapped in a ``FinalCutPro/FCPXML/Locator`` model object.
95 | /// Call this on a `locator` element only.
96 | public var fcpAsLocator: FinalCutPro.FCPXML.Locator? {
97 | .init(element: self)
98 | }
99 | }
100 |
101 | #endif
102 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Resources/FCPXML ObjectTracker TrackingShape.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML ObjectTracker TrackingShape.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension FinalCutPro.FCPXML.ObjectTracker {
12 | /// Tracking shape used by an object tracker resource.
13 | ///
14 | /// > Final Cut Pro FCPXML 1.11 Reference:
15 | /// >
16 | /// > Define a shape that the `object-tracker` uses to match the movement of an object.
17 | /// >
18 | /// > In Final Cut Pro, users can track the `shape-mask` of a video effect such as a blur,
19 | /// > highlight, or color effect to a moving object in a video clip.
20 | /// >
21 | /// > Use the `tracking-shape` element to define the shape that the `object-tracker` element
22 | /// > uses to match the movement of a moving object in a video clip. Each `object-tracker`
23 | /// > element consists of one or more tracking shapes.
24 | /// >
25 | /// > See [`tracking-shape`](
26 | /// > https://developer.apple.com/documentation/professional_video_applications/fcpxml_reference/tracking-shape
27 | /// > ).
28 | public struct TrackingShape: FCPXMLElement {
29 | public let element: XMLElement
30 |
31 | public let elementType: FinalCutPro.FCPXML.ElementType = .trackingShape
32 |
33 | public static let supportedElementTypes: Set = [.trackingShape]
34 |
35 | public init() {
36 | element = XMLElement(name: elementType.rawValue)
37 | }
38 |
39 | public init?(element: XMLElement) {
40 | self.element = element
41 | guard _isElementTypeSupported(element: element) else { return nil }
42 | }
43 | }
44 | }
45 |
46 | // MARK: - Parameterized init
47 |
48 | extension FinalCutPro.FCPXML.ObjectTracker.TrackingShape {
49 | // TODO: add init after adding attribute properties
50 | }
51 |
52 | // MARK: - Structure
53 |
54 | extension FinalCutPro.FCPXML.ObjectTracker.TrackingShape {
55 | public enum Attributes: String {
56 | case id // required
57 | case name
58 | case offsetEnabled // 0 or 1, Default: 0
59 | case analysisMethod // enum case
60 | case dataLocator
61 | }
62 |
63 | }
64 |
65 | // MARK: - Attributes
66 |
67 | // TODO: Add attributes etc.
68 |
69 | // MARK: - Typing
70 |
71 | // ObjectTracker.TrackingShape
72 | extension XMLElement {
73 | /// FCPXML: Returns the element wrapped in a ``FinalCutPro/FCPXML/ObjectTracker/TrackingShape``
74 | /// model object.
75 | /// Call this on a `tracking-shape` element only.
76 | public var fcpAsTrackingShape: FinalCutPro.FCPXML.ObjectTracker.TrackingShape? {
77 | .init(element: self)
78 | }
79 | }
80 |
81 | #endif
82 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Resources/FCPXML ObjectTracker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML ObjectTracker.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import OTCore
11 |
12 | extension FinalCutPro.FCPXML {
13 | /// Object tracker shared resource.
14 | ///
15 | /// > Final Cut Pro FCPXML 1.11 Reference:
16 | /// >
17 | /// > Describe a tracked object such as a face or other moving object in a video clip.
18 | /// >
19 | /// > Users can track moving objects in video clips to match their movement to a clip, title,
20 | /// > logo, generator, or a still image, by using the object-tracker feature in Final Cut Pro.
21 | /// > They can also track the shape mask of a video effect, for example a blur, highlight, or
22 | /// > color effect, to a moving object in a video clip.
23 | /// >
24 | /// > See [`object-tracker`](https://developer.apple.com/documentation/professional_video_applications/fcpxml_reference/object-tracker).
25 | public struct ObjectTracker: FCPXMLElement {
26 | public let element: XMLElement
27 |
28 | public let elementType: ElementType = .objectTracker
29 |
30 | public static let supportedElementTypes: Set = [.objectTracker]
31 |
32 | public init() {
33 | element = XMLElement(name: elementType.rawValue)
34 | }
35 |
36 | public init?(element: XMLElement) {
37 | self.element = element
38 | guard _isElementTypeSupported(element: element) else { return nil }
39 | }
40 | }
41 | }
42 |
43 | // MARK: - Parameterized init
44 |
45 | extension FinalCutPro.FCPXML.ObjectTracker {
46 | public init(
47 | trackingShapes: [TrackingShape]
48 | ) {
49 | self.init()
50 |
51 | element._addChildren(trackingShapes)
52 | }
53 | }
54 |
55 | // MARK: - Structure
56 |
57 | extension FinalCutPro.FCPXML.ObjectTracker {
58 | // no Attributes
59 |
60 | // contains 1 or more `tracking-shape`
61 | }
62 |
63 | // MARK: - Children
64 |
65 | extension FinalCutPro.FCPXML.ObjectTracker {
66 | /// Returns child `tracking-shape` elements.
67 | public var trackingShapes: LazyFCPXMLChildrenSequence {
68 | element.children(whereFCPElement: .trackingShape)
69 | }
70 | }
71 |
72 | // MARK: - Typing
73 |
74 | // ObjectTracker
75 | extension XMLElement {
76 | /// FCPXML: Returns the element wrapped in a ``FinalCutPro/FCPXML/ObjectTracker`` model object.
77 | /// Call this on a `object-tracker` element only.
78 | public var fcpAsObjectTracker: FinalCutPro.FCPXML.ObjectTracker? {
79 | .init(element: self)
80 | }
81 | }
82 |
83 | #endif
84 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Roles/Meta/FCPXML RoleType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML RoleType.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import TimecodeKit
11 |
12 | extension FinalCutPro.FCPXML {
13 | /// Role type/classification.
14 | public enum RoleType: String, Equatable, Hashable, CaseIterable, Sendable {
15 | /// Audio role.
16 | case audio
17 |
18 | /// Video role.
19 | case video
20 |
21 | /// Closed caption role.
22 | case caption
23 | }
24 | }
25 |
26 | extension Set {
27 | public static let allCases: Self = Set(Element.allCases)
28 | }
29 |
30 | #endif
31 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Model/Roles/Meta/FCPXMLCollapsibleRole.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLRole.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | public protocol FCPXMLCollapsibleRole: FCPXMLRole {
12 | /// Returns the role with a collapsed sub-role, if the sub-role is derivative of the main role.
13 | /// Only applies to audio and video roles.
14 | /// Has no effect on closed caption roles since they do not contain sub-roles.
15 | ///
16 | /// The sub-role is considered collapsible if it is identical to the main role with a trailing
17 | /// dash and number.
18 | ///
19 | /// For example:
20 | ///
21 | /// - The raw role string `Role` has no sub-role, and is therefore already collapsed.
22 | /// - The raw role string `Role.Role-1` is considered collapsible and would return
23 | /// just the main `Role`, removing the sub-role.
24 | /// - The raw role string `Role.SubRole` is not considered collapsible since the sub-role is not
25 | /// derived from the main role. The main and sub-role would be returned unchanged.
26 | ///
27 | /// > Note:
28 | /// >
29 | /// > This is provided merely as a convenience for representing simplified role names to the
30 | /// > user. It does not play a direct role in encoding or decoding FCPXML.
31 | func collapsingSubRole() -> Self
32 | }
33 |
34 | #endif
35 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/Occlusion/FCPXML ElementOcclusion.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML ElementOcclusion.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | extension FinalCutPro.FCPXML {
12 | public enum ElementOcclusion: Equatable, Hashable, CaseIterable, Sendable {
13 | /// The element is not occluded at all by its parent.
14 | case notOccluded
15 |
16 | /// The element is partially occluded by its parent.
17 | case partiallyOccluded
18 |
19 | /// The element is fully occluded by its parent.
20 | case fullyOccluded
21 | }
22 | }
23 |
24 | extension Set {
25 | public static let allCases: Self = Set(Element.allCases)
26 | }
27 |
28 | #endif
29 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/XML/FCPXML Root Parsing.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXML Root Parsing.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 | import OTCore
11 | import TimecodeKit
12 |
13 | extension XMLElement {
14 | /// FCPXML: Returns the root-level `fcpxml` element.
15 | /// This may be called on any element within a FCPXML.
16 | public var fcpRoot: XMLElement? {
17 | rootDocument?
18 | .rootElement() // `fcpxml` element
19 | }
20 | }
21 |
22 | #endif
23 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FCPXML/XML/XMLParsableAttributesKey.swift:
--------------------------------------------------------------------------------
1 | //
2 | // XMLParsableAttributesKey.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import Foundation
10 |
11 | // not used after refactoring
12 |
13 | // protocol XMLParsableAttributesKey: Hashable, RawRepresentable, CaseIterable
14 | // where RawValue == String { }
15 |
16 | // extension XMLElement {
17 | // /// Utility:
18 | // /// Parse an XML element's attributes and return a key-value dictionary,
19 | // /// parsing only the keys contained in the `key` type passed.
20 | // /// Any missing keys will simply be omitted from the returned dictionary.
21 | // func parseRawAttributeValues(
22 | // key: K.Type
23 | // ) -> [K: String] where K: XMLParsableAttributesKey {
24 | // K.allCases.reduce(into: [:]) { dict, key in
25 | // dict[key] = stringValue(forAttributeNamed: key.rawValue)
26 | // }
27 | // }
28 |
29 | // /// Utility:
30 | // /// Returns attribute string value for the given attribute key name.
31 | // subscript(_ key: some XMLParsableAttributesKey) -> String? {
32 | // self.attributeStringValue(forName: key.rawValue)
33 | // }
34 | // }
35 |
36 | #endif
37 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/FinalCutPro/FinalCutPro.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FinalCutPro.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 | import TimecodeKit
9 |
10 | /// Collection of methods and structures related to Final Cut Pro.
11 | /// Do not instance; use methods within directly.
12 | public enum FinalCutPro {
13 | /// `Timecode` setting for `.subFramesBase`.
14 | /// Final Cut Pro uses 80 subframes per frame.
15 | public static let timecodeSubFramesBase: Timecode.SubFramesBase = .max80SubFrames
16 |
17 | /// `Timecode` setting for `.upperLimit`.
18 | /// Final Cut Pro is confined to a 24-hour SMPTE timecode clock.
19 | public static let timecodeUpperLimit: Timecode.UpperLimit = .max24Hours
20 |
21 | /// `Timecode` setting for `.stringFormat`.
22 | public static let timecodeStringFormat: Timecode.StringFormat = []
23 | }
24 |
25 | extension FinalCutPro {
26 | /// `Timecode` template.
27 | public static func formTimecode(
28 | at rate: TimecodeFrameRate
29 | ) -> Timecode {
30 | Timecode(
31 | .zero,
32 | at: rate,
33 | base: timecodeSubFramesBase,
34 | limit: timecodeUpperLimit
35 | )
36 | }
37 |
38 | /// `Timecode` template.
39 | public static func formTimecode(
40 | rational: Fraction,
41 | at rate: TimecodeFrameRate
42 | ) throws -> Timecode {
43 | try Timecode(
44 | .rational(rational),
45 | at: rate,
46 | base: timecodeSubFramesBase,
47 | limit: timecodeUpperLimit
48 | )
49 | }
50 |
51 | /// `Timecode` template.
52 | public static func formTimecode(
53 | realTime seconds: TimeInterval,
54 | at rate: TimecodeFrameRate
55 | ) throws -> Timecode {
56 | try Timecode(
57 | .realTime(seconds: seconds),
58 | at: rate,
59 | base: timecodeSubFramesBase,
60 | limit: timecodeUpperLimit
61 | )
62 | }
63 |
64 | /// `TimecodeInterval` template.
65 | public static func formTimecodeInterval(
66 | at rate: TimecodeFrameRate
67 | ) -> TimecodeInterval {
68 | let tc = formTimecode(at: rate)
69 | return TimecodeInterval(tc)
70 | }
71 |
72 | /// `TimecodeInterval` template.
73 | public static func formTimecodeInterval(
74 | realTime: TimeInterval,
75 | at rate: TimecodeFrameRate
76 | ) throws -> TimecodeInterval {
77 |
78 | try TimecodeInterval(
79 | realTime: realTime,
80 | at: rate,
81 | base: timecodeSubFramesBase,
82 | limit: timecodeUpperLimit
83 | )
84 | }
85 |
86 | /// `TimecodeInterval` template.
87 | public static func formTimecodeInterval(
88 | rational: Fraction,
89 | at rate: TimecodeFrameRate
90 | ) throws -> TimecodeInterval {
91 | try TimecodeInterval(
92 | rational,
93 | at: rate,
94 | base: timecodeSubFramesBase,
95 | limit: timecodeUpperLimit
96 | )
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/MIDIFile/Errors/MIDIFile BuildError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MIDIFile BuildError.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 | import MIDIKitSMF
9 |
10 | extension MIDIFile {
11 | /// Cubase track archive XML parsing error.
12 | public enum BuildError: Error {
13 | case general(String)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/ProTools/ProTools.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProTools.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 | import TimecodeKit
9 |
10 | /// Collection of methods and structures related to Pro Tools.
11 | public enum ProTools {
12 | /// `Timecode` setting for `.subFramesBase`.
13 | /// Pro Tools uses 100 subframes per frame.
14 | public static let timecodeSubFramesBase: Timecode.SubFramesBase = .max100SubFrames
15 |
16 | /// `Timecode` setting for `.upperLimit`.
17 | /// Pro Tools uses a 24-hour SMPTE timecode clock.
18 | public static let timecodeUpperLimit: Timecode.UpperLimit = .max24Hours
19 |
20 | /// `Timecode` setting for `.stringFormat`.
21 | public static let timecodeStringFormat: Timecode.StringFormat = []
22 | }
23 |
24 | extension ProTools {
25 | /// `Timecode` struct template.
26 | public static func formTimecode(
27 | _ exactly: Timecode.Components,
28 | at rate: TimecodeFrameRate
29 | ) throws -> Timecode {
30 | try Timecode(
31 | .components(exactly),
32 | at: rate,
33 | base: timecodeSubFramesBase,
34 | limit: timecodeUpperLimit
35 | )
36 | }
37 |
38 | /// `Timecode` struct template.
39 | public static func formTimecode(
40 | _ exactly: String,
41 | at rate: TimecodeFrameRate
42 | ) throws -> Timecode {
43 | try Timecode(
44 | .string(exactly),
45 | at: rate,
46 | base: timecodeSubFramesBase,
47 | limit: timecodeUpperLimit
48 | )
49 | }
50 |
51 | /// `Timecode` struct template.
52 | public static func formTimecode(
53 | realTimeValue: TimeInterval,
54 | at rate: TimecodeFrameRate
55 | ) throws -> Timecode {
56 | try Timecode(
57 | .realTime(seconds: realTimeValue),
58 | at: rate,
59 | base: timecodeSubFramesBase,
60 | limit: timecodeUpperLimit
61 | )
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/ProTools/SessionInfo/Errors/SessionInfo ParseError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SessionInfo ParseError.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 |
9 | extension ProTools.SessionInfo {
10 | /// Pro Tools session info text file parsing error.
11 | public enum ParseError: Error {
12 | case general(String)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/ProTools/SessionInfo/Messages/SessionInfo ParseMessage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SessionInfo ParseMessage.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 |
9 | extension ProTools.SessionInfo {
10 | public enum ParseMessage {
11 | /// Info message.
12 | /// Can be disregarded and only useful for debugging.
13 | case info(String)
14 |
15 | /// Error message.
16 | /// Something was malformed or data format was not expected.
17 | case error(String)
18 | }
19 | }
20 |
21 | // MARK: - Extensions
22 |
23 | extension Collection where Element == ProTools.SessionInfo.ParseMessage {
24 | /// Returns all `.info` cases as enum-unwrapped Strings.
25 | public var infos: [String] {
26 | reduce(into: [String]()) {
27 | switch $1 {
28 | case let .info(message):
29 | $0.append(message)
30 | default:
31 | break
32 | }
33 | }
34 | }
35 |
36 | /// Returns all `.error` cases as enum-unwrapped Strings.
37 | public var errors: [String] {
38 | reduce(into: [String]()) {
39 | switch $1 {
40 | case let .error(message):
41 | $0.append(message)
42 | default:
43 | break
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/ProTools/SessionInfo/SessionInfo Clip.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SessionInfo Clip.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 |
9 | extension ProTools.SessionInfo {
10 | /// Represents a clip used in the session.
11 | public struct Clip: Equatable, Hashable {
12 | public internal(set) var name: String = ""
13 | public internal(set) var sourceFile: String = ""
14 | public internal(set) var channel: String?
15 |
16 | /// Flag determining if clip was online (true) or offline (false)
17 | public internal(set) var online: Bool = true
18 | }
19 | }
20 |
21 | extension ProTools.SessionInfo.Clip: Sendable { }
22 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/ProTools/SessionInfo/SessionInfo File.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SessionInfo File.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 |
9 | extension ProTools.SessionInfo {
10 | /// Represents a file used in the session
11 | public struct File: Equatable, Hashable {
12 | public internal(set) var filename: String = ""
13 | public internal(set) var path: String = ""
14 |
15 | /// Flag determining if file was online (true) or offline (false)
16 | public internal(set) var online: Bool = true
17 | }
18 | }
19 |
20 | extension ProTools.SessionInfo.File: Sendable { }
21 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/ProTools/SessionInfo/SessionInfo Main.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SessionInfo Main.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 | import TimecodeKit
9 |
10 | extension ProTools.SessionInfo {
11 | /// Contains the global session meta data
12 | /// (from the Session Info Text file header)
13 | public struct Main: Equatable, Hashable {
14 | public internal(set) var name: String?
15 |
16 | public internal(set) var sampleRate: Double?
17 | public internal(set) var bitDepth: String?
18 |
19 | public internal(set) var startTimecode: Timecode?
20 | public internal(set) var frameRate: TimecodeFrameRate?
21 |
22 | public internal(set) var audioTrackCount: Int?
23 | public internal(set) var audioClipCount: Int?
24 | public internal(set) var audioFileCount: Int?
25 | }
26 | }
27 |
28 | extension ProTools.SessionInfo.Main: Sendable { }
29 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/ProTools/SessionInfo/SessionInfo OrphanData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SessionInfo OrphanData.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 |
9 | extension ProTools.SessionInfo {
10 | public struct OrphanData: Equatable, Hashable {
11 | public internal(set) var heading: String
12 | public internal(set) var content: [String]
13 | }
14 | }
15 |
16 | extension ProTools.SessionInfo.OrphanData: Sendable { }
17 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/ProTools/SessionInfo/SessionInfo Plugin.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SessionInfo Plugin.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 |
9 | extension ProTools.SessionInfo {
10 | /// Represents a plug-in used in the session
11 | public struct Plugin: Equatable, Hashable {
12 | public internal(set) var manufacturer: String = ""
13 | public internal(set) var name: String = ""
14 | public internal(set) var version: String = ""
15 | public internal(set) var format: String = ""
16 | public internal(set) var stems: String = ""
17 | public internal(set) var numberOfInstances: String = ""
18 | }
19 | }
20 |
21 | extension ProTools.SessionInfo.Plugin: Sendable { }
22 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/ProTools/SessionInfo/SessionInfo Track.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SessionInfo Track.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 | import TimecodeKit
9 |
10 | extension ProTools.SessionInfo {
11 | /// Represents a track and its contents.
12 | public struct Track: Equatable, Hashable {
13 | public internal(set) var name: String = ""
14 | public internal(set) var comments: String = ""
15 | public internal(set) var userDelay: Int = 0
16 | public internal(set) var state: Set = []
17 | public internal(set) var plugins: [String] = []
18 | public internal(set) var clips: [Clip] = []
19 | }
20 | }
21 |
22 | extension ProTools.SessionInfo.Track: Sendable { }
23 |
24 | // MARK: - State
25 |
26 | extension ProTools.SessionInfo.Track {
27 | /// A track's state.
28 | public enum State: String {
29 | case inactive = "Inactive"
30 | case hidden = "Hidden"
31 | case muted = "Muted"
32 | case solo = "Solo"
33 | case soloSafe = "SoloSafe"
34 | }
35 | }
36 |
37 | extension ProTools.SessionInfo.Track.State: Sendable { }
38 |
39 | // MARK: - Clip
40 |
41 | extension ProTools.SessionInfo.Track {
42 | /// Represents a clip contained on a track.
43 | public struct Clip: Equatable, Hashable {
44 | public internal(set) var channel: Int = 0
45 | public internal(set) var event: Int = 0
46 | public internal(set) var name: String = ""
47 | public internal(set) var startTime: ProTools.SessionInfo.TimeValue?
48 | public internal(set) var endTime: ProTools.SessionInfo.TimeValue?
49 | public internal(set) var duration: ProTools.SessionInfo.TimeValue?
50 | public internal(set) var state: State = .unmuted
51 | }
52 | }
53 |
54 | extension ProTools.SessionInfo.Track.Clip: Sendable { }
55 |
56 | // MARK: - Clip State
57 |
58 | extension ProTools.SessionInfo.Track.Clip {
59 | /// A clip's state (such as 'Muted', 'Unmuted')
60 | public enum State: String {
61 | // TODO: there may be more states possible than this -- need to test
62 | case muted = "Muted"
63 | case unmuted = "Unmuted"
64 | }
65 | }
66 |
67 | extension ProTools.SessionInfo.Track.Clip.State: Sendable { }
68 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/ProTools/SessionInfo/SessionInfo Versions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SessionInfo Versions.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2023 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 |
9 | extension ProTools.SessionInfo {
10 | public enum MarkersListingVersion: Equatable, Hashable {
11 | /// Pro Tools versions prior to 2023.12.
12 | case legacy
13 |
14 | /// Pro Tools 2023.12 and up.
15 | case pt2023_12
16 | }
17 | }
18 |
19 | extension ProTools.SessionInfo.MarkersListingVersion: Sendable { }
20 |
21 | extension ProTools.SessionInfo.MarkersListingVersion {
22 | public var columnCount: Int {
23 | switch self {
24 | case .legacy: return 6
25 | case .pt2023_12: return 8
26 | }
27 | }
28 |
29 | public var commentColumnIndex: Int {
30 | switch self {
31 | case .legacy: return 5
32 | case .pt2023_12: return 7
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/ProTools/SessionInfo/SessionInfo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SessionInfo.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 | import TimecodeKit
9 |
10 | // MARK: - ProTools.SessionInfo
11 |
12 | extension ProTools {
13 | /// Contains parsed data after reading a Pro Tools Session Info text file.
14 | public struct SessionInfo: Equatable, Hashable {
15 | /// Meta data contained in the main header of the data file.
16 | public internal(set) var main = Main()
17 |
18 | /// Files listing (online).
19 | public internal(set) var onlineFiles: [File]?
20 |
21 | /// Files listing (offline).
22 | public internal(set) var offlineFiles: [File]?
23 |
24 | /// Clips listing (online).
25 | public internal(set) var onlineClips: [Clip]?
26 |
27 | /// Clips listing (offline).
28 | public internal(set) var offlineClips: [Clip]?
29 |
30 | /// Plugin listing.
31 | public internal(set) var plugins: [Plugin]?
32 |
33 | /// Tracks listing.
34 | public internal(set) var tracks: [Track]?
35 |
36 | /// Markers listing.
37 | public internal(set) var markers: [Marker]?
38 |
39 | /// Holds any extraneous sections or data that was not recognized while parsing the file.
40 | public internal(set) var orphanData: [OrphanData]?
41 | }
42 | }
43 |
44 | extension ProTools.SessionInfo: Sendable { }
45 |
46 | // MARK: - Constants
47 |
48 | extension ProTools.SessionInfo {
49 | /// Array of file types for use with NSOpenPanel / NSSavePanel
50 | public static let fileTypes = ["public.txt", "txt"]
51 | }
52 |
--------------------------------------------------------------------------------
/Sources/DAWFileKit/Utilities/Utilities.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Utilities.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import Foundation
8 |
9 | @propertyWrapper
10 | public struct EquatableExempt: Equatable {
11 | public var wrappedValue: Value
12 |
13 | public static func == (lhs: EquatableExempt,
14 | rhs: EquatableExempt) -> Bool {
15 | true
16 | }
17 |
18 | public init(wrappedValue: Value) {
19 | self.wrappedValue = wrappedValue
20 | }
21 | }
22 |
23 | @propertyWrapper
24 | public struct EquatableAndHashableExempt: Equatable, Hashable {
25 | public var wrappedValue: Value
26 |
27 | public static func == (lhs: EquatableAndHashableExempt,
28 | rhs: EquatableAndHashableExempt) -> Bool {
29 | true
30 | }
31 |
32 | public func hash(into hasher: inout Hasher) { }
33 |
34 | public init(wrappedValue: Value) {
35 | self.wrappedValue = wrappedValue
36 | }
37 | }
38 |
39 | extension Sequence {
40 | /// Wraps the sequence in a `AnySequence` instance.
41 | var asAnySequence: AnySequence {
42 | AnySequence(self)
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/Cubase/Cubase TrackArchive Helper Tests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Cubase TrackArchive Helper Tests.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import XCTest
10 | @testable import DAWFileKit
11 | import OTCore
12 | import TimecodeKit
13 |
14 | class Cubase_Helper_Tests: XCTestCase {
15 | override func setUp() { }
16 | override func tearDown() { }
17 |
18 | func testCollection_XMLElement_FilterAttribute() throws {
19 | // prep
20 |
21 | let nodes = [
22 | try XMLElement(xmlString: ""),
23 | try XMLElement(xmlString: ""),
24 | try XMLElement(xmlString: ""),
25 | try XMLElement(xmlString: "")
26 | ]
27 |
28 | // test
29 |
30 | let filteredA = nodes.filter(whereNameAttributeValue: "name2").zeroIndexed
31 | XCTAssertEqual(filteredA[0], nodes[1])
32 |
33 | let filteredB = nodes.filter(whereClassAttributeValue: "classA").zeroIndexed
34 | XCTAssertEqual(filteredB[0], nodes[0])
35 | XCTAssertEqual(filteredB[1], nodes[1])
36 | }
37 |
38 | func testCollection_XMLElement_FirstAttribute() throws {
39 | // prep
40 |
41 | let nodes = [
42 | try XMLElement(xmlString: ""),
43 | try XMLElement(xmlString: ""),
44 | try XMLElement(xmlString: ""),
45 | try XMLElement(xmlString: "")
46 | ]
47 |
48 | // test
49 |
50 | let firstA = nodes.first(whereNameAttributeValue: "name2")
51 | XCTAssertEqual(firstA, nodes[1])
52 |
53 | let firstB = nodes.first(whereClassAttributeValue: "classA")
54 | XCTAssertEqual(firstB, nodes[0])
55 | }
56 | }
57 |
58 | #endif
59 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/DAWFileKitTests Constants.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DAWFileKitTests Constants.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import XCTest
8 |
9 | // MARK: - Constants
10 |
11 | enum ResourcesFolder: String {
12 | case cubaseTrackArchiveXML = "Cubase TrackArchive XML Exports"
13 | case ptSessionTextExports = "PT Session Text Exports"
14 | case fcpxmlExports = "FCPXML Exports"
15 | }
16 |
17 | // MARK: - Utilities
18 |
19 | func loadFileContents(
20 | forResource: String,
21 | withExtension: String,
22 | subFolder: ResourcesFolder?
23 | ) -> Data? {
24 | guard let url = Bundle.module.url(
25 | forResource: forResource,
26 | withExtension: withExtension,
27 | subdirectory: subFolder?.rawValue
28 | )
29 | else {
30 | XCTFail("Could not form URL, possibly could not find file.")
31 | return nil
32 | }
33 |
34 | guard let data = try? Data(contentsOf: url)
35 | else { XCTFail("Could not read file at URL \(url.absoluteString.quoted)."); return nil }
36 |
37 | return data
38 | }
39 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/FCPXMLTestCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FCPXMLTestCase.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import XCTest
10 | @testable import DAWFileKit
11 | import OTCore
12 | import TimecodeKit
13 |
14 | protocol FCPXMLUtilities { }
15 |
16 | extension FCPXMLUtilities {
17 | static func tc(_ timecodeString: String, _ fr: TimecodeFrameRate) -> Timecode {
18 | try! Timecode(.string(timecodeString), at: fr, base: .max80SubFrames)
19 | }
20 |
21 | static func tc(frames: Int, _ fr: TimecodeFrameRate) -> Timecode {
22 | try! Timecode(.frames(frames), at: fr, base: .max80SubFrames)
23 | }
24 |
25 | static func tc(_ rational: Fraction, _ fr: TimecodeFrameRate) -> Timecode {
26 | try! Timecode(.rational(rational), at: fr, base: .max80SubFrames)
27 | }
28 |
29 | static func tc(seconds: TimeInterval, _ fr: TimecodeFrameRate) -> Timecode {
30 | try! Timecode(.realTime(seconds: seconds), at: fr, base: .max80SubFrames)
31 | }
32 |
33 | static func tcInterval(frames: Int, _ fr: TimecodeFrameRate) -> TimecodeInterval {
34 | if frames < 0 {
35 | return .negative(tc(frames: abs(frames), fr))
36 | } else {
37 | return .positive(tc(frames: frames, fr))
38 | }
39 | }
40 |
41 | static func fraction(frames: Int, _ fr: TimecodeFrameRate) -> Fraction {
42 | tcInterval(frames: frames, fr).rationalValue
43 | }
44 |
45 | static func debugString(for em: FinalCutPro.FCPXML.ExtractedMarker) -> String {
46 | let absTC: String
47 | if let absoluteStart = em.timecode() {
48 | absTC = absoluteStart.stringValue(format: [.showSubFrames]) + " @ " + absoluteStart.frameRate.stringValue
49 | } else {
50 | absTC = "??:??:??:??.?? @ ??"
51 | }
52 |
53 | let name = em.name.quoted
54 | let note = em.note != nil ? " note:\(em.note!.quoted)" : ""
55 | let durTC = em.duration()?.stringValue(format: [.showSubFrames]) ?? "?"
56 |
57 | let parentName = em.value(forContext: .parentName)?.quoted ?? "<>"
58 | return "\(absTC): \(name)\(note) dur:\(durTC) parent:\(parentName)"
59 | }
60 |
61 | static func debugString(for extractedMarkers: some Collection) -> String {
62 | extractedMarkers.map { debugString(for: $0) }.joined(separator: "\n")
63 | }
64 | }
65 |
66 | class FCPXMLTestCase: XCTestCase, FCPXMLUtilities { }
67 |
68 | #endif
69 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/File Tests/FinalCutPro FCPXML AudioOnly.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FinalCutPro FCPXML AudioOnly.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import XCTest
10 | @testable import DAWFileKit
11 | import OTCore
12 | import TimecodeKit
13 |
14 | final class FinalCutPro_FCPXML_AudioOnly: FCPXMLTestCase {
15 | override func setUp() { }
16 | override func tearDown() { }
17 |
18 | // MARK: - Test Data
19 |
20 | var fileContents: Data { get throws {
21 | try XCTUnwrap(loadFileContents(
22 | forResource: "AudioOnly",
23 | withExtension: "fcpxml",
24 | subFolder: .fcpxmlExports
25 | ))
26 | } }
27 |
28 | /// Project @ 24fps.
29 | let projectFrameRate: TimecodeFrameRate = .fps24
30 |
31 | func testParse() throws {
32 | // load
33 | let rawData = try fileContents
34 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
35 |
36 | // version
37 | XCTAssertEqual(fcpxml.version, .ver1_11)
38 |
39 | // skip testing file contents
40 | }
41 |
42 | func testExtractMarkers() async throws {
43 | // load file
44 | let rawData = try fileContents
45 |
46 | // load
47 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
48 |
49 | // project
50 | let project = try XCTUnwrap(fcpxml.allProjects().first)
51 |
52 | let extractedMarkers = await project
53 | .extract(preset: .markers, scope: .mainTimeline)
54 | .sortedByAbsoluteStartTimecode()
55 | // .zeroIndexed // not necessary after sorting - sort returns new array
56 |
57 | let markers = extractedMarkers
58 |
59 | let expectedMarkerCount = 10
60 | XCTAssertEqual(markers.count, expectedMarkerCount)
61 |
62 | // print("Markers sorted by absolute timecode:")
63 | // print(Self.debugString(for: markers))
64 | }
65 |
66 | func testExtractRoles() async throws {
67 | // load file
68 | let rawData = try fileContents
69 |
70 | // load
71 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
72 |
73 | // project
74 | let project = try XCTUnwrap(fcpxml.allProjects().first)
75 |
76 | let roles = await project.extract(
77 | preset: .roles(roleTypes: .allCases),
78 | scope: .deep(auditions: .active, mcClipAngles: .active)
79 | )
80 |
81 | dump(roles)
82 |
83 | XCTAssertEqual(roles.count, 2)
84 | XCTAssertTrue(roles.contains(.audio(raw: "music.music-1")!))
85 | XCTAssertTrue(roles.contains(.audio(raw: "effects")!))
86 | }
87 | }
88 |
89 | #endif
90 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/File Tests/FinalCutPro FCPXML Keywords.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FinalCutPro FCPXML Keywords.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import XCTest
10 | @testable import DAWFileKit
11 | import OTCore
12 | import TimecodeKit
13 |
14 | final class FinalCutPro_FCPXML_Keywords: FCPXMLTestCase {
15 | override func setUp() { }
16 | override func tearDown() { }
17 |
18 | // MARK: - Test Data
19 |
20 | var fileContents: Data { get throws {
21 | try XCTUnwrap(loadFileContents(
22 | forResource: "Keywords",
23 | withExtension: "fcpxml",
24 | subFolder: .fcpxmlExports
25 | ))
26 | } }
27 |
28 | // MARK: - Tests
29 |
30 | func testParse() throws {
31 | // load file
32 | let rawData = try fileContents
33 |
34 | // parse file
35 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
36 |
37 | // version
38 | XCTAssertEqual(fcpxml.version, .ver1_11)
39 | }
40 |
41 | /// Test keywords that apply to each marker.
42 | func testExtractMarkers() async throws {
43 | // load file
44 | let rawData = try fileContents
45 |
46 | // load
47 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
48 |
49 | // project
50 | let project = try XCTUnwrap(fcpxml.allProjects().first)
51 |
52 | let extractedMarkers = await project
53 | .extract(preset: .markers, scope: .mainTimeline)
54 | .sortedByAbsoluteStartTimecode()
55 | // .zeroIndexed // not necessary after sorting - sort returns new array
56 |
57 | let markers = extractedMarkers
58 |
59 | let expectedMarkerCount = 6
60 | XCTAssertEqual(markers.count, expectedMarkerCount)
61 |
62 | print("Markers sorted by absolute timecode:")
63 | print(Self.debugString(for: markers))
64 |
65 | // markers
66 |
67 | let marker1 = try XCTUnwrap(markers[safe: 0])
68 | let marker2 = try XCTUnwrap(markers[safe: 1])
69 | let marker3 = try XCTUnwrap(markers[safe: 2])
70 | let marker4 = try XCTUnwrap(markers[safe: 3])
71 | let marker5 = try XCTUnwrap(markers[safe: 4])
72 | let marker6 = try XCTUnwrap(markers[safe: 5])
73 |
74 | // Check keywords while constraining to keyword ranges
75 | XCTAssertEqual(marker1.keywords(constrainToKeywordRanges: true), ["flower", "nature"])
76 | XCTAssertEqual(marker2.keywords(constrainToKeywordRanges: true), ["birds"])
77 | XCTAssertEqual(marker3.keywords(constrainToKeywordRanges: true), ["flower", "nature"])
78 | XCTAssertEqual(marker4.keywords(constrainToKeywordRanges: true), ["lava", "nature"])
79 | XCTAssertEqual(marker5.keywords(constrainToKeywordRanges: true), ["penguin"])
80 | XCTAssertEqual(marker6.keywords(constrainToKeywordRanges: true), ["noStartOrDuration", "penguin"])
81 | }
82 | }
83 |
84 | #endif
85 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/File Tests/FinalCutPro FCPXML RolesList.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FinalCutPro FCPXML RolesList.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import XCTest
10 | @testable import DAWFileKit
11 | import OTCore
12 | import TimecodeKit
13 |
14 | final class FinalCutPro_FCPXML_RolesList: FCPXMLTestCase {
15 | override func setUp() { }
16 | override func tearDown() { }
17 |
18 | // MARK: - Test Data
19 |
20 | var fileContents: Data { get throws {
21 | try XCTUnwrap(loadFileContents(
22 | forResource: "RolesList",
23 | withExtension: "fcpxml",
24 | subFolder: .fcpxmlExports
25 | ))
26 | } }
27 |
28 | /// Project @ 24fps.
29 | let projectFrameRate: TimecodeFrameRate = .fps24
30 |
31 | func testParse() throws {
32 | // load
33 | let rawData = try fileContents
34 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
35 |
36 | // version
37 | XCTAssertEqual(fcpxml.version, .ver1_11)
38 |
39 | // skip testing file contents, we only care about roles extraction
40 | }
41 |
42 | func testExtractRoles() async throws {
43 | // load file
44 | let rawData = try fileContents
45 |
46 | // load
47 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
48 |
49 | // project
50 | let project = try XCTUnwrap(fcpxml.allProjects().first)
51 |
52 | let roles = await project.extract(
53 | preset: .roles(roleTypes: .allCases),
54 | scope: .deep(auditions: .active, mcClipAngles: .active)
55 | )
56 |
57 | // dump(roles)
58 |
59 | XCTAssertEqual(roles.count, 4)
60 | XCTAssertTrue(roles.contains(.video(raw: "Video")!))
61 | XCTAssertTrue(roles.contains(.video(raw: "FIXING.FIXING-1")!))
62 | XCTAssertTrue(roles.contains(.video(raw: "TO-DO.TO-DO-1")!))
63 | XCTAssertTrue(roles.contains(.video(raw: "VFX.VFX-1")!))
64 | }
65 | }
66 |
67 | #endif
68 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/File Tests/FinalCutPro FCPXML StandaloneAssetClip.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FinalCutPro FCPXML StandaloneAssetClip.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import XCTest
10 | @testable import DAWFileKit
11 | import OTCore
12 | import TimecodeKit
13 |
14 | final class FinalCutPro_FCPXML_StandaloneAssetClip: FCPXMLTestCase {
15 | override func setUp() { }
16 | override func tearDown() { }
17 |
18 | // MARK: - Test Data
19 |
20 | var fileContents: Data { get throws {
21 | try XCTUnwrap(loadFileContents(
22 | forResource: "StandaloneAssetClip",
23 | withExtension: "fcpxml",
24 | subFolder: .fcpxmlExports
25 | ))
26 | } }
27 |
28 | // MARK: - Tests
29 |
30 | func testParse() throws {
31 | // load file
32 | let rawData = try fileContents
33 |
34 | // parse file
35 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
36 |
37 | // version
38 | XCTAssertEqual(fcpxml.version, .ver1_11)
39 | }
40 |
41 | /// Test that FCPXML that doesn't contain a project is still able to extract standalone clips.
42 | func testExtract() async throws {
43 | // load file
44 | let rawData = try fileContents
45 |
46 | // load
47 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
48 |
49 | // timelines
50 | let timelines = fcpxml.allTimelines()
51 | XCTAssertEqual(timelines.count, 1)
52 |
53 | let anyTimeline = try XCTUnwrap(timelines.first)
54 |
55 | // AnyTimeline
56 |
57 | let timelineStartTC = try XCTUnwrap(anyTimeline.timelineStartAsTimecode())
58 | XCTAssertEqual(timelineStartTC.components, .init(h: 00, m: 59, s: 50, f: 00))
59 | XCTAssertEqual(timelineStartTC.frameRate, .fps29_97)
60 | let timelineDurTC = try XCTUnwrap(anyTimeline.timelineDurationAsTimecode())
61 | XCTAssertEqual(timelineDurTC.components, .init(h: 00, m: 00, s: 10, f: 00))
62 | XCTAssertEqual(timelineDurTC.frameRate, .fps29_97)
63 |
64 | // unwrap AssetClip
65 |
66 | guard case .assetClip(let assetClip) = anyTimeline else { XCTFail() ; return }
67 |
68 | // FCPXMLElementMetaTimeline
69 | let assetClipStartTC = try XCTUnwrap(anyTimeline.timelineStartAsTimecode())
70 | XCTAssertEqual(assetClipStartTC.components, .init(h: 00, m: 59, s: 50, f: 00))
71 | XCTAssertEqual(assetClipStartTC.frameRate, .fps29_97)
72 | let assetClipDurTC = try XCTUnwrap(anyTimeline.timelineDurationAsTimecode())
73 | XCTAssertEqual(assetClipDurTC.components, .init(h: 00, m: 00, s: 10, f: 00))
74 | XCTAssertEqual(assetClipDurTC.frameRate, .fps29_97)
75 |
76 | // local XML attributes
77 | let clipStartTC = try XCTUnwrap(assetClip.startAsTimecode())
78 | XCTAssertEqual(clipStartTC.components, .init(h: 00, m: 59, s: 50, f: 00))
79 | XCTAssertEqual(clipStartTC.frameRate, .fps29_97)
80 | let clipDurTC = try XCTUnwrap(assetClip.durationAsTimecode())
81 | XCTAssertEqual(clipDurTC.components, .init(h: 00, m: 00, s: 10, f: 00))
82 | XCTAssertEqual(clipDurTC.frameRate, .fps29_97)
83 | }
84 | }
85 |
86 | #endif
87 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/File Tests/FinalCutPro FCPXML StandaloneLibraryEventClip.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FinalCutPro FCPXML StandaloneLibraryEventClip.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import XCTest
10 | @testable import DAWFileKit
11 | import OTCore
12 | import TimecodeKit
13 |
14 | final class FinalCutPro_FCPXML_StandaloneLibraryEventClip: FCPXMLTestCase {
15 | override func setUp() { }
16 | override func tearDown() { }
17 |
18 | // MARK: - Test Data
19 |
20 | var fileContents: Data { get throws {
21 | try XCTUnwrap(loadFileContents(
22 | forResource: "StandaloneLibraryEventClip",
23 | withExtension: "fcpxml",
24 | subFolder: .fcpxmlExports
25 | ))
26 | } }
27 |
28 | // MARK: - Tests
29 |
30 | func testParse() throws {
31 | // load file
32 | let rawData = try fileContents
33 |
34 | // parse file
35 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
36 |
37 | // version
38 | XCTAssertEqual(fcpxml.version, .ver1_11)
39 | }
40 |
41 | /// Test that FCPXML that doesn't contain a project is still able to extract standalone clips.
42 | func testExtract() async throws {
43 | // load file
44 | let rawData = try fileContents
45 |
46 | // load
47 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
48 |
49 | // timelines
50 | let timelines = fcpxml.allTimelines()
51 | XCTAssertEqual(timelines.count, 1)
52 |
53 | let anyTimeline = try XCTUnwrap(timelines.first)
54 |
55 | // AnyTimeline
56 |
57 | let timelineStartTC = try XCTUnwrap(anyTimeline.timelineStartAsTimecode())
58 | XCTAssertEqual(timelineStartTC.components, .init(h: 00, m: 00, s: 00, f: 00))
59 | XCTAssertEqual(timelineStartTC.frameRate, .fps25)
60 | let timelineDurTC = try XCTUnwrap(anyTimeline.timelineDurationAsTimecode())
61 | XCTAssertEqual(timelineDurTC.components, .init(h: 00, m: 01, s: 31, f: 12))
62 | XCTAssertEqual(timelineDurTC.frameRate, .fps25)
63 |
64 | // unwrap RefClip
65 |
66 | guard case .refClip(let refClip) = anyTimeline else { XCTFail() ; return }
67 |
68 | // FCPXMLElementMetaTimeline
69 | let refClipStartTC = try XCTUnwrap(refClip.timelineStartAsTimecode())
70 | XCTAssertEqual(refClipStartTC.components, .init(h: 00, m: 00, s: 00, f: 00))
71 | XCTAssertEqual(refClipStartTC.frameRate, .fps25)
72 | let refClipDurTC = try XCTUnwrap(refClip.timelineDurationAsTimecode())
73 | XCTAssertEqual(refClipDurTC.components, .init(h: 00, m: 01, s: 31, f: 12))
74 | XCTAssertEqual(refClipDurTC.frameRate, .fps25)
75 |
76 | // local XML attributes
77 | // `ref-clip` itself doesn't have a start time, but its resource does
78 | XCTAssertNil(refClip.startAsTimecode())
79 | // `ref-clip` itself doesn't have a duration time, but its resource does
80 | XCTAssertNil(refClip.durationAsTimecode())
81 |
82 | // test markers
83 |
84 | let markers = await refClip.extract(preset: .markers, scope: .mainTimeline)
85 | XCTAssertEqual(markers.count, 10)
86 | }
87 | }
88 |
89 | #endif
90 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/File Tests/FinalCutPro FCPXML StandaloneRefClip.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FinalCutPro FCPXML StandaloneRefClip.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import XCTest
10 | @testable import DAWFileKit
11 | import OTCore
12 | import TimecodeKit
13 |
14 | final class FinalCutPro_FCPXML_StandaloneRefClip: FCPXMLTestCase {
15 | override func setUp() { }
16 | override func tearDown() { }
17 |
18 | // MARK: - Test Data
19 |
20 | var fileContents: Data { get throws {
21 | try XCTUnwrap(loadFileContents(
22 | forResource: "StandaloneRefClip",
23 | withExtension: "fcpxml",
24 | subFolder: .fcpxmlExports
25 | ))
26 | } }
27 |
28 | // MARK: - Tests
29 |
30 | func testParse() throws {
31 | // load file
32 | let rawData = try fileContents
33 |
34 | // parse file
35 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
36 |
37 | // version
38 | XCTAssertEqual(fcpxml.version, .ver1_11)
39 | }
40 |
41 | /// Test that FCPXML that doesn't contain a project is still able to extract standalone clips.
42 | func testExtract() async throws {
43 | // load file
44 | let rawData = try fileContents
45 |
46 | // load
47 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
48 |
49 | // timelines
50 | let timelines = fcpxml.allTimelines()
51 | XCTAssertEqual(timelines.count, 1)
52 |
53 | let anyTimeline = try XCTUnwrap(timelines.first)
54 |
55 | // AnyTimeline
56 |
57 | let timelineStartTC = try XCTUnwrap(anyTimeline.timelineStartAsTimecode())
58 | XCTAssertEqual(timelineStartTC.components, .init(h: 00, m: 00, s: 00, f: 00))
59 | XCTAssertEqual(timelineStartTC.frameRate, .fps29_97)
60 | let timelineDurTC = try XCTUnwrap(anyTimeline.timelineDurationAsTimecode())
61 | XCTAssertEqual(timelineDurTC.components, .init(h: 00, m: 00, s: 09, f: 00))
62 | XCTAssertEqual(timelineDurTC.frameRate, .fps29_97)
63 |
64 | // unwrap RefClip
65 |
66 | guard case .refClip(let refClip) = anyTimeline else { XCTFail() ; return }
67 |
68 | // FCPXMLElementMetaTimeline
69 | let refClipStartTC = try XCTUnwrap(refClip.timelineStartAsTimecode())
70 | XCTAssertEqual(refClipStartTC.components, .init(h: 00, m: 00, s: 00, f: 00))
71 | XCTAssertEqual(refClipStartTC.frameRate, .fps29_97)
72 | let refClipDurTC = try XCTUnwrap(refClip.timelineDurationAsTimecode())
73 | XCTAssertEqual(refClipDurTC.components, .init(h: 00, m: 00, s: 09, f: 00))
74 | XCTAssertEqual(refClipDurTC.frameRate, .fps29_97)
75 |
76 | // local XML attributes
77 | // `ref-clip` itself doesn't have a start time, but its resource does
78 | XCTAssertNil(refClip.startAsTimecode())
79 | // `ref-clip` itself doesn't have a duration time, but its resource does
80 | XCTAssertNil(refClip.durationAsTimecode())
81 |
82 | // test markers
83 |
84 | let markers = await refClip.extract(preset: .markers, scope: .mainTimeline)
85 | XCTAssertEqual(markers.count, 1)
86 | }
87 | }
88 |
89 | #endif
90 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/File Tests/FinalCutPro FCPXML SyncClipRoles2.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FinalCutPro FCPXML SyncClipRoles2.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import XCTest
10 | /* @testable */ import DAWFileKit
11 | import OTCore
12 | import TimecodeKit
13 |
14 | final class FinalCutPro_FCPXML_SyncClipRoles2: FCPXMLTestCase {
15 | override func setUp() { }
16 | override func tearDown() { }
17 |
18 | // MARK: - Test Data
19 |
20 | var fileContents: Data { get throws {
21 | try XCTUnwrap(loadFileContents(
22 | forResource: "SyncClipRoles2",
23 | withExtension: "fcpxml",
24 | subFolder: .fcpxmlExports
25 | ))
26 | } }
27 |
28 | /// Ensure that elements that can appear in various locations in the XML hierarchy are all found.
29 | func testParse() async throws {
30 | // load file
31 | let rawData = try fileContents
32 |
33 | // parse file
34 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
35 |
36 | // events
37 | let events = fcpxml.allEvents()
38 | XCTAssertEqual(events.count, 1)
39 |
40 | let event = try XCTUnwrap(events[safe: 0])
41 |
42 | // project
43 | let projects = event.projects.zeroIndexed
44 | XCTAssertEqual(projects.count, 1)
45 |
46 | let project = try XCTUnwrap(projects[safe: 0])
47 |
48 | // sequence
49 | let sequence = try XCTUnwrap(project.sequence)
50 |
51 | // spine
52 | let spine = try XCTUnwrap(sequence.spine)
53 |
54 | let storyElements = spine.storyElements.zeroIndexed
55 | XCTAssertEqual(storyElements.count, 1)
56 |
57 | // story elements
58 | let clip1 = try XCTUnwrap(storyElements[safe: 0]?.fcpAsSyncClip)
59 | // confirm we have the right clip
60 | XCTAssertEqual(clip1.name, "5A-1-1")
61 |
62 | let markers = clip1.storyElements
63 | .filter(whereFCPElement: .marker)
64 | .zeroIndexed
65 | XCTAssertEqual(markers.count, 1)
66 |
67 | let marker = try XCTUnwrap(markers.first)
68 | // confirm we have the right marker
69 | XCTAssertEqual(marker.name, "Marker 1")
70 | let extractedMarker = await marker.element.fcpExtract()
71 | XCTAssertEqual(
72 | extractedMarker.value(forContext: .absoluteStartAsTimecode()),
73 | Self.tc("01:01:18:03", .fps25)
74 | )
75 | XCTAssertEqual(extractedMarker.value(forContext: .inheritedRoles), [
76 | .defaulted(.video(raw: "Video")!) // from first video asset in sync clip
77 | // no audio role
78 | ])
79 | }
80 | }
81 |
82 | #endif
83 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/File Tests/FinalCutPro FCPXML TitlesRoles.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FinalCutPro FCPXML TitlesRoles.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import XCTest
10 | @testable import DAWFileKit
11 | import OTCore
12 | import TimecodeKit
13 |
14 | final class FinalCutPro_FCPXML_TitlesRoles: FCPXMLTestCase {
15 | override func setUp() { }
16 | override func tearDown() { }
17 |
18 | // MARK: - Test Data
19 |
20 | var fileContents: Data { get throws {
21 | try XCTUnwrap(loadFileContents(
22 | forResource: "TitlesRoles",
23 | withExtension: "fcpxml",
24 | subFolder: .fcpxmlExports
25 | ))
26 | } }
27 |
28 | /// Project @ 24fps.
29 | let projectFrameRate: TimecodeFrameRate = .fps24
30 |
31 | func testParse() throws {
32 | // load
33 | let rawData = try fileContents
34 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
35 |
36 | // version
37 | XCTAssertEqual(fcpxml.version, .ver1_11)
38 |
39 | // skip testing file contents, we only care about roles assigned to markers for these tests
40 | }
41 |
42 | func testExtractMarkers() async throws {
43 | // load file
44 | let rawData = try fileContents
45 |
46 | // load
47 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
48 |
49 | // project
50 | let project = try XCTUnwrap(fcpxml.allProjects().first)
51 |
52 | let extractedMarkers = await project
53 | .extract(preset: .markers, scope: .deep())
54 | .sortedByAbsoluteStartTimecode()
55 | // .zeroIndexed // not necessary after sorting - sort returns new array
56 |
57 | let markers = extractedMarkers
58 |
59 | let expectedMarkerCount = 2
60 | XCTAssertEqual(markers.count, expectedMarkerCount)
61 |
62 | print("Markers sorted by absolute timecode:")
63 | print(Self.debugString(for: markers))
64 |
65 | // Titles clips should never have an audio role
66 |
67 | let marker1 = try XCTUnwrap(markers[safe: 0])
68 |
69 | XCTAssertEqual(marker1.roles, [
70 | .defaulted(.video(.titlesRole))
71 | ])
72 |
73 | let marker2 = try XCTUnwrap(markers[safe: 1])
74 |
75 | // In FCP, this Title clip anchored to has the role of Titles
76 | XCTAssertEqual(marker2.roles, [
77 | .defaulted(.video(.titlesRole))
78 | ])
79 | }
80 | }
81 |
82 | #endif
83 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/Logic and Parsing/FinalCutPro FCPXML Library Tests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FinalCutPro FCPXML Library Tests.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import XCTest
10 | @testable import DAWFileKit
11 | import OTCore
12 | import TimecodeKit
13 |
14 | final class FinalCutPro_FCPXML_Library: FCPXMLTestCase {
15 | override func setUp() { }
16 | override func tearDown() { }
17 |
18 | func testLocation() throws {
19 | let url = try XCTUnwrap(URL(string: "file:///Users/user/Movies/MyLibrary.fcpbundle/"))
20 | let library = FinalCutPro.FCPXML.Library(location: url)
21 |
22 | XCTAssertEqual(library.location, url)
23 | }
24 |
25 | func testName() throws {
26 | let url = try XCTUnwrap(URL(string: "file:///Users/user/Movies/MyLibrary.fcpbundle/"))
27 | let library = FinalCutPro.FCPXML.Library(location: url)
28 |
29 | XCTAssertEqual(library.name, "MyLibrary")
30 | }
31 | }
32 |
33 | #endif
34 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/Logic and Parsing/FinalCutPro FCPXML Root Version Tests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FinalCutPro FCPXML Root Version Tests.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import XCTest
10 | /* @testable */ import DAWFileKit
11 | import OTCore
12 | import TimecodeKit
13 |
14 | final class FinalCutPro_FCPXML_RootVersionTests: FCPXMLTestCase {
15 | override func setUp() { }
16 | override func tearDown() { }
17 |
18 | typealias Version = FinalCutPro.FCPXML.Version
19 |
20 | func testVersion() {
21 | let v = Version(major: 1, minor: 12)
22 |
23 | XCTAssertEqual(v.major, 1)
24 | XCTAssertEqual(v.minor, 12)
25 | }
26 |
27 | func testVersion_Equatable() {
28 | XCTAssertEqual(
29 | Version(major: 1, minor: 12),
30 | Version(major: 1, minor: 12)
31 | )
32 |
33 | XCTAssertNotEqual(
34 | Version(major: 1, minor: 12),
35 | Version(major: 1, minor: 13)
36 | )
37 |
38 | XCTAssertNotEqual(
39 | Version(major: 1, minor: 12),
40 | Version(major: 2, minor: 12)
41 | )
42 | }
43 |
44 | func testVersion_Comparable() {
45 | XCTAssertFalse(
46 | Version(major: 1, minor: 12) < Version(major: 1, minor: 12)
47 | )
48 |
49 | XCTAssertFalse(
50 | Version(major: 1, minor: 12) > Version(major: 1, minor: 12)
51 | )
52 |
53 | XCTAssertTrue(
54 | Version(major: 1, minor: 11) < Version(major: 1, minor: 12)
55 | )
56 |
57 | XCTAssertTrue(
58 | Version(major: 1, minor: 12) > Version(major: 1, minor: 11)
59 | )
60 |
61 | XCTAssertTrue(
62 | Version(major: 1, minor: 10) < Version(major: 2, minor: 3)
63 | )
64 |
65 | XCTAssertTrue(
66 | Version(major: 2, minor: 3) > Version(major: 1, minor: 10)
67 | )
68 | }
69 |
70 | func testVersion_RawValue_Invalid() throws {
71 | XCTAssertNil(Version(rawValue: ""))
72 | XCTAssertNil(Version(rawValue: "1."))
73 | XCTAssertNil(Version(rawValue: "1"))
74 | XCTAssertNil(Version(rawValue: "1.A"))
75 | XCTAssertNil(Version(rawValue: "A"))
76 | XCTAssertNil(Version(rawValue: "A.1"))
77 | XCTAssertNil(Version(rawValue: "A.A"))
78 | XCTAssertNil(Version(rawValue: "A.A.A"))
79 | XCTAssertNil(Version(rawValue: "1.12."))
80 | XCTAssertNil(Version(rawValue: "1.12.A"))
81 | }
82 |
83 | func testVersion_Init_RawValue() throws {
84 | let v = try XCTUnwrap(Version(rawValue: "1.12"))
85 |
86 | XCTAssertEqual(v.major, 1)
87 | XCTAssertEqual(v.minor, 12)
88 | }
89 |
90 | func testVersion_RawValue() throws {
91 | let v = try XCTUnwrap(Version(rawValue: "1.12"))
92 |
93 | XCTAssertEqual(v.rawValue, "1.12")
94 | }
95 | }
96 |
97 | #endif
98 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/Logic and Parsing/FinalCutPro FCPXML Structure.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FinalCutPro FCPXML Structure.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | #if os(macOS) // XMLNode only works on macOS
8 |
9 | import XCTest
10 | /* @testable */ import DAWFileKit
11 | import OTCore
12 | import TimecodeKit
13 |
14 | final class FinalCutPro_FCPXML_Structure: FCPXMLTestCase {
15 | override func setUp() { }
16 | override func tearDown() { }
17 |
18 | /// Ensure that elements that can appear in various locations in the XML hierarchy are all found.
19 | func testParse() throws {
20 | // load file
21 |
22 | let rawData = try XCTUnwrap(loadFileContents(
23 | forResource: "Structure",
24 | withExtension: "fcpxml",
25 | subFolder: .fcpxmlExports
26 | ))
27 |
28 | // load
29 |
30 | let fcpxml = try FinalCutPro.FCPXML(fileContent: rawData)
31 |
32 | // events
33 |
34 | let events = Set(fcpxml.allEvents().map(\.name))
35 | XCTAssertEqual(events, ["Test Event", "Test Event 2"])
36 |
37 | // projects
38 |
39 | let projects = Set(fcpxml.allProjects().map(\.name))
40 | XCTAssertEqual(projects, ["Test Project", "Test Project 2", "Test Project 3"])
41 |
42 | // TODO: it may be possible for story elements (sequence, clips, etc.) to be in the root `fcpxml` element
43 | // the docs say that they can be there as browser elements
44 | // test parsing them? might need a new method to get them specifically like `FinalCutPro.FCPXML.parseStoryElementsInRoot()`
45 | }
46 | }
47 |
48 | #endif
49 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/Resources/FCPXML Exports/AuditionMarkers.fcpxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Title
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | Title
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/Resources/FCPXML Exports/BasicMarkers.fcpxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Title
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/Resources/FCPXML Exports/BasicMarkers_1HourProjectStart.fcpxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Title
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/Resources/FCPXML Exports/Occlusion2.fcpxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/Resources/FCPXML Exports/StandaloneAssetClip.fcpxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Lavc59.37.100 libx264
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/Resources/FCPXML Exports/StandaloneRefClip.fcpxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | Lavc59.37.100 libx264
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/FinalCutPro/Resources/FCPXML Exports/Structure.fcpxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/ProTools SessionText EmptySession.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProTools SessionText EmptySession.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import XCTest
8 | @testable import DAWFileKit
9 | import OTCore
10 | import TimecodeKit
11 |
12 | class ProTools_SessionText_EmptySession: XCTestCase {
13 | override func setUp() { }
14 | override func tearDown() { }
15 |
16 | func testSessionText_EmptySession() throws {
17 | // load file
18 |
19 | let filename = "SessionText_EmptySession_23-976fps_DefaultExportOptions_PT2020.3"
20 | guard let rawData = loadFileContents(
21 | forResource: filename,
22 | withExtension: "txt",
23 | subFolder: .ptSessionTextExports
24 | )
25 | else { XCTFail("Could not form URL, possibly could not find file."); return }
26 |
27 | // parse
28 |
29 | var parseMessages: [ProTools.SessionInfo.ParseMessage] = []
30 | let sessionInfo = try ProTools.SessionInfo(
31 | fileContent: rawData,
32 | // no time values present in the file but supply a time format anyway to suppress the
33 | // format auto-detect error
34 | timeValueFormat: .timecode,
35 | messages: &parseMessages
36 | )
37 |
38 | // parse messages
39 |
40 | XCTAssertEqual(parseMessages.errors.count, 0)
41 | if !parseMessages.errors.isEmpty {
42 | dump(parseMessages.errors)
43 | }
44 |
45 | // main header
46 |
47 | XCTAssertEqual(sessionInfo.main.name, "SessionText_EmptySession")
48 | XCTAssertEqual(sessionInfo.main.sampleRate, 48000.0)
49 | XCTAssertEqual(sessionInfo.main.bitDepth, "24-bit")
50 | XCTAssertEqual(
51 | sessionInfo.main.startTimecode,
52 | try ProTools.formTimecode(.init(h: 0, m: 59, s: 55, f: 00), at: .fps23_976)
53 | )
54 | XCTAssertEqual(sessionInfo.main.frameRate, .fps23_976)
55 | XCTAssertEqual(sessionInfo.main.audioTrackCount, 0)
56 | XCTAssertEqual(sessionInfo.main.audioClipCount, 0)
57 | XCTAssertEqual(sessionInfo.main.audioFileCount, 0)
58 |
59 | // files - online
60 |
61 | XCTAssertEqual(sessionInfo.onlineFiles, [])
62 |
63 | // files - offline
64 |
65 | XCTAssertEqual(sessionInfo.offlineFiles, [])
66 |
67 | // clips - online
68 |
69 | XCTAssertNil(sessionInfo.onlineClips) // missing section
70 |
71 | // clips - offline
72 |
73 | XCTAssertNil(sessionInfo.offlineClips) // missing section
74 |
75 | // plug-ins
76 |
77 | XCTAssertEqual(sessionInfo.plugins, [])
78 |
79 | // tracks
80 |
81 | XCTAssertEqual(sessionInfo.tracks, [])
82 |
83 | // markers
84 |
85 | XCTAssertEqual(sessionInfo.markers, [])
86 |
87 | // orphan data
88 |
89 | XCTAssertNil(sessionInfo.orphanData) // none
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/ProTools SessionText OrphanData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProTools SessionText OrphanData.swift
3 | // DAWFileKit • https://github.com/orchetect/DAWFileKit
4 | // © 2022 Steffan Andrews • Licensed under MIT License
5 | //
6 |
7 | import XCTest
8 | @testable import DAWFileKit
9 | import OTCore
10 | import TimecodeKit
11 |
12 | class ProTools_SessionText_OrphanData: XCTestCase {
13 | override func setUp() { }
14 | override func tearDown() { }
15 |
16 | func testSessionText_OrphanData() throws {
17 | // load file
18 |
19 | let filename = "SessionText_UnrecognizedSection_23-976fps_DefaultExportOptions_PT2020.3"
20 | guard let rawData = loadFileContents(
21 | forResource: filename,
22 | withExtension: "txt",
23 | subFolder: .ptSessionTextExports
24 | )
25 | else { XCTFail("Could not form URL, possibly could not find file."); return }
26 |
27 | // parse
28 |
29 | var parseMessages: [ProTools.SessionInfo.ParseMessage] = []
30 | let sessionInfo = try ProTools.SessionInfo(fileContent: rawData, messages: &parseMessages)
31 |
32 | // parse messages
33 |
34 | XCTAssertEqual(parseMessages.errors.count, 0)
35 | if !parseMessages.errors.isEmpty {
36 | dump(parseMessages.errors)
37 | }
38 |
39 | // orphan data
40 | // just test for orphan sections (unrecognized - a hypothetical in case new sections get
41 | // added to Pro Tools in the future)
42 |
43 | XCTAssertEqual(sessionInfo.orphanData?.count, 1)
44 |
45 | XCTAssertEqual(
46 | sessionInfo.orphanData?.first?.heading,
47 | "U N R E C O G N I Z E D S E C T I O N"
48 | )
49 | XCTAssertEqual(sessionInfo.orphanData?.first?.content, [])
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_EmptySession_23-976fps_DefaultExportOptions_PT2020.3.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: SessionText_EmptySession
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 00:59:55:00
5 | TIMECODE FORMAT: 23.976 Frame
6 | # OF AUDIO TRACKS: 0
7 | # OF AUDIO CLIPS: 0
8 | # OF AUDIO FILES: 0
9 |
10 |
11 | O N L I N E F I L E S I N S E S S I O N
12 | Filename Location
13 |
14 |
15 | O F F L I N E F I L E S I N S E S S I O N
16 | Filename Location
17 |
18 |
19 | P L U G - I N S L I S T I N G
20 | MANUFACTURER PLUG-IN NAME VERSION FORMAT STEMS NUMBER OF INSTANCES
21 |
22 |
23 | T R A C K L I S T I N G
24 | M A R K E R S L I S T I N G
25 | # LOCATION TIME REFERENCE UNITS NAME COMMENTS
26 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_ExtendedChars_TextEditFormat_PT2023.3.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orchetect/DAWFileKit/e71576a436405e7a20650a3dd1be3f7f3d8fbb4d/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_ExtendedChars_TextEditFormat_PT2023.3.txt
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_ExtendedChars_UTF8Format_PT2023.3.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: SessionText_UTF8Chars
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 00:59:50:00
5 | TIMECODE FORMAT: 24 Frame
6 | # OF AUDIO TRACKS: 0
7 | # OF AUDIO CLIPS: 0
8 | # OF AUDIO FILES: 0
9 |
10 |
11 | O N L I N E F I L E S I N S E S S I O N
12 | Filename Location
13 |
14 |
15 | O F F L I N E F I L E S I N S E S S I O N
16 | Filename Location
17 |
18 |
19 | P L U G - I N S L I S T I N G
20 | MANUFACTURER PLUG-IN NAME VERSION FORMAT STEMS NUMBER OF INSTANCES
21 |
22 |
23 | T R A C K L I S T I N G
24 | M A R K E R S L I S T I N G
25 | # LOCATION TIME REFERENCE UNITS NAME COMMENTS
26 | 1 01:00:00:00 480000 Samples Test Ellipsis…
27 | 2 01:01:00:00 3360000 Samples Test Em Dash —
28 | 3 01:02:00:00 6240000 Samples Test En Dash –
29 | 4 01:03:00:00 9120000 Samples Right Side Quote’s Not An Apostrophe
30 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_FPPFinal_23-976fps_DefaultExportOptions_PT2020.3.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orchetect/DAWFileKit/e71576a436405e7a20650a3dd1be3f7f3d8fbb4d/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_FPPFinal_23-976fps_DefaultExportOptions_PT2020.3.txt
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_MarkerRulersAndTrackMarkers_PT2023.12.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: Test
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 00:59:50:00
5 | TIMECODE FORMAT: 24 Frame
6 | # OF AUDIO TRACKS: 2
7 | # OF AUDIO CLIPS: 0
8 | # OF AUDIO FILES: 0
9 |
10 |
11 | M A R K E R S L I S T I N G
12 | # LOCATION TIME REFERENCE UNITS NAME TRACK NAME TRACK TYPE COMMENTS
13 | 1 01:00:00:00 480000 Samples Marker 1 Markers Ruler
14 | 2 01:00:01:00 528000 Samples Marker 2 Markers 2 Ruler Some comments
15 | 3 01:00:02:00 576000 Samples Marker 3 Markers 3 Ruler
16 | 4 01:00:03:00 624000 Samples Marker 4 Markers 4 Ruler
17 | 5 01:00:04:00 672000 Samples Marker 5 Markers 5 Ruler
18 | 6 01:00:05:00 720000 Samples Marker 6 Audio 1 Track More comments
19 | 7 01:00:06:00 768000 Samples Marker 7 Audio 2 Track
20 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_NewLinesAndTabs_DefaultExportOptions_PT2023.6.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: SessionText_NewLinesAndTabs
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 00:59:55:00
5 | TIMECODE FORMAT: 23.976 Frame
6 | # OF AUDIO TRACKS: 0
7 | # OF AUDIO CLIPS: 0
8 | # OF AUDIO FILES: 0
9 |
10 |
11 | O N L I N E F I L E S I N S E S S I O N
12 | Filename Location
13 |
14 |
15 | O F F L I N E F I L E S I N S E S S I O N
16 | Filename Location
17 |
18 |
19 | P L U G - I N S L I S T I N G
20 | MANUFACTURER PLUG-IN NAME VERSION FORMAT STEMS NUMBER OF INSTANCES
21 |
22 |
23 | T R A C K L I S T I N G
24 | M A R K E R S L I S T I N G
25 | # LOCATION TIME REFERENCE UNITS NAME COMMENTS
26 | 1 01:00:00:00 240240 Samples Marker Name
27 | With New Line
28 | 2 01:00:01:00 288288 Samples Normal Marker Name Comment Here
29 | With New Line
30 | 3 01:00:02:00 336336 Samples Marker Name Again
31 | With New Line Again Comment Here Again
32 | With New Line Again
33 | 4 01:00:03:00 384384 Samples Normal Marker Name Again
34 | 5 01:00:04:00 432432 Samples Marker Name With Tab
35 | 6 01:00:05:00 480480 Samples Normal Marker Name Comments Here With Tab
36 | 7 01:00:06:00 528528 Samples Marker Name With Tab Comments Here With Tab
37 | 8 01:00:07:00 576576 Samples Marker Name With Tab And Another Tab
38 | 9 01:00:08:00 624624 Samples Normal Marker Name Comment Here With Tab And Another Tab
39 | 10 01:00:09:00 672672 Samples Marker Name With Tab And Another Tab Comment Here With Tab And Another Tab
40 | 11 01:00:10:00 720720 Samples Marker Name With Tab
41 | And Newline
42 | 12 01:00:11:00 768768 Samples Normal Marker Name Comment Here With Tab
43 | And Newline
44 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_Plugins_23-976fps_DefaultExportOptions_PT2020.3.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orchetect/DAWFileKit/e71576a436405e7a20650a3dd1be3f7f3d8fbb4d/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_Plugins_23-976fps_DefaultExportOptions_PT2020.3.txt
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_SimpleTest_23-976fps_DefaultExportOptions_PT2020.3.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: SessionText_SimpleTest
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 00:59:55:00
5 | TIMECODE FORMAT: 23.976 Frame
6 | # OF AUDIO TRACKS: 1
7 | # OF AUDIO CLIPS: 1
8 | # OF AUDIO FILES: 1
9 |
10 |
11 | O N L I N E F I L E S I N S E S S I O N
12 | Filename Location
13 | Audio 1_01.wav Macintosh HD:Users:stef:Desktop:SessionText_SimpleTest:Audio Files:
14 |
15 |
16 | O F F L I N E F I L E S I N S E S S I O N
17 | Filename Location
18 |
19 |
20 | O N L I N E C L I P S I N S E S S I O N
21 | CLIP NAME Source File
22 | Audio 1_01 Audio 1_01.wav
23 |
24 |
25 | P L U G - I N S L I S T I N G
26 | MANUFACTURER PLUG-IN NAME VERSION FORMAT STEMS NUMBER OF INSTANCES
27 |
28 |
29 | T R A C K L I S T I N G
30 | TRACK NAME: Audio 1
31 | COMMENTS:
32 | USER DELAY: 0 Samples
33 | STATE:
34 | PLUG-INS:
35 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
36 | 1 1 Audio 1_01 01:00:00:00 01:00:05:00 00:00:05:00 Unmuted
37 |
38 |
39 | M A R K E R S L I S T I N G
40 | # LOCATION TIME REFERENCE UNITS NAME COMMENTS
41 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_TimeFormats_BarsBeats_PT2022.9.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: Test
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 23:57:00:00
5 | TIMECODE FORMAT: 23.976 Frame
6 | # OF AUDIO TRACKS: 2
7 | # OF AUDIO CLIPS: 0
8 | # OF AUDIO FILES: 0
9 |
10 |
11 | O N L I N E F I L E S I N S E S S I O N
12 | Filename Location
13 |
14 |
15 | O F F L I N E F I L E S I N S E S S I O N
16 | Filename Location
17 |
18 |
19 | P L U G - I N S L I S T I N G
20 | MANUFACTURER PLUG-IN NAME VERSION FORMAT STEMS NUMBER OF INSTANCES
21 |
22 |
23 | T R A C K L I S T I N G
24 | TRACK NAME: Audio A
25 | COMMENTS:
26 | USER DELAY: 0 Samples
27 | STATE:
28 | PLUG-INS:
29 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
30 | 1 1 Audio Clip 1 Name 13|3 20|1 6|2| 720 Unmuted
31 |
32 |
33 | TRACK NAME: Audio B
34 | COMMENTS:
35 | USER DELAY: 0 Samples
36 | STATE:
37 | PLUG-INS:
38 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
39 | 1 1 Audio Clip 2 Name 5|1 11|4 6|2| 720 Unmuted
40 |
41 |
42 | M A R K E R S L I S T I N G
43 | # LOCATION TIME REFERENCE UNITS NAME COMMENTS
44 | 1 29|1 2695168 Samples Marker Absolute Comment
45 | 2 58|4 58|4 Ticks Marker Bars-Beats Comment
46 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_TimeFormats_BarsBeats_ShowSubframes_PT2022.9.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: Test
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 23:57:00:00.00
5 | TIMECODE FORMAT: 23.976 Frame
6 | # OF AUDIO TRACKS: 2
7 | # OF AUDIO CLIPS: 0
8 | # OF AUDIO FILES: 0
9 |
10 |
11 | O N L I N E F I L E S I N S E S S I O N
12 | Filename Location
13 |
14 |
15 | O F F L I N E F I L E S I N S E S S I O N
16 | Filename Location
17 |
18 |
19 | P L U G - I N S L I S T I N G
20 | MANUFACTURER PLUG-IN NAME VERSION FORMAT STEMS NUMBER OF INSTANCES
21 |
22 |
23 | T R A C K L I S T I N G
24 | TRACK NAME: Audio A
25 | COMMENTS:
26 | USER DELAY: 0 Samples
27 | STATE:
28 | PLUG-INS:
29 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
30 | 1 1 Audio Clip 1 Name 13|3| 048 20|1| 768 6|2| 720 Unmuted
31 |
32 |
33 | TRACK NAME: Audio B
34 | COMMENTS:
35 | USER DELAY: 0 Samples
36 | STATE:
37 | PLUG-INS:
38 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
39 | 1 1 Audio Clip 2 Name 5|1| 696 11|4| 456 6|2| 720 Unmuted
40 |
41 |
42 | M A R K E R S L I S T I N G
43 | # LOCATION TIME REFERENCE UNITS NAME COMMENTS
44 | 1 29|1| 287 2695168 Samples Marker Absolute Comment
45 | 2 58|4| 735 58|4| 735 Ticks Marker Bars-Beats Comment
46 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_TimeFormats_FeetFrames_PT2022.9.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: Test
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 23:57:00:00
5 | TIMECODE FORMAT: 23.976 Frame
6 | # OF AUDIO TRACKS: 2
7 | # OF AUDIO CLIPS: 0
8 | # OF AUDIO FILES: 0
9 |
10 |
11 | O N L I N E F I L E S I N S E S S I O N
12 | Filename Location
13 |
14 |
15 | O F F L I N E F I L E S I N S E S S I O N
16 | Filename Location
17 |
18 |
19 | P L U G - I N S L I S T I N G
20 | MANUFACTURER PLUG-IN NAME VERSION FORMAT STEMS NUMBER OF INSTANCES
21 |
22 |
23 | T R A C K L I S T I N G
24 | TRACK NAME: Audio A
25 | COMMENTS:
26 | USER DELAY: 0 Samples
27 | STATE:
28 | PLUG-INS:
29 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
30 | 1 1 Audio Clip 1 Name 37+08 57+09 20+01 Unmuted
31 |
32 |
33 | TRACK NAME: Audio B
34 | COMMENTS:
35 | USER DELAY: 0 Samples
36 | STATE:
37 | PLUG-INS:
38 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
39 | 1 1 Audio Clip 2 Name 12+08 32+09 20+01 Unmuted
40 |
41 |
42 | M A R K E R S L I S T I N G
43 | # LOCATION TIME REFERENCE UNITS NAME COMMENTS
44 | 1 84+03 2695168 Samples Marker Absolute Comment
45 | 2 173+13 58|4 Ticks Marker Bars-Beats Comment
46 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_TimeFormats_FeetFrames_ShowSubframes_PT2022.9.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: Test
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 23:57:00:00.00
5 | TIMECODE FORMAT: 23.976 Frame
6 | # OF AUDIO TRACKS: 2
7 | # OF AUDIO CLIPS: 0
8 | # OF AUDIO FILES: 0
9 |
10 |
11 | O N L I N E F I L E S I N S E S S I O N
12 | Filename Location
13 |
14 |
15 | O F F L I N E F I L E S I N S E S S I O N
16 | Filename Location
17 |
18 |
19 | P L U G - I N S L I S T I N G
20 | MANUFACTURER PLUG-IN NAME VERSION FORMAT STEMS NUMBER OF INSTANCES
21 |
22 |
23 | T R A C K L I S T I N G
24 | TRACK NAME: Audio A
25 | COMMENTS:
26 | USER DELAY: 0 Samples
27 | STATE:
28 | PLUG-INS:
29 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
30 | 1 1 Audio Clip 1 Name 37+08.60 57+09.60 20+01.00 Unmuted
31 |
32 |
33 | TRACK NAME: Audio B
34 | COMMENTS:
35 | USER DELAY: 0 Samples
36 | STATE:
37 | PLUG-INS:
38 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
39 | 1 1 Audio Clip 2 Name 12+08.70 32+09.70 20+01.00 Unmuted
40 |
41 |
42 | M A R K E R S L I S T I N G
43 | # LOCATION TIME REFERENCE UNITS NAME COMMENTS
44 | 1 84+03.58 2695168 Samples Marker Absolute Comment
45 | 2 173+13.18 58|4| 735 Ticks Marker Bars-Beats Comment
46 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_TimeFormats_MinSecs_PT2022.9.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: Test
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 23:57:00:00
5 | TIMECODE FORMAT: 23.976 Frame
6 | # OF AUDIO TRACKS: 2
7 | # OF AUDIO CLIPS: 0
8 | # OF AUDIO FILES: 0
9 |
10 |
11 | O N L I N E F I L E S I N S E S S I O N
12 | Filename Location
13 |
14 |
15 | O F F L I N E F I L E S I N S E S S I O N
16 | Filename Location
17 |
18 |
19 | P L U G - I N S L I S T I N G
20 | MANUFACTURER PLUG-IN NAME VERSION FORMAT STEMS NUMBER OF INSTANCES
21 |
22 |
23 | T R A C K L I S T I N G
24 | TRACK NAME: Audio A
25 | COMMENTS:
26 | USER DELAY: 0 Samples
27 | STATE:
28 | PLUG-INS:
29 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
30 | 1 1 Audio Clip 1 Name 0:25 0:38 0:13 Unmuted
31 |
32 |
33 | TRACK NAME: Audio B
34 | COMMENTS:
35 | USER DELAY: 0 Samples
36 | STATE:
37 | PLUG-INS:
38 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
39 | 1 1 Audio Clip 2 Name 0:08 0:21 0:13 Unmuted
40 |
41 |
42 | M A R K E R S L I S T I N G
43 | # LOCATION TIME REFERENCE UNITS NAME COMMENTS
44 | 1 0:56 2695168 Samples Marker Absolute Comment
45 | 2 1:55 58|4 Ticks Marker Bars-Beats Comment
46 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_TimeFormats_MinSecs_ShowSubframes_PT2022.9.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: Test
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 23:57:00:00.00
5 | TIMECODE FORMAT: 23.976 Frame
6 | # OF AUDIO TRACKS: 2
7 | # OF AUDIO CLIPS: 0
8 | # OF AUDIO FILES: 0
9 |
10 |
11 | O N L I N E F I L E S I N S E S S I O N
12 | Filename Location
13 |
14 |
15 | O F F L I N E F I L E S I N S E S S I O N
16 | Filename Location
17 |
18 |
19 | P L U G - I N S L I S T I N G
20 | MANUFACTURER PLUG-IN NAME VERSION FORMAT STEMS NUMBER OF INSTANCES
21 |
22 |
23 | T R A C K L I S T I N G
24 | TRACK NAME: Audio A
25 | COMMENTS:
26 | USER DELAY: 0 Samples
27 | STATE:
28 | PLUG-INS:
29 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
30 | 1 1 Audio Clip 1 Name 0:25.025 0:38.400 0:13.375 Unmuted
31 |
32 |
33 | TRACK NAME: Audio B
34 | COMMENTS:
35 | USER DELAY: 0 Samples
36 | STATE:
37 | PLUG-INS:
38 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
39 | 1 1 Audio Clip 2 Name 0:08.362 0:21.737 0:13.375 Unmuted
40 |
41 |
42 | M A R K E R S L I S T I N G
43 | # LOCATION TIME REFERENCE UNITS NAME COMMENTS
44 | 1 0:56.149 2695168 Samples Marker Absolute Comment
45 | 2 1:55.882 58|4| 735 Ticks Marker Bars-Beats Comment
46 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_TimeFormats_Samples_PT2022.9.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: Test
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 23:57:00:00
5 | TIMECODE FORMAT: 23.976 Frame
6 | # OF AUDIO TRACKS: 2
7 | # OF AUDIO CLIPS: 0
8 | # OF AUDIO FILES: 0
9 |
10 |
11 | O N L I N E F I L E S I N S E S S I O N
12 | Filename Location
13 |
14 |
15 | O F F L I N E F I L E S I N S E S S I O N
16 | Filename Location
17 |
18 |
19 | P L U G - I N S L I S T I N G
20 | MANUFACTURER PLUG-IN NAME VERSION FORMAT STEMS NUMBER OF INSTANCES
21 |
22 |
23 | T R A C K L I S T I N G
24 | TRACK NAME: Audio A
25 | COMMENTS:
26 | USER DELAY: 0 Samples
27 | STATE:
28 | PLUG-INS:
29 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
30 | 1 1 Audio Clip 1 Name 1201200 1843200 642000 Unmuted
31 |
32 |
33 | TRACK NAME: Audio B
34 | COMMENTS:
35 | USER DELAY: 0 Samples
36 | STATE:
37 | PLUG-INS:
38 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
39 | 1 1 Audio Clip 2 Name 401408 1043408 642000 Unmuted
40 |
41 |
42 | M A R K E R S L I S T I N G
43 | # LOCATION TIME REFERENCE UNITS NAME COMMENTS
44 | 1 2695168 2695168 Samples Marker Absolute Comment
45 | 2 5562368 58|4 Ticks Marker Bars-Beats Comment
46 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_TimeFormats_Samples_ShowSubframes_PT2022.9.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: Test
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 23:57:00:00.00
5 | TIMECODE FORMAT: 23.976 Frame
6 | # OF AUDIO TRACKS: 2
7 | # OF AUDIO CLIPS: 0
8 | # OF AUDIO FILES: 0
9 |
10 |
11 | O N L I N E F I L E S I N S E S S I O N
12 | Filename Location
13 |
14 |
15 | O F F L I N E F I L E S I N S E S S I O N
16 | Filename Location
17 |
18 |
19 | P L U G - I N S L I S T I N G
20 | MANUFACTURER PLUG-IN NAME VERSION FORMAT STEMS NUMBER OF INSTANCES
21 |
22 |
23 | T R A C K L I S T I N G
24 | TRACK NAME: Audio A
25 | COMMENTS:
26 | USER DELAY: 0 Samples
27 | STATE:
28 | PLUG-INS:
29 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
30 | 1 1 Audio Clip 1 Name 1201200 1843200 642000 Unmuted
31 |
32 |
33 | TRACK NAME: Audio B
34 | COMMENTS:
35 | USER DELAY: 0 Samples
36 | STATE:
37 | PLUG-INS:
38 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
39 | 1 1 Audio Clip 2 Name 401408 1043408 642000 Unmuted
40 |
41 |
42 | M A R K E R S L I S T I N G
43 | # LOCATION TIME REFERENCE UNITS NAME COMMENTS
44 | 1 2695168 2695168 Samples Marker Absolute Comment
45 | 2 5562368 58|4| 735 Ticks Marker Bars-Beats Comment
46 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_TimeFormats_Timecode_PT2022.9.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: Test
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 23:57:00:00
5 | TIMECODE FORMAT: 23.976 Frame
6 | # OF AUDIO TRACKS: 2
7 | # OF AUDIO CLIPS: 0
8 | # OF AUDIO FILES: 0
9 |
10 |
11 | O N L I N E F I L E S I N S E S S I O N
12 | Filename Location
13 |
14 |
15 | O F F L I N E F I L E S I N S E S S I O N
16 | Filename Location
17 |
18 |
19 | P L U G - I N S L I S T I N G
20 | MANUFACTURER PLUG-IN NAME VERSION FORMAT STEMS NUMBER OF INSTANCES
21 |
22 |
23 | T R A C K L I S T I N G
24 | TRACK NAME: Audio A
25 | COMMENTS:
26 | USER DELAY: 0 Samples
27 | STATE:
28 | PLUG-INS:
29 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
30 | 1 1 Audio Clip 1 Name 23:57:25:00 23:57:38:08 00:00:13:08 Unmuted
31 |
32 |
33 | TRACK NAME: Audio B
34 | COMMENTS:
35 | USER DELAY: 0 Samples
36 | STATE:
37 | PLUG-INS:
38 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
39 | 1 1 Audio Clip 2 Name 23:57:08:08 23:57:21:17 00:00:13:08 Unmuted
40 |
41 |
42 | M A R K E R S L I S T I N G
43 | # LOCATION TIME REFERENCE UNITS NAME COMMENTS
44 | 1 23:57:56:02 2695168 Samples Marker Absolute Comment
45 | 2 23:58:55:18 58|4 Ticks Marker Bars-Beats Comment
46 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_TimeFormats_Timecode_ShowSubframes_PT2022.9.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: Test
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 23:57:00:00.00
5 | TIMECODE FORMAT: 23.976 Frame
6 | # OF AUDIO TRACKS: 2
7 | # OF AUDIO CLIPS: 0
8 | # OF AUDIO FILES: 0
9 |
10 |
11 | O N L I N E F I L E S I N S E S S I O N
12 | Filename Location
13 |
14 |
15 | O F F L I N E F I L E S I N S E S S I O N
16 | Filename Location
17 |
18 |
19 | P L U G - I N S L I S T I N G
20 | MANUFACTURER PLUG-IN NAME VERSION FORMAT STEMS NUMBER OF INSTANCES
21 |
22 |
23 | T R A C K L I S T I N G
24 | TRACK NAME: Audio A
25 | COMMENTS:
26 | USER DELAY: 0 Samples
27 | STATE:
28 | PLUG-INS:
29 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
30 | 1 1 Audio Clip 1 Name 23:57:25:00.00 23:57:38:08.68 00:00:13:08.68 Unmuted
31 |
32 |
33 | TRACK NAME: Audio B
34 | COMMENTS:
35 | USER DELAY: 0 Samples
36 | STATE:
37 | PLUG-INS:
38 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
39 | 1 1 Audio Clip 2 Name 23:57:08:08.50 23:57:21:17.18 00:00:13:08.68 Unmuted
40 |
41 |
42 | M A R K E R S L I S T I N G
43 | # LOCATION TIME REFERENCE UNITS NAME COMMENTS
44 | 1 23:57:56:02.24 2695168 Samples Marker Absolute Comment
45 | 2 23:58:55:18.41 58|4| 735 Ticks Marker Bars-Beats Comment
46 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_TracksOnly_OnlyTrackEDLs_PT2023.6.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: SessionText_TracksOnly
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 00:59:50:00
5 | TIMECODE FORMAT: 24 Frame
6 | # OF AUDIO TRACKS: 163
7 | # OF AUDIO CLIPS: 1541
8 | # OF AUDIO FILES: 247
9 |
10 |
11 | T R A C K L I S T I N G
12 | TRACK NAME: Audio 1
13 | COMMENTS:
14 | USER DELAY: 0 Samples
15 | STATE:
16 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
17 | 1 1 Warm Day in the City 01:00:15:06 01:01:05:13 00:00:50:07 Unmuted
18 | 1 2 Happy Go Lucky 01:01:05:13 01:01:57:23 00:00:52:09 Unmuted
19 |
20 |
21 | TRACK NAME: Audio 2
22 | COMMENTS:
23 | USER DELAY: 0 Samples
24 | STATE:
25 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
26 |
27 |
28 | TRACK NAME: Audio 3
29 | COMMENTS:
30 | USER DELAY: 0 Samples
31 | STATE:
32 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
33 |
34 |
35 | TRACK NAME: Audio 4
36 | COMMENTS:
37 | USER DELAY: 0 Samples
38 | STATE:
39 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
40 |
41 |
42 | TRACK NAME: Audio 5
43 | COMMENTS:
44 | USER DELAY: 0 Samples
45 | STATE:
46 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
47 |
48 |
49 | TRACK NAME: Audio 6
50 | COMMENTS:
51 | USER DELAY: 0 Samples
52 | STATE:
53 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
54 |
55 |
56 | TRACK NAME: Audio 7
57 | COMMENTS:
58 | USER DELAY: 0 Samples
59 | STATE:
60 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
61 |
62 |
63 |
--------------------------------------------------------------------------------
/Tests/DAWFileKitTests/ProTools/Resources/PT Session Text Exports/SessionText_UnrecognizedSection_23-976fps_DefaultExportOptions_PT2020.3.txt:
--------------------------------------------------------------------------------
1 | SESSION NAME: SessionText_SimpleTest
2 | SAMPLE RATE: 48000.000000
3 | BIT DEPTH: 24-bit
4 | SESSION START TIMECODE: 00:59:55:00
5 | TIMECODE FORMAT: 23.976 Frame
6 | # OF AUDIO TRACKS: 1
7 | # OF AUDIO CLIPS: 1
8 | # OF AUDIO FILES: 1
9 |
10 |
11 | O N L I N E F I L E S I N S E S S I O N
12 | Filename Location
13 | Audio 1_01.wav Macintosh HD:Users:stef:Desktop:SessionText_SimpleTest:Audio Files:
14 |
15 |
16 | O F F L I N E F I L E S I N S E S S I O N
17 | Filename Location
18 |
19 |
20 | O N L I N E C L I P S I N S E S S I O N
21 | CLIP NAME Source File
22 | Audio 1_01 Audio 1_01.wav
23 |
24 |
25 | P L U G - I N S L I S T I N G
26 | MANUFACTURER PLUG-IN NAME VERSION FORMAT STEMS NUMBER OF INSTANCES
27 |
28 |
29 | U N R E C O G N I Z E D S E C T I O N
30 |
31 |
32 | T R A C K L I S T I N G
33 | TRACK NAME: Audio 1
34 | COMMENTS:
35 | USER DELAY: 0 Samples
36 | STATE:
37 | PLUG-INS:
38 | CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
39 | 1 1 Audio 1_01 01:00:00:00 01:00:05:00 00:00:05:00 Unmuted
40 |
41 |
42 | M A R K E R S L I S T I N G
43 | # LOCATION TIME REFERENCE UNITS NAME COMMENTS
44 |
--------------------------------------------------------------------------------