-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
161 additions
and
132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
140 changes: 140 additions & 0 deletions
140
Sources/DAWFileKit/ProTools/SessionInfo/SessionInfo TimeValueFormat.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
// | ||
// SessionInfo TimeValue.swift | ||
// DAWFileKit • https://github.com/orchetect/DAWFileKit | ||
// © 2022 Steffan Andrews • Licensed under MIT License | ||
// | ||
|
||
import Foundation | ||
|
||
extension ProTools.SessionInfo { | ||
public enum TimeValueFormat: Equatable, Hashable, CaseIterable { | ||
/// Timecode at the project frame rate. | ||
/// | ||
/// Pro Tools always uses a subframe base of 100 subframes per frame. | ||
case timecode | ||
|
||
/// Min:Secs time format. | ||
/// This can either be `MM:SS` or `MM:SS.sss` (where `sss` is milliseconds). | ||
case minSecs | ||
|
||
/// Elapsed audio samples since the project start. | ||
/// | ||
/// Refer to ``ProTools/SessionInfo/main-swift.property``.``ProTools/SessionInfo/Main-swift.struct/sampleRate`` | ||
/// for project sample rate. | ||
case samples | ||
|
||
/// Bars and Beats (musical). | ||
/// Ticks (quarter note division) is only present when the _Show Subframes_ option is | ||
/// enabled in Pro Tools' Export Session Text window while exporting. Pro Tools uses a PPQ | ||
/// base of 960 ticks per quarter. | ||
case barsAndBeats | ||
|
||
/// Feet and Frames. | ||
/// | ||
/// This can either be `FT:FR` or `FT:FR.sf` (where `sf` is subframes). | ||
/// | ||
/// SubFrames is only present when the _Show Subframes_ option is enabled in Pro Tools' | ||
/// Export Session Text window while exporting. Pro Tools uses a PPQ base of 960 ticks per | ||
/// quarter. | ||
case feetAndFrames | ||
} | ||
} | ||
|
||
extension ProTools.SessionInfo.TimeValueFormat: Identifiable { | ||
public var id: Self { self } | ||
} | ||
|
||
extension ProTools.SessionInfo.TimeValueFormat: Sendable { } | ||
|
||
extension ProTools.SessionInfo.TimeValueFormat: CustomStringConvertible { | ||
public var description: String { | ||
name | ||
} | ||
} | ||
|
||
extension ProTools.SessionInfo.TimeValueFormat { | ||
/// Returns human-readable name of the time value format type suitable for UI or debugging. | ||
public var name: String { | ||
switch self { | ||
case .timecode: return "Timecode" | ||
case .minSecs: return "Min:Secs" | ||
case .samples: return "Samples" | ||
case .barsAndBeats: return "Bars|Beats" | ||
case .feetAndFrames: return "Feet+Frames" | ||
} | ||
} | ||
} | ||
|
||
// MARK: - Internal Methods | ||
|
||
extension ProTools.SessionInfo.TimeValueFormat { | ||
/// Employs a format detection heuristic to attempt to determine the time format of the given | ||
/// time string. | ||
/// This does not perform exhaustive validation on the values themselves, but matches against | ||
/// expected formatting. | ||
/// Returns `nil` if no matches can be ascertained. | ||
init(heuristic source: String) throws { | ||
// as a performance optimization, the formats here | ||
// are ordered from most common to least common | ||
|
||
if Self.isTimecode(source) { | ||
self = .timecode | ||
return | ||
} | ||
if Self.isMinSecs(source) { | ||
self = .minSecs | ||
return | ||
} | ||
if Self.isBarsAndBeats(source) { | ||
self = .barsAndBeats | ||
return | ||
} | ||
if Self.isSamples(source) { | ||
self = .samples | ||
return | ||
} | ||
if Self.isFeetAndFrames(source) { | ||
self = .feetAndFrames | ||
return | ||
} | ||
|
||
throw ProTools.SessionInfo.ParseError.general( | ||
"Not a valid time value." | ||
) | ||
} | ||
|
||
private static func isTimecode( | ||
_ source: String | ||
) -> Bool { | ||
let regExPattern = #"^\d{2}:\d{2}:\d{2}[:|;]\d{2}(.\d{2}){0,1}$"# | ||
return source.regexMatches(pattern: regExPattern).count == 1 | ||
} | ||
|
||
private static func isMinSecs( | ||
_ source: String | ||
) -> Bool { | ||
let regExPattern = #"^(\d+):(\d{2})(.\d{3}){0,1}$"# | ||
return source.regexMatches(pattern: regExPattern).count == 1 | ||
} | ||
|
||
private static func isSamples( | ||
_ source: String | ||
) -> Bool { | ||
let regExPattern = #"^\d+$"# | ||
return source.regexMatches(pattern: regExPattern).count == 1 | ||
} | ||
|
||
private static func isBarsAndBeats( | ||
_ source: String | ||
) -> Bool { | ||
let regExPattern = #"^\d+\|\d+(\|[\s\d]{1}\d{3}){0,1}$"# | ||
return source.regexMatches(pattern: regExPattern).count == 1 | ||
} | ||
|
||
private static func isFeetAndFrames( | ||
_ source: String | ||
) -> Bool { | ||
let regExPattern = #"^(\d+)\+(\d{2})(.\d{2}){0,1}$"# | ||
return source.regexMatches(pattern: regExPattern).count == 1 | ||
} | ||
} |