Skip to content

Commit 09c4720

Browse files
committed
Improved real time conversion & added additional unit test
1 parent 424ad09 commit 09c4720

File tree

2 files changed

+101
-12
lines changed

2 files changed

+101
-12
lines changed

Sources/TimecodeKit/Data Interchange/Timecode Real Time.swift

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,7 @@ extension Timecode {
1919
public var realTimeValue: TimeInterval {
2020

2121
get {
22-
var calc = Double(totalElapsedFrames) * (1.0 / frameRate.frameRateForRealTimeCalculation)
23-
24-
// over-estimate so real time is just past the equivalent timecode
25-
// so calculations of real time back into timecode work reliably
26-
// otherwise, this math produces a real time value that can be a hair under the actual elapsed real time that would trigger the equivalent timecode
27-
28-
calc += 0.000_000_010 // 10 nanoseconds
29-
30-
return calc
22+
Double(totalElapsedFrames) * (1.0 / frameRate.frameRateForRealTimeCalculation)
3123
}
3224

3325
set {
@@ -48,10 +40,9 @@ extension Timecode {
4840
var calc = fromRealTimeValue / (1.0 / frameRate.frameRateForRealTimeCalculation)
4941

5042
// over-estimate so real time is just past the equivalent timecode
51-
// so calculations of real time back into timecode work reliably
52-
// otherwise, this math produces a real time value that can be a hair under the actual elapsed real time that would trigger the equivalent timecode
43+
// since raw time values in practise can be a hair under the actual elapsed real time that would trigger the equivalent timecode (due to precision and rounding behaviors that may not be in our control, depending on where the passed real time value originated)
5344

54-
calc += 0.000_000_600 // 600 nanoseconds
45+
calc += 0.000_010 // 10 microseconds
5546

5647
// final calculation
5748

Tests/TimecodeKit-Unit-Tests/Unit Tests/Data Interchange/Timecode Real Time Tests.swift

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,104 @@ class Timecode_UT_DI_Real_Time_Tests: XCTestCase {
161161

162162
}
163163

164+
func testTimecode_RealTimeValue_RealWorld_SubFrames() {
165+
166+
// test against real-world values extracted from DAWs
167+
168+
// Cubase 11 XML file output (high resolution floating-point times in seconds)
169+
170+
// the timecodes in the constant variable names are the timecodes as seen in Cubase
171+
// the float-point number constant values are extracted from a Track Archive XML file exported from the Cubase project which outputs very high precision float-point numbers in seconds to define many attributes such as the project start time, and event start times and lengths on tracks which are in absolute time mode (not musical bars/beats mode which gets stored as PPQ values in the XML file instead of float-point seconds)
172+
173+
174+
// 23.976fps, 80 subframe divisor
175+
176+
// _HH_MM_SS_FF_SF
177+
178+
// session start timecode
179+
let _00_49_27_15_00 = 2970.5926250000002255546860396862030029296875
180+
181+
// events: delta times from session start time
182+
let _00_49_29_17_00_delta = 2.0854162836310767836778268247144296765327453613281
183+
let _00_49_31_09_00_delta = 3.7537499627098442900319241744000464677810668945312
184+
let _00_49_33_21_79_delta = 6.297436893962323978257700218819081783294677734375
185+
let _00_49_38_01_79_delta = 10.468270548180987233877203834708780050277709960938
186+
187+
// test timecode formation from real time
188+
189+
let start = Timecode(realTimeValue: _00_49_27_15_00,
190+
at: ._23_976)!
191+
XCTAssertEqual(start.components,
192+
TCC(h: 00, m: 49, s: 27, f: 15, sf: 00))
193+
194+
let event1 = Timecode(realTimeValue: _00_49_27_15_00 + _00_49_29_17_00_delta,
195+
at: ._23_976)!
196+
XCTAssertEqual(event1.components,
197+
TCC(h: 00, m: 49, s: 29, f: 17, sf: 00))
198+
199+
let event2 = Timecode(realTimeValue: _00_49_27_15_00 + _00_49_31_09_00_delta,
200+
at: ._23_976)!
201+
XCTAssertEqual(event2.components,
202+
TCC(h: 00, m: 49, s: 31, f: 09, sf: 00))
203+
204+
let event3 = Timecode(realTimeValue: _00_49_27_15_00 + _00_49_33_21_79_delta,
205+
at: ._23_976)!
206+
XCTAssertEqual(event3.components,
207+
TCC(h: 00, m: 49, s: 33, f: 21, sf: 79))
208+
209+
let event4 = Timecode(realTimeValue: _00_49_27_15_00 + _00_49_38_01_79_delta,
210+
at: ._23_976)!
211+
XCTAssertEqual(event4.components,
212+
TCC(h: 00, m: 49, s: 38, f: 01, sf: 79))
213+
214+
// test real time matching the seconds constants
215+
216+
// start
217+
XCTAssertEqual(
218+
TCC(h: 00, m: 49, s: 27, f: 15, sf: 00)
219+
.toTimecode(at: ._23_976)!
220+
.realTimeValue,
221+
_00_49_27_15_00
222+
)
223+
224+
// event1
225+
XCTAssertEqual(
226+
TCC(h: 00, m: 49, s: 29, f: 17, sf: 00)
227+
.toTimecode(at: ._23_976)!
228+
.realTimeValue,
229+
_00_49_27_15_00 + _00_49_29_17_00_delta,
230+
accuracy: 0.0000005
231+
)
232+
233+
// event2
234+
XCTAssertEqual(
235+
TCC(h: 00, m: 49, s: 31, f: 09, sf: 00)
236+
.toTimecode(at: ._23_976)!
237+
.realTimeValue,
238+
_00_49_27_15_00 + _00_49_31_09_00_delta,
239+
accuracy: 0.0000005
240+
)
241+
242+
// event3
243+
XCTAssertEqual(
244+
TCC(h: 00, m: 49, s: 33, f: 21, sf: 79)
245+
.toTimecode(at: ._23_976)!
246+
.realTimeValue,
247+
_00_49_27_15_00 + _00_49_33_21_79_delta,
248+
accuracy: 0.0000005
249+
)
250+
251+
// event4
252+
XCTAssertEqual(
253+
TCC(h: 00, m: 49, s: 38, f: 01, sf: 79)
254+
.toTimecode(at: ._23_976)!
255+
.realTimeValue,
256+
_00_49_27_15_00 + _00_49_38_01_79_delta,
257+
accuracy: 0.0000005
258+
)
259+
260+
}
261+
164262
}
165263

166264
#endif

0 commit comments

Comments
 (0)