package main import "core:fmt" import "core:os" import "core:strings" import "core:path/filepath" import "core:c/libc" import "../ltc" /* TODO: - Automatically detect if timecode is in left or right channel - Mute channel containing timecode - Better error when specifying a pure folder, or handle it automatically */ temp_audio_file_name :: "temp.raw" sample_rate :: 48000 frame_rate := i32(25) main :: proc() { fmt.println("ChirpSync - Copyright (C) 2024 Sander J. Skjegstad\nThis program comes with ABSOLUTELY NO WARRANTY.\nThis is free software, and you are welcome to redistribute it\nunder certain conditions. See the GPLv3 for details.") arguments := os.args[1:] input := arguments[0] output := arguments[1] input_files, err_in_list := filepath.glob(input) if err_in_list == .Syntax_Error { fmt.println("ERROR: Syntax_Error in input.") os.exit(1) } fmt.printfln("%#v", input_files) output_info, erro := os.lstat(output) if erro != os.ERROR_NONE { fmt.println("ERROR: Output invalid.") os.exit(1) } fmt.printfln("%#v", output_info) if !output_info.is_dir { fmt.println("ERROR: Output needs to be a directory.") os.exit(1) } for input_file, index in input_files { fmt.println("\n\n - - - File", index, "- - -\n", input_file, "\n\n") fmt.println("\n\nExtracting audio...\n\n") os.remove(temp_audio_file_name) audio_extract_command := fmt.caprintf("ffmpeg -y -loglevel fatal -i %v -ss 0 -t 12 -f u8 -hide_banner -map_metadata -1 -filter_complex \"[0:a]channelsplit=channel_layout=stereo:channels=FL[left]\" -map \"[left]\" -acodec pcm_u8 %v", input_file, temp_audio_file_name) fmt.printf("Command: %v\n\n", audio_extract_command) libc.system(audio_extract_command) fmt.println("\n\nLoading audio...") raw_audio_handle, errra := os.open(temp_audio_file_name) if errra != os.ERROR_NONE { fmt.println("ERROR: Could not get file handle for raw audio.") os.exit(1) } raw_audio, erra := os.read_entire_file_from_handle(raw_audio_handle) if !erra { fmt.println("ERROR: Could not read raw audio file.") os.exit(1) } fmt.println("\nDecoding LTC...\n") decoder := ltc.decoder_create(sample_rate/frame_rate, 128) address := 0xA0 for ltc.decoder_queue_length(decoder)<24 && address=sample_rate*10 { fmt.println("WARNING: Failed to find timecode in:", input_file) continue } avg_sum : i64 = 0 prev_item : ltc.FrameExt = {} for item, i in decoder.queue[0:10] { fmt.printf("%03d - ", i) print_ltc(item) fmt.printf("\n") if i>0 do avg_sum += item.off_start - prev_item.off_start prev_item = item } average_frame_distance := avg_sum/9 fmt.println("Avg. frame distance:", average_frame_distance) initial_timecode := decoder.queue[0] tv_standard : ltc.TV_STANDARD = .TV_625_50 switch { case frame_rate % 30 == 0: tv_standard = .TV_525_60 case frame_rate % 25 == 0: tv_standard = .TV_625_50 case frame_rate % 24 == 0: tv_standard = .TV_FILM_24 } output_timecode := initial_timecode for output_timecode.off_start >= 0 { fmt.println("Reconstructing timecode at start point... @", output_timecode.off_start) ltc.frame_decrement(&output_timecode.ltc, frame_rate, tv_standard, {.TC_CLOCK, .BGF_DONT_TOUCH}) output_timecode.off_start -= average_frame_distance } fmt.print("\nFinal timecode: ") print_ltc(output_timecode) fmt.print("\n") ltc.decoder_free(decoder) fmt.println("\nWriting new file...\n") timecode_string := timecode_to_string(output_timecode) output_command := fmt.caprintf("ffmpeg -hide_banner -y -i %v -metadata:s timecode=%v -map 0:a -map 0:v -movflags use_metadata_tags -acodec copy -vcodec copy -metadata:s timecode=%v -timecode %v %v%v", input_file, timecode_string, timecode_string, timecode_string, output_info.fullpath, filepath.base(input_file)) fmt.println("Command:", output_command, "\n") libc.system(output_command) os.close(raw_audio_handle) } } print_ltc :: proc(f : ltc.FrameExt) { timecode_string := timecode_to_string(f) fmt.printf("@%07d - TC: %v", f.off_start, timecode_string,) } timecode_to_string :: proc(f : ltc.FrameExt) -> string { return fmt.aprintf("%02d:%02d:%02d:%02d", f.ltc.hours_tens * 10 + f.ltc.hours_units, f.ltc.mins_tens * 10 + f.ltc.mins_units, f.ltc.secs_tens * 10 + f.ltc.secs_units, f.ltc.frame_tens * 10 + f.ltc.frame_units,) }