diff options
Diffstat (limited to 'src/main.odin')
| -rw-r--r-- | src/main.odin | 410 |
1 files changed, 334 insertions, 76 deletions
diff --git a/src/main.odin b/src/main.odin index c0f66ba..7255dd6 100644 --- a/src/main.odin +++ b/src/main.odin @@ -6,6 +6,7 @@ import "core:os/os2" import "core:path/filepath" import "core:sys/windows" import "core:strings" +import "core:math" import "wav" /* @@ -45,6 +46,7 @@ Info_Line :: struct { } Report :: struct { + // Content title : string, info_lines : []Info_Line, header : []string, @@ -53,12 +55,15 @@ Report :: struct { row_count : int, info_line_count : int, tc_column_index : int, + + // Meta + path : string, } CSV :: string Directory :: [dynamic]string Job :: union {CSV, Directory} -job_list :: [dynamic]Job +job_list : [dynamic]Job // TODO: Changing file_list to job_list, so the Directory jobs can contain a list of all the relevant .wav files before being sent to parse_folder() main :: proc() { @@ -92,33 +97,48 @@ main :: proc() { if(path_info.is_dir) { fmt.printf("Directory submitted! Walking directory...\n\n") fmt.printf("š {}\n", path_info.name) - try_os2 := walk_directory(path_info.fullpath, &file_count, &job_list, 1) + try_os2 := walk_directory(path_info.fullpath, &file_count, 1) if len(job_list) == 0 && try_os2 { fmt.printf("\nNot_Dir error encountered. Trying os2 version...\n\n") fmt.printf("š {}\n", path_info.name) - walk_directory_os2(path_info.fullpath, &file_count, &job_list, 1) + walk_directory_os2(path_info.fullpath, &file_count, 1) } } else { fmt.println("File submitted! Processing file...") - append(&job_list, strings.clone(path_info.fullpath)) + append(&job_list, CSV(strings.clone(path_info.fullpath))) } - for file, f in job_list { + for job, i in job_list { - file_info, _ := os.stat(file) - fmt.printf("\nš File {}: {}\n", f+1, file_info.name) - parsed, ok_parse := parse(file_info.fullpath) - if !ok_parse { - fmt.printf("Parse failed: {}\n", file_info.fullpath) - continue + parsed : Report + parse_ok : bool + switch file in job { + case CSV: + file_info, _ := os.stat(file) + fmt.printf("\nš File {}: {}\n", i+1, file_info.name) + parsed, parse_ok = parse_file(file_info.fullpath) + if !parse_ok { + fmt.printf("Parse failed: {}\n", file_info.fullpath) + continue + } + case Directory: + fmt.printf("\nš Folder {}: ", i+1) + parsed, parse_ok = parse_folder(file) + fmt.printf("{}", parsed.title) + if parse_ok { + fmt.printf("\nParsed %d WAV(s).\n", parsed.row_count) + } else { + file_info, _ := os.stat(file[0]) + fmt.printf("\nParse failed: {}\n", file_info.fullpath) + continue + } } - output_name := fmt.aprintf("{}/{}_Knekt_Lydrapport.html", filepath.dir(file_info.fullpath), parsed.title, allocator=context.temp_allocator) - render(parsed, output_name) + render(parsed) free_all(context.temp_allocator) files_done += 1 } - fmt.printf("\nCompleted {}/{} files.\n\n", files_done, len(job_list)) + fmt.printf("\nCompleted {}/{} job(s).\n\n", files_done, len(job_list)) } else { fmt.printf("ERROR could not get path info for: {}\n", input_file_name) } @@ -126,19 +146,253 @@ main :: proc() { } -parse :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { - if os.is_dir(path) { - return parse_folder(path) +parse_folder :: proc(paths : Directory) -> (Report, bool) { + + // 888 888 888 8888b. 888 888 + // 888 888 888 "88b 888 888 + // 888 888 888 .d888888 Y88 88P + // d8b Y88b 888 d88P 888 888 Y8bd8P + // Y8P "Y8888888P" "Y888888 Y88P + + output : Report = {} + + wavs : [dynamic]wav.Wav + + max_channels := 0 + for path, i in paths { + w, ok := wav.read(path) + if ok { + append(&wavs, w) + max_channels = max(max_channels, w.channels) + } + } + + header_build : [dynamic]string + append(&header_build, "Circled") + append(&header_build, "File Name") + append(&header_build, "Scene") + append(&header_build, "Take") + append(&header_build, "Timecode") + append(&header_build, "TC FPS") + append(&header_build, "User Bits") + append(&header_build, "Tape") + append(&header_build, "Date") + append(&header_build, "Project") + append(&header_build, "Sample Rate") + append(&header_build, "Format") // Bit depth and int vs float + first_channel_index := len(header_build) + last_channel_index := -1 + for i in 0..<max_channels { + track_title := fmt.aprintf("Track %d", i+1) + last_channel_index = len(header_build) + append(&header_build, track_title) + } + append(&header_build, "Note") + output.header = header_build[:] + + output.column_count = len(header_build) + output.row_count = len(wavs) + + output.table = make([][]string, output.row_count, context.temp_allocator) + for &row in output.table { + row = make([]string, output.column_count, context.temp_allocator) + } + + output.info_lines = make([]Info_Line, 64, context.temp_allocator) + + info_txt, info_txt_ok := os.read_entire_file(HEADER_FIELDS_PATH, context.temp_allocator) + if info_txt_ok { + it := string(info_txt) + line_index := 0 + for line in strings.split_lines_iterator(&it) { + if strings.starts_with(line, "#") { + continue + } + if len(line)<2 { + continue + } + colon := strings.index_rune(line, ':') + if colon==-1 { + continue + } + CUTSET :: " " + output.info_lines[line_index].field = strings.trim(line[:colon+1], CUTSET) + output.info_lines[line_index].entry = strings.trim(line[colon+1:], CUTSET) + line_index += 1 + } + output.info_lines[line_index].field = " " + output.info_lines[line_index].entry = "- - - - -" + line_index += 1 + output.info_line_count = line_index } - return parse_file(path, device) -} -parse_folder :: proc(path : string) -> (Report, bool) { - output : Report = {} - return output, false + + // Populating the table with data + + for w, i in wavs { + row := output.table[i] + stat, _ := os.stat(w.path, allocator=context.temp_allocator) + + for name, i in w.channel_names { + row[first_channel_index + i] = name + } + for title, i in output.header { + switch title { + case "File Name": + row[i] = stat.name + case "Scene": + row[i] = w.scene + case "Take": + row[i] = fmt.tprintf("T%03d", w.take) + case "Timecode": + row[i] = fmt.tprintf("%02d:%02d:%02d:%02d", // Timecode + w.timecode.hour, + w.timecode.minute, + w.timecode.second, + int(math.round(w.timecode.frame))) + case "TC FPS": + if w.tc_dropframe { // TC FPS + row[i] = fmt.tprintf("%.03f DF", w.tc_framerate) + } else { + row[i] = fmt.tprintf("%.03f ND", w.tc_framerate) + } + case "User Bits": + if w.ubits != {0,0,0,0,0,0,0,0,} { + row[i] = fmt.tprintf("%d%d%d%d%d%d%d%d", expand_values(w.ubits)) + } + case "Tape": + row[i] = w.tape + case "Date": + row[i] = fmt.tprintf("%04d-%02d-%02d", expand_values(w.date)) + case "Project": + row[i] = w.project + case "Sample Rate": + row[i] = fmt.tprintf("%d Hz", w.sample_rate) + case "Format": + switch w.format { // "Format", aka bit depth + int vs float + case .INT: + row[i] = fmt.tprintf("%d-bit int", w.bit_depth) + case .FLOAT: + row[i] = fmt.tprintf("%d-bit float", w.bit_depth) + } + case "Circled": + if w.circled do row[i] = "O" + case "Note": + row[i] = w.note + } + } + } + + + + // Cleanup! + when VERBOSE do fmt.printf("Struct before cleanup:\n%#v\n", output) + + // Stacking tracks to the left + for &line, l in output.table { + stacking_index := first_channel_index + for &field, f in line[first_channel_index:last_channel_index+1] { + if field != "" { + line[stacking_index] = field + stacking_index += 1 + } + } + for &field, f in line[stacking_index:last_channel_index+1] { + field = "" + } + } + + + // Cleaning out unused columns + touched := make([]bool, output.column_count, context.temp_allocator) + // Finding them + for line, l in output.table { + for field, f in line { + if touched[f] do continue + if field != "" { + touched[f] = true + } + } + } + + // Turning unchanging columns into info line + changed := make([]bool, output.column_count, context.temp_allocator) + prev_line : []string = nil + for line, l in output.table { + if l>0 { + prev_line = output.table[l - 1] + for field, f in line { + if (prev_line[f] != field) || + (first_channel_index <= f && f <= last_channel_index) || + (f == output.tc_column_index) { + changed[f] = true + } + } + } + } + for did_change, i in changed { + if (!did_change) && touched[i] { + field := fmt.aprintf("{}: ", output.header[i], allocator=context.temp_allocator) + entry := prev_line[i] + output.info_lines[output.info_line_count] = {field=field, entry=entry} + output.info_line_count += 1 + } + } + + + // Removing unused and static + for &line, l in output.table { + stacking_index := 0 + for &field, f in line { + if touched[f] && changed[f] { + line[stacking_index] = field + stacking_index += 1 + } + } + for &field, f in line[stacking_index:] { + field = "" + } + } + stacking_index := 0 + for &field, f in output.header { + if touched[f] && changed[f] { + output.header[stacking_index] = field + stacking_index += 1 + } + } + for &field, f in output.header[stacking_index:] { + field = "" + } + + output.column_count = stacking_index + + // Setting title for report + output.title = strings.trim(filepath.base(filepath.dir(paths[0])), "/\\") + for item in output.info_lines { + if item.field == "Tape" { + output.title = item.entry + } + } + + // Setting column to sort by + for title, i in output.header { + if title == "Timecode" { + output.tc_column_index = i + break + } + } + + when VERBOSE do fmt.printf("Struct before output:\n%#v\n", output) + + output.path = fmt.tprintf("{}/{}_Knekt_Lydrapport.html", filepath.dir(paths[0]), output.title) + + return output, true } -parse_file :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { + + + +parse_file :: proc(path : CSV, device : Device = .UNSET) -> (Report, bool) { device := device output : Report = {} data, ok := os.read_entire_file(path, context.temp_allocator) @@ -158,20 +412,20 @@ parse_file :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { if (device!=.UNSET) { break } if line == "\"SOUND REPORT\"," { device = .ZOOM - if VERBOSE do fmt.printf("Detected ZOOM from quotes and comma on line index {}\n", line_number) + when VERBOSE do fmt.printf("Detected ZOOM from quotes and comma on line index {}\n", line_number) } if line == "\"ZOOM F8\"," { device = .ZOOM - if VERBOSE do fmt.printf("Detected ZOOM from \"ZOOM F8\" on line index {}\n", line_number) + when VERBOSE do fmt.printf("Detected ZOOM from \"ZOOM F8\" on line index {}\n", line_number) } if line == "SOUND REPORT" { device = .SD6 - if VERBOSE do fmt.printf("Detected SOUND_DEVICES from unquoted SOUND REPORT line index {}\n", line_number) + when VERBOSE do fmt.printf("Detected SOUND_DEVICES from unquoted SOUND REPORT line index {}\n", line_number) } if len(line)<15 do continue if line[:13] == "SOUND REPORT," { device = .SD8 - if VERBOSE do fmt.printf("Detected SOUND_DEVICES 8-series from SOUND REPORT with missing newline on line index {}\n", line_number) + when VERBOSE do fmt.printf("Detected SOUND_DEVICES 8-series from SOUND REPORT with missing newline on line index {}\n", line_number) } } @@ -271,7 +525,7 @@ parse_file :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { // STAGE 3 -------------------------------------------------------------- // Filling with data - if VERBOSE do fmt.printf("Struct before main parse:\n%#v\n", output) + when VERBOSE do fmt.printf("Struct before main parse:\n%#v\n", output) first_channel_index := -1 last_channel_index := -1 @@ -304,7 +558,7 @@ parse_file :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { stage = .INFO line_elements := strings.split(line, ",") - if VERBOSE do fmt.printf(".INFO {}: {}\n", line_index, line_elements) + when VERBOSE do fmt.printf(".INFO {}: {}\n", line_index, line_elements) field := fmt.aprintf("{}:", line_elements[1], allocator=context.temp_allocator) entry := line_elements[2] output.info_lines[info_line_index].field = field @@ -318,9 +572,9 @@ parse_file :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { continue } line_elements := strings.split(line, ",") - if VERBOSE do fmt.printf(".INFO {}: {}\n", line_index, line_elements) + when VERBOSE do fmt.printf(".INFO {}: {}\n", line_index, line_elements) if line_elements[0] == "Date" { - if VERBOSE do fmt.printf("Skipping line {}, because it's the retarded date field on an 8-series\n", line_index) + when VERBOSE do fmt.printf("Skipping line {}, because it's the retarded date field on an 8-series\n", line_index) output.info_line_count -= 1 continue } @@ -335,10 +589,10 @@ parse_file :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { if line == "," { continue // This is here because there are a bunch of lines that are just commas before the header } else if len(line)>3 { - if VERBOSE do fmt.printf(".HEADER {}:", line_index) + when VERBOSE do fmt.printf(".HEADER {}:", line_index) // No trailing comma in the header?? for element, e in strings.split(line, ",") { - if VERBOSE do fmt.printf(" {}", element) + when VERBOSE do fmt.printf(" {}", element) output.header[e] = element if element[:3] == "Trk" { @@ -351,22 +605,22 @@ parse_file :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { } } - if VERBOSE do fmt.printf("\n") - if VERBOSE do fmt.printf("first_channel_index: {}\n", first_channel_index) - if VERBOSE do fmt.printf("last_channel_index: {}\n", last_channel_index) + when VERBOSE do fmt.printf("\n") + when VERBOSE do fmt.printf("first_channel_index: {}\n", first_channel_index) + when VERBOSE do fmt.printf("last_channel_index: {}\n", last_channel_index) stage = .BODY } case .BODY: if len(line) > 2 { - if VERBOSE do fmt.printf(".BODY {}:", line_index) + when VERBOSE do fmt.printf(".BODY {}:", line_index) for element, e in strings.split(line, ",") { - if VERBOSE do fmt.printf(" {}", element) + when VERBOSE do fmt.printf(" {}", element) entry : string = element output.table[body_line_index][e] = entry } - if VERBOSE do fmt.printf("\n") + when VERBOSE do fmt.printf("\n") body_line_index += 1 } @@ -409,7 +663,7 @@ parse_file :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { continue } line_elements := strings.split(line, ",") - if VERBOSE do fmt.printf(".INFO {}: {}\n", line_index, line_elements) + when VERBOSE do fmt.printf(".INFO {}: {}\n", line_index, line_elements) field := line_elements[0] entry_raw := line_elements[1] entry := line_elements[1][1:len(entry_raw)-1] @@ -422,10 +676,10 @@ parse_file :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { if line == "," { // This is here because there are a bunch of lines that are just commas before the header } else if len(line)>3 { - if VERBOSE do fmt.printf(".HEADER {}:", line_index) + when VERBOSE do fmt.printf(".HEADER {}:", line_index) // No trailing comma in the header?? for element, e in strings.split(line, ",") { - if VERBOSE do fmt.printf(" {}", element) + when VERBOSE do fmt.printf(" {}", element) output.header[e] = element if element[:4] == "Trk " { @@ -438,20 +692,20 @@ parse_file :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { } } - if VERBOSE do fmt.printf("\n") + when VERBOSE do fmt.printf("\n") } else if line == "" { stage = .BODY - if VERBOSE do fmt.printf("first_channel_index: {}\n", first_channel_index) - if VERBOSE do fmt.printf("last_channel_index: {}\n", last_channel_index) + when VERBOSE do fmt.printf("first_channel_index: {}\n", first_channel_index) + when VERBOSE do fmt.printf("last_channel_index: {}\n", last_channel_index) } case .BODY: if len(line) > 2 { - if VERBOSE do fmt.printf(".BODY {}:", line_index) + when VERBOSE do fmt.printf(".BODY {}:", line_index) // to skip empty entry after trailing comma we do a silly slice for element, e in strings.split(line, ",")[:output.column_count] { - if VERBOSE do fmt.printf(" {}", element) + when VERBOSE do fmt.printf(" {}", element) entry : string = element // Stripping quotes if after tracks begin if e >= first_channel_index && (len(element)>0) { @@ -459,7 +713,7 @@ parse_file :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { } output.table[body_line_index][e] = entry } - if VERBOSE do fmt.printf("\n") + when VERBOSE do fmt.printf("\n") body_line_index += 1 } @@ -506,7 +760,7 @@ parse_file :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { continue } line_elements := strings.split(line, ",") - if VERBOSE do fmt.printf(".INFO {}: {}\n", line_index, line_elements) + when VERBOSE do fmt.printf(".INFO {}: {}\n", line_index, line_elements) field_raw := line_elements[0] entry_raw := line_elements[1] field := line_elements[0][1:len(field_raw)-1] @@ -516,10 +770,10 @@ parse_file :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { info_line_index += 1 case .HEADER: - if VERBOSE do fmt.printf(".HEADER {}:", line_index) + when VERBOSE do fmt.printf(".HEADER {}:", line_index) // to skip empty entry after trailing comma we do a silly slice for element, e in strings.split(line, ",")[:output.column_count] { - if VERBOSE do fmt.printf(" {}", element) + when VERBOSE do fmt.printf(" {}", element) output.header[e] = element[1:len(element)-1] if element[:4] == "\"Tr " { @@ -531,20 +785,20 @@ parse_file :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { output.tc_column_index = e } } - if VERBOSE do fmt.printf("\n") + when VERBOSE do fmt.printf("\n") stage = .BODY - if VERBOSE do fmt.printf("first_channel_index: {}\n", first_channel_index) - if VERBOSE do fmt.printf("last_channel_index: {}\n", last_channel_index) + when VERBOSE do fmt.printf("first_channel_index: {}\n", first_channel_index) + when VERBOSE do fmt.printf("last_channel_index: {}\n", last_channel_index) case .BODY: if line == "" do break - if VERBOSE do fmt.printf(".BODY {}:", line_index) + when VERBOSE do fmt.printf(".BODY {}:", line_index) // to skip empty entry after trailing comma we do a silly slice for element, e in strings.split(line, ",")[:output.column_count] { - if VERBOSE do fmt.printf(" {}", element) + when VERBOSE do fmt.printf(" {}", element) output.table[body_line_index][e] = element[1:len(element)-1] } - if VERBOSE do fmt.printf("\n") + when VERBOSE do fmt.printf("\n") body_line_index += 1 } } @@ -555,7 +809,7 @@ parse_file :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { // STAGE 4 -------------------------------------------------------------- // Cleanup! - if VERBOSE do fmt.printf("Struct before cleanup:\n%#v\n", output) + when VERBOSE do fmt.printf("Struct before cleanup:\n%#v\n", output) // Stacking tracks to the left for &line, l in output.table { @@ -635,13 +889,15 @@ parse_file :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { output.column_count = stacking_index - if VERBOSE do fmt.printf("Struct before output:\n%#v\n", output) + when VERBOSE do fmt.printf("Struct before output:\n%#v\n", output) + + output.path = fmt.tprintf("{}/{}_Knekt_Lydrapport.html", filepath.dir(path), output.title) return output, true } -render :: proc(report : Report, path : string) { +render :: proc(report : Report) { // Now we output the HTML. builder := strings.builder_make(context.temp_allocator) @@ -687,9 +943,9 @@ render :: proc(report : Report, path : string) { strings.write_string(&builder, PART_END) output_text := strings.to_string(builder) - os.write_entire_file(path, transmute([]u8)output_text) + os.write_entire_file(report.path, transmute([]u8)output_text) - fmt.printf("Output: {}\n", path) + fmt.printf("Output: {}\n", report.path) } indent_by :: proc(i : int) { @@ -698,7 +954,7 @@ indent_by :: proc(i : int) { } } -walk_directory :: proc(path : string, file_number : ^int, job_list : ^[dynamic]string, depth : int = 0) -> bool { +walk_directory :: proc(path : string, file_number : ^int, depth : int = 0) -> bool { handle, ok := os.open(path) if ok != os.ERROR_NONE { indent_by(depth) @@ -715,7 +971,7 @@ walk_directory :: proc(path : string, file_number : ^int, job_list : ^[dynamic]s return true } - wav_count := 0 + wav_files : [dynamic]string has_csv := false for file in files { @@ -725,7 +981,7 @@ walk_directory :: proc(path : string, file_number : ^int, job_list : ^[dynamic]s if file.is_dir { indent_by(depth) fmt.printf("š %s\n", file.name) - walk_directory(full_path, file_number, job_list, depth+1) // Recurse + walk_directory(full_path, file_number, depth+1) // Recurse } else { // If file is actually a file @@ -734,29 +990,30 @@ walk_directory :: proc(path : string, file_number : ^int, job_list : ^[dynamic]s if extension == ".csv" { indent_by(depth) fmt.printf("š [#%d] %s\n", file_number^, file.name) - append(job_list, strings.clone(file.fullpath)) + append(&job_list, strings.clone(file.fullpath)) file_number^ += 1 has_csv = true } if extension == ".wav" { - wav_count += 1 + append(&wav_files, strings.clone(full_path)) } } } + wav_count := len(wav_files) if wav_count>0 && !has_csv { - indent_by(depth+1) + indent_by(depth) if wav_count == 1 { - fmt.printf("š½ [#%d] 1 WAV file.\n", file_number^) + fmt.printf("š½ [#%d] A WAV file.\n", file_number^) } else { fmt.printf("š½ [#%d] %d WAV files.\n", file_number^, wav_count) } - append(job_list, strings.clone(path)) + append(&job_list, wav_files) file_number^ += 1 } return false } -walk_directory_os2 :: proc(path : string, file_number : ^int, job_list : ^[dynamic]string, depth : int = 0) { +walk_directory_os2 :: proc(path : string, file_number : ^int, depth : int = 0) { handle, ok := os2.open(path) if ok != os2.ERROR_NONE { indent_by(depth) @@ -772,7 +1029,7 @@ walk_directory_os2 :: proc(path : string, file_number : ^int, job_list : ^[dynam return } - wav_count := 0 + wav_files : [dynamic]string has_csv := false for file in files { @@ -782,7 +1039,7 @@ walk_directory_os2 :: proc(path : string, file_number : ^int, job_list : ^[dynam if os.is_dir(full_path) { indent_by(depth) fmt.printf("š %s\n", file.name) - walk_directory_os2(full_path, file_number, job_list, depth+1) // Recurse + walk_directory_os2(full_path, file_number, depth+1) // Recurse } else { // If file is actually a file @@ -791,15 +1048,16 @@ walk_directory_os2 :: proc(path : string, file_number : ^int, job_list : ^[dynam if extension == ".csv" { indent_by(depth) fmt.printf("š [#%d] %s\n", file_number^, file.name) - append(job_list, strings.clone(file.fullpath)) + append(&job_list, strings.clone(file.fullpath)) file_number^ += 1 has_csv = true } if extension == ".wav" { - wav_count += 1 + append(&wav_files, strings.clone(full_path)) } } } + wav_count := len(wav_files) if wav_count>0 && !has_csv { indent_by(depth+1) if wav_count == 1 { @@ -807,7 +1065,7 @@ walk_directory_os2 :: proc(path : string, file_number : ^int, job_list : ^[dynam } else { fmt.printf("š½ [#%d] %d WAV files.\n", file_number^, wav_count) } - append(job_list, strings.clone(path)) + append(&job_list, wav_files) file_number^ += 1 } }
\ No newline at end of file |