Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decoder not parsing the direct output of the Encoder #65

Closed
roozmahdavian opened this issue Sep 4, 2022 · 3 comments
Closed

Decoder not parsing the direct output of the Encoder #65

roozmahdavian opened this issue Sep 4, 2022 · 3 comments

Comments

@roozmahdavian
Copy link

roozmahdavian commented Sep 4, 2022

Clearly I'm doing something dumb here. This is intended to be a very simple test of LTC encoding on macOS (arm64) - the code below is in Swift, calling directly to libltc through Swift's support for C interop.

Our intention is to generate (and playback) single LTC frames from our app when an event occurs, for timesync to that specific event from external cameras.

The code below generates a single LTC-encoded audio frame for the current time at a given fps/sample-rate, and then (as a sanity check) tries to decode that same frame (as stored in buffer). But the decode fails to parse an LTC, returning 0 from ltc_decoder_read. The actual values that the Encoder is writing into buffer for each sample are attached, and I've verified that Decoder does in fact read them appropriately when indexing into buffer in C-land. They seem at least superficially correct, as they're in the 38-218 range.

EncoderSamples-44100Hz-30fps.txt

// MARK: - LTC Input

let date = Date()
var calendar = Calendar.current
calendar.timeZone = .init(abbreviation: "UTC")!

let seconds = calendar.component(.second, from: date)
let minutes = calendar.component(.minute, from: date)
let hour = calendar.component(.hour, from: date)

let day = calendar.component(.day, from: date)
let month = calendar.component(.month, from: date)
let year = calendar.component(.year, from: date) % 2000

var timecode = SMPTETimecode()
let timezone = "+0000".utf8CString
timecode.timezone = (timezone[0], timezone[1], timezone[2], timezone[3], timezone[4], timezone[5])
timecode.days = UInt8(day)
timecode.hours = UInt8(hour)
timecode.mins = UInt8(minutes)
timecode.secs = UInt8(seconds)
timecode.months = UInt8(month)
timecode.years = UInt8(year)
timecode.frame = 0

// Along with the sample rate, determines the duration of individually encoded frame (i.e. timecode).
let fps: Double = 30

let encoder = ltc_encoder_create(Double(sampleRate), fps, LTC_TV_525_60, Int32(LTC_USE_DATE.rawValue))
ltc_encoder_set_timecode(encoder, &timecode)

// We need to write two frames to properly encode a single frame - size the buffer accordingly.
var encodedSize = ltc_encoder_get_buffersize(encoder)
var buffer: [ltcsnd_sample_t] = Array(repeating: 0, count: 2 * encodedSize)

ltc_encoder_encode_frame(encoder)
let primaryFrameOffset = ltc_encoder_copy_buffer(encoder, &buffer)
ltc_encoder_end_encode(encoder)
// The call to end_encode writes an additional frame and clears the internal buffer - we need to copy that back over.
ltc_encoder_copy_buffer(encoder, &buffer[Int(primaryFrameOffset)])

for i in 0..<encodedSize {
    print("Sample \(i): \(buffer[i])")
}

// MARK: - Verify Encoding

let decoder = ltc_decoder_create(Int32(Double(sampleRate) / fps), 2)
ltc_decoder_write(decoder, &buffer, encodedSize, 0)

var decodedFrame = LTCFrameExt()
let res = ltc_decoder_read(decoder, &decodedFrame)
print("Decoded Frame: ", res)

var decodedTimecode = SMPTETimecode()
ltc_frame_to_time(&decodedTimecode, &(decodedFrame.ltc), 0)

print("Wrote:  \(timecode)")
print("Decoded: \(decodedTimecode)")

exit(0)
@x42
Copy link
Owner

x42 commented Sep 4, 2022

Have a look at http://x42.github.io/libltc/ltc_8h.html#structLTCFrame

image

There is a rising-edge at both ends. The transition at the start of the next frame marks the end of the previous frame. However this is only encoded at the beginning of the next frame.

This is not usually an issue since LTC is sent as continuous stream of data, and even when the timecode does not change, it is common to repeat the last frame (rather than send silence. A loss of LTC signal is usually used indicate to a tape machine to unspool the tape).

So in your example, you need call ltc_encoder_encode_frame twice. the 2nd call terminates the first Frame and you can then decode that.

Also change the line let decoder = ltc_decoder_create(1, 1) to let decoder = ltc_decoder_create(sampleRate/fps, 2).

--
PS. I'll add an API to explicitly end encoding and add a final transition.

@x42
Copy link
Owner

x42 commented Sep 4, 2022

Recent git now has a ltc_encoder_end_encode method.
See also https://github.com/x42/libltc/blob/master/tests/ltcloop.c

@roozmahdavian
Copy link
Author

Wonderful - thank you so much for adding this.

The time now appears to be decoded correctly, but the date (year/month/day) is missing / output as zeros. I've updated the code snippet above to reflect what I'm currently running -- is there perhaps an additional flag I need to set to encode the date? I tried LTC_USE_DATE, to no avail. The test you linked just verifies time, sans date.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants