Skip to content

Commit 5650a9f

Browse files
authored
Merge pull request #44 from orchetect/dev
Added `FrameRate(raw:favorDropFrame:)` init
2 parents b638496 + a24668c commit 5650a9f

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//
2+
// FrameRate Conversions.swift
3+
// TimecodeKit • https://github.com/orchetect/TimecodeKit
4+
//
5+
6+
import Foundation
7+
@_implementationOnly import OTCore
8+
9+
extension Timecode.FrameRate {
10+
11+
/// Initialize from raw floating-point frame rate (fps).
12+
///
13+
/// A floating-point fps value can't always explicitly determine a BITC frame rate. Drop frame information cannot be conveyed. For example, 29.97002997 is ambiguous as it is valid for both 29.97 non-drop, 29.97 drop, and 30 drop. For this purpose, the `favorDropFrame` parameter will return the drop-frame variant if the raw rate matches more than one rate.
14+
///
15+
/// `nil` is returned if rate does not match any of the supported rates. (Rate is non-standard or variable (VFR))
16+
@_disfavoredOverload
17+
public init?(raw: Float,
18+
favorDropFrame: Bool = false) {
19+
20+
self.init(raw: Double(raw),
21+
favorDropFrame: favorDropFrame)
22+
23+
}
24+
25+
/// Initialize from raw floating-point frame rate (fps).
26+
///
27+
/// A floating-point fps value can't always explicitly determine a BITC frame rate. Drop frame information cannot be conveyed. For example, 29.97002997 is ambiguous as it is valid for both 29.97 non-drop, 29.97 drop, and 30 drop. For this purpose, the `favorDropFrame` parameter will return the drop-frame variant if the raw rate matches more than one rate.
28+
///
29+
/// `nil` is returned if rate does not match any of the supported rates. (Rate is non-standard or variable (VFR))
30+
public init?(raw: Double,
31+
favorDropFrame: Bool = false) {
32+
33+
let findMatches = Self.allCases.filter {
34+
$0.frameRateForRealTimeCalculation.truncated(decimalPlaces: 3)
35+
== raw.truncated(decimalPlaces: 3)
36+
}
37+
38+
// in cases where it's not clear which frame rate it is,
39+
// there may be more than one match
40+
if favorDropFrame,
41+
findMatches.count > 1,
42+
let firstDrop = findMatches.first(where: { $0.isDrop })
43+
{
44+
self = firstDrop
45+
return
46+
}
47+
48+
if let firstMatch = findMatches.first {
49+
self = firstMatch
50+
return
51+
}
52+
53+
return nil
54+
55+
}
56+
57+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//
2+
// FrameRate Conversions Tests.swift
3+
// TimecodeKit • https://github.com/orchetect/TimecodeKit
4+
//
5+
6+
#if !os(watchOS)
7+
8+
import XCTest
9+
@testable import TimecodeKit
10+
import OTCore
11+
12+
class Timecode_UT_FrameRate_Conversions_Tests: XCTestCase {
13+
14+
override func setUp() { }
15+
override func tearDown() { }
16+
17+
func testInit_raw() {
18+
19+
XCTAssertEqual(Timecode.FrameRate(raw: 23.976), ._23_976)
20+
XCTAssertEqual(Timecode.FrameRate(raw: 23.976023976), ._23_976)
21+
22+
XCTAssertEqual(Timecode.FrameRate(raw: 24), ._24)
23+
24+
// raw 24.98 fps won't match "24.98" because it's not close enough to correct rate
25+
XCTAssertNil(Timecode.FrameRate(raw: 24.98))
26+
XCTAssertEqual(Timecode.FrameRate(raw: 24.975), ._24_98)
27+
XCTAssertEqual(Timecode.FrameRate(raw: 24.975024975), ._24_98)
28+
29+
XCTAssertEqual(Timecode.FrameRate(raw: 25), ._25)
30+
31+
XCTAssertEqual(Timecode.FrameRate(raw: 29.97), ._29_97)
32+
XCTAssertEqual(Timecode.FrameRate(raw: 29.97, favorDropFrame: true), ._29_97_drop)
33+
XCTAssertEqual(Timecode.FrameRate(raw: 29.97002997), ._29_97)
34+
XCTAssertEqual(Timecode.FrameRate(raw: 29.97002997, favorDropFrame: true), ._29_97_drop)
35+
36+
XCTAssertEqual(Timecode.FrameRate(raw: 30), ._30)
37+
// raw 30 fps is not correct for 30d; matches 30 instead
38+
XCTAssertEqual(Timecode.FrameRate(raw: 30, favorDropFrame: true), ._30)
39+
40+
XCTAssertEqual(Timecode.FrameRate(raw: 47.952), ._47_952)
41+
XCTAssertEqual(Timecode.FrameRate(raw: 47.952047952), ._47_952)
42+
43+
XCTAssertEqual(Timecode.FrameRate(raw: 50), ._50)
44+
45+
XCTAssertEqual(Timecode.FrameRate(raw: 59.94), ._59_94)
46+
XCTAssertEqual(Timecode.FrameRate(raw: 59.94, favorDropFrame: true), ._59_94_drop)
47+
XCTAssertEqual(Timecode.FrameRate(raw: 59.9400599401), ._59_94)
48+
XCTAssertEqual(Timecode.FrameRate(raw: 59.9400599401, favorDropFrame: true), ._59_94_drop)
49+
50+
XCTAssertEqual(Timecode.FrameRate(raw: 60), ._60)
51+
// raw 60 fps is not correct for 60d; matches 60 instead
52+
XCTAssertEqual(Timecode.FrameRate(raw: 60, favorDropFrame: true), ._60)
53+
54+
XCTAssertEqual(Timecode.FrameRate(raw: 100), ._100)
55+
56+
XCTAssertEqual(Timecode.FrameRate(raw: 119.88), ._119_88)
57+
XCTAssertEqual(Timecode.FrameRate(raw: 119.88, favorDropFrame: true), ._119_88_drop)
58+
XCTAssertEqual(Timecode.FrameRate(raw: 119.8801198801), ._119_88)
59+
XCTAssertEqual(Timecode.FrameRate(raw: 119.8801198801, favorDropFrame: true), ._119_88_drop)
60+
61+
XCTAssertEqual(Timecode.FrameRate(raw: 120), ._120)
62+
// raw 120 fps is not correct for 120d; matches 120 instead
63+
XCTAssertEqual(Timecode.FrameRate(raw: 120, favorDropFrame: true), ._120)
64+
65+
}
66+
67+
func testInit_raw_invalid() {
68+
69+
XCTAssertNil(Timecode.FrameRate(raw: 0.0))
70+
XCTAssertNil(Timecode.FrameRate(raw: 1.0))
71+
XCTAssertNil(Timecode.FrameRate(raw: 26.0))
72+
XCTAssertNil(Timecode.FrameRate(raw: 29.0))
73+
XCTAssertNil(Timecode.FrameRate(raw: 29.9))
74+
XCTAssertNil(Timecode.FrameRate(raw: 30.1))
75+
XCTAssertNil(Timecode.FrameRate(raw: 30.5))
76+
XCTAssertNil(Timecode.FrameRate(raw: 31.0))
77+
XCTAssertNil(Timecode.FrameRate(raw: 59.0))
78+
XCTAssertNil(Timecode.FrameRate(raw: 59.9))
79+
XCTAssertNil(Timecode.FrameRate(raw: 60.1))
80+
XCTAssertNil(Timecode.FrameRate(raw: 60.5))
81+
XCTAssertNil(Timecode.FrameRate(raw: 61.0))
82+
XCTAssertNil(Timecode.FrameRate(raw: 119.0))
83+
XCTAssertNil(Timecode.FrameRate(raw: 119.8))
84+
XCTAssertNil(Timecode.FrameRate(raw: 120.1))
85+
XCTAssertNil(Timecode.FrameRate(raw: 120.5))
86+
XCTAssertNil(Timecode.FrameRate(raw: 121.0))
87+
88+
}
89+
90+
}
91+
92+
#endif

0 commit comments

Comments
 (0)