From 7c5a4bd34e286608d61735bd77f5635dc6bc227e Mon Sep 17 00:00:00 2001 From: San Jacobs Date: Sun, 21 Dec 2025 06:50:16 +0100 Subject: Major restructuring, GUI, icons, MacOS compatibility, fixes --- src/graphics/bg.png | Bin 0 -> 4858 bytes src/graphics/logo.icns | Bin 0 -> 35597 bytes src/graphics/logo.ico | Bin 0 -> 166515 bytes src/graphics/logo.png | Bin 0 -> 1744 bytes src/graphics/macos.rc | 1 + src/graphics/windows.rc | 1 + src/main.odin | 197 +++++++++++++++++++++++++++++++++++++++--------- src/parts/footer.html | 2 +- src/wav/wav.odin | 15 ++-- 9 files changed, 171 insertions(+), 45 deletions(-) create mode 100644 src/graphics/bg.png create mode 100644 src/graphics/logo.icns create mode 100644 src/graphics/logo.ico create mode 100644 src/graphics/logo.png create mode 100644 src/graphics/macos.rc create mode 100644 src/graphics/windows.rc (limited to 'src') diff --git a/src/graphics/bg.png b/src/graphics/bg.png new file mode 100644 index 0000000..c3bdb5a Binary files /dev/null and b/src/graphics/bg.png differ diff --git a/src/graphics/logo.icns b/src/graphics/logo.icns new file mode 100644 index 0000000..483e67a Binary files /dev/null and b/src/graphics/logo.icns differ diff --git a/src/graphics/logo.ico b/src/graphics/logo.ico new file mode 100644 index 0000000..ef93518 Binary files /dev/null and b/src/graphics/logo.ico differ diff --git a/src/graphics/logo.png b/src/graphics/logo.png new file mode 100644 index 0000000..4c9f213 Binary files /dev/null and b/src/graphics/logo.png differ diff --git a/src/graphics/macos.rc b/src/graphics/macos.rc new file mode 100644 index 0000000..e2eb31b --- /dev/null +++ b/src/graphics/macos.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON "logo.icns" \ No newline at end of file diff --git a/src/graphics/windows.rc b/src/graphics/windows.rc new file mode 100644 index 0000000..e681731 --- /dev/null +++ b/src/graphics/windows.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON "logo.ico" \ No newline at end of file diff --git a/src/main.odin b/src/main.odin index bc198b2..037d91d 100644 --- a/src/main.odin +++ b/src/main.odin @@ -7,6 +7,9 @@ import "core:path/filepath" import "core:sys/windows" import "core:strings" import "core:math" +import "core:slice" +import "core:mem" +import rl "vendor:raylib" import "wav" /* @@ -23,7 +26,7 @@ when ODIN_OS == .Windows { VERSION :: "1.6" -KNEKT := false +BRANDING : Brand = .NONE VERBOSE :: false INCLUDE_DATE :: false // By default I delete the retarded date field that says what day the report was generated. @@ -37,6 +40,9 @@ HEADER_TEMPLATE :: #load("header_template.txt", string) HEADER_FIELDS_FILENAME :: "info.txt" header_fields_file : string +BG_PNG :: #load("graphics/bg.png", []u8) +LOGO_PNG :: #load("graphics/logo.png", []u8) + Device :: enum { UNSET, ZOOM, @@ -51,6 +57,11 @@ Stages :: enum { BODY, } +Brand :: enum { + NONE, + KNEKT, +} + Info_Line :: struct { field : string, entry : string, @@ -79,41 +90,148 @@ 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() { + when ODIN_DEBUG { + track: mem.Tracking_Allocator + mem.tracking_allocator_init(&track, context.allocator) + context.allocator = mem.tracking_allocator(&track) + + defer { + if len(track.allocation_map) > 0 { + fmt.eprintf("=== %v allocations not freed: ===\n", len(track.allocation_map)) + for _, entry in track.allocation_map { + fmt.eprintf("- %v bytes @ %v\n", entry.size, entry.location) + } + } + if len(track.bad_free_array) > 0 { + fmt.eprintf("=== %v incorrect frees: ===\n", len(track.bad_free_array)) + for entry in track.bad_free_array { + fmt.eprintf("- %p @ %v\n", entry.memory, entry.location) + } + } + mem.tracking_allocator_destroy(&track) + } + } else { + rl.SetTraceLogLevel(.ERROR) + } + when ODIN_OS == .Windows { windows.SetConsoleOutputCP(windows.CODEPAGE.UTF8) } - input_file_name : string + fmt.printfln("Report v{} - Copyright (C) 2025 Sander J. Skjegstad\n", VERSION) + + exe_path, _ := os2.get_executable_directory(context.allocator) + exe_local_fields_file := fmt.aprint(exe_path, HEADER_FIELDS_FILENAME, sep=SEPARATOR) + + instant_run_paths : [dynamic]string if len(os.args) < 2 { - fmt.println("No paths submitted.") - if os.is_file(HEADER_FIELDS_FILENAME) { - fmt.printfln("\"%v\" already exists.", HEADER_FIELDS_FILENAME) + if os.is_file(exe_local_fields_file) { + when ODIN_DEBUG do fmt.printfln("\"%v\" exists.", exe_local_fields_file) } else { - os.write_entire_file(HEADER_FIELDS_FILENAME, transmute([]u8)HEADER_TEMPLATE) - fmt.printfln("Created \"%v\".", HEADER_FIELDS_FILENAME) + os.write_entire_file(exe_local_fields_file, transmute([]u8)HEADER_TEMPLATE) + fmt.printfln("Created \"%v\".", exe_local_fields_file) } - return - } else if len(os.args) > 2 { - for arg in os.args[2:] { - if arg=="-knekt" { - KNEKT = true + } else { + for arg in os.args[1:] { + switch arg { + case "-knekt": + BRANDING = .KNEKT + case: + append(&instant_run_paths, arg) } } } - exe_path, _ := os2.get_executable_directory(context.allocator) - exe_local_fields_file := fmt.aprint(exe_path, HEADER_FIELDS_FILENAME, sep=SEPARATOR) - fmt.println(exe_local_fields_file) + input_file_name : string + if os.is_file(exe_local_fields_file) { header_fields_file = exe_local_fields_file } - fmt.printf("Input path: {}\n", os.args[1]) - input_file_name = os.args[1] - path_info, error := os.stat(input_file_name) + if len(instant_run_paths)>0 { + // Basically, if user passed something in the command line, don't do the UI. + + for path in instant_run_paths { + scan(path) + free_all(context.temp_allocator) + } + return + } + + // Setting up raylib stuff + + cosmos_color : rl.Color = {30, 30, 30, 255} + current_color : rl.Color = cosmos_color + flash_color : rl.Color = {70, 50, 60, 255} + + width : i32 = 640 + height : i32 = 480 + bg_image := rl.LoadImageFromMemory(".png", raw_data(BG_PNG), i32(len(BG_PNG))) + logo_image := rl.LoadImageFromMemory(".png", raw_data(LOGO_PNG), i32(len(LOGO_PNG))) + + rl.InitWindow(width, height, "Better Report") + rl.SetWindowIcon(logo_image) + rl.SetWindowState({.WINDOW_RESIZABLE}) + rl.SetWindowMinSize(400, 300) + rl.SetTargetFPS(60) + rl.SetExitKey(.KEY_NULL) + bg := rl.LoadTextureFromImage(bg_image) + + for !rl.WindowShouldClose() { + + delta := rl.GetFrameTime() + // Input + //---------------------------------------------------------------------------------- + if rl.IsWindowResized() { + height = rl.GetScreenHeight() + width = rl.GetScreenWidth() + when ODIN_DEBUG { + fmt.println("Resized!", height, "x", width) + } + } + dropped_files := rl.IsFileDropped() + if dropped_files { + current_color = flash_color + } + + logo_left := (width-bg.width)/2 + logo_top := (height-bg.height)/2 + + + rl.BeginDrawing() + rl.ClearBackground(current_color) + rl.DrawTexture(bg, logo_left, logo_top, {255,255,255,20}) + rl.EndDrawing() + + current_color = rl.ColorLerp(current_color, cosmos_color, 0.05) + + if dropped_files { + files := rl.LoadDroppedFiles() + defer rl.UnloadDroppedFiles(files) + for i in 0.. (Report, bool) { // 888 888 888 8888b. 888 888 @@ -185,18 +300,18 @@ parse_folder :: proc(paths : Directory) -> (Report, bool) { output : Report = {} output.dir = filepath.dir(paths[0]) - wavs : [dynamic]wav.Wav + wavs := make([dynamic]wav.Wav, allocator=context.temp_allocator) max_channels := 0 for path, i in paths { - w, ok := wav.read(path) + w, ok := wav.read(path, allocator=context.temp_allocator) if ok { append(&wavs, w) max_channels = max(max_channels, w.channels) } } - header_build : [dynamic]string + header_build := make([dynamic]string, allocator=context.temp_allocator) append(&header_build, "Circled") append(&header_build, "File Name") append(&header_build, "Scene") @@ -231,9 +346,11 @@ parse_folder :: proc(paths : Directory) -> (Report, bool) { output.info_lines = make([]Info_Line, 64, context.temp_allocator) - // Header fields + previous_header_file := header_fields_file + defer header_fields_file = previous_header_file + header_file := header_fields_file local_header_file_path := fmt.tprint(output.dir, HEADER_FIELDS_FILENAME, sep=SEPARATOR) if os.is_file(local_header_file_path) { @@ -252,6 +369,11 @@ parse_folder :: proc(paths : Directory) -> (Report, bool) { if len(line)<2 { continue } + if len(line)==len("KNEKT") { + if line=="KNEKT" { + BRANDING=.KNEKT + } + } colon := strings.index_rune(line, ':') if colon==-1 { continue @@ -379,7 +501,7 @@ parse_folder :: proc(paths : Directory) -> (Report, bool) { } for did_change, i in changed { if (!did_change) && touched[i] { - field := fmt.aprintf("{}: ", output.header[i], allocator=context.temp_allocator) + field := fmt.tprintf("{}: ", output.header[i]) entry := prev_line[i] output.info_lines[output.info_line_count] = {field=field, entry=entry} output.info_line_count += 1 @@ -609,7 +731,7 @@ parse_file :: proc(path : CSV, device : Device = .UNSET) -> (Report, bool) { line_elements := strings.split(line, ",") when VERBOSE do fmt.printf(".INFO {}: {}\n", line_index, line_elements) - field := fmt.aprintf("{}:", line_elements[1], allocator=context.temp_allocator) + field := fmt.tprintf("{}:", line_elements[1]) entry := line_elements[2] output.info_lines[info_line_index].field = field output.info_lines[info_line_index].entry = entry @@ -628,7 +750,7 @@ parse_file :: proc(path : CSV, device : Device = .UNSET) -> (Report, bool) { output.info_line_count -= 1 continue } - field := fmt.aprintf("{}:", line_elements[0], allocator=context.temp_allocator) + field := fmt.tprintf("{}:", line_elements[0]) entry := line_elements[1] output.info_lines[info_line_index].field = field output.info_lines[info_line_index].entry = entry @@ -648,7 +770,7 @@ parse_file :: proc(path : CSV, device : Device = .UNSET) -> (Report, bool) { if element[:3] == "Trk" { if first_channel_index == -1 do first_channel_index = e last_channel_index = e - output.header[e] = fmt.aprintf("Trk {}", e-first_channel_index+1, allocator=context.temp_allocator) + output.header[e] = fmt.tprintf("Trk {}", e-first_channel_index+1) } if element == "Start TC" { output.tc_column_index = e @@ -954,13 +1076,13 @@ render :: proc(report : Report) { strings.write_string(&builder, PART_START) title := fmt.tprint(report.title, "- Sound Report") - logo := "" - if KNEKT { + bg := "" + if BRANDING==.KNEKT { title = fmt.tprint(report.title, "- Lydrapport") - logo = PART_KNEKT + bg = PART_KNEKT } strings.builder_replace_all(&builder, "¤¤¤TITLE¤¤¤", title) - strings.builder_replace_all(&builder, "¤¤¤LOGO¤¤¤", logo) + strings.builder_replace_all(&builder, "¤¤¤LOGO¤¤¤", bg) title_section := fmt.tprintf("

{}

", report.title) strings.write_string(&builder, title_section) @@ -1000,7 +1122,7 @@ render :: proc(report : Report) { strings.write_string(&builder, PART_END) footer := PART_FOOTER - if KNEKT { + if BRANDING == .KNEKT { footer = "" } strings.builder_replace_all(&builder, "¤¤¤FOOTER¤¤¤", footer) @@ -1013,7 +1135,7 @@ render :: proc(report : Report) { } output_path := "" - if KNEKT { + if BRANDING == .KNEKT { output_path = fmt.tprintf("{}{}{}_Knekt_Lydrapport.html", report.dir, SEPARATOR, report.title) } else { output_path = fmt.tprintf("{}{}{}_Sound_Report.html", report.dir, SEPARATOR, report.title) @@ -1033,12 +1155,12 @@ indent_by :: proc(i : int) { walk_directory :: proc(path : string, file_number : ^int, depth : int = 0) -> bool { handle, ok := os.open(path) + defer os.close(handle) if ok != os.ERROR_NONE { indent_by(depth) fmt.printf("ERROR opening dir: %s\n", path) return false } - defer os.close(handle) files, okr := os.read_dir(handle, -1, context.temp_allocator) if okr != os.ERROR_NONE { @@ -1051,6 +1173,7 @@ walk_directory :: proc(path : string, file_number : ^int, depth : int = 0) -> bo } wav_files : [dynamic]string + clear(&wav_files) has_csv := false for file in files { diff --git a/src/parts/footer.html b/src/parts/footer.html index d725a55..6ea1111 100644 --- a/src/parts/footer.html +++ b/src/parts/footer.html @@ -1,3 +1,3 @@
-

¤¤¤PROCESS¤¤¤ with 💜 using Report by SJS of Knekt Lyd
v¤¤¤VERSION¤¤¤

+

¤¤¤PROCESS¤¤¤ with 💜 using Better Report by SJS at Knekt Lyd
v¤¤¤VERSION¤¤¤

\ No newline at end of file diff --git a/src/wav/wav.odin b/src/wav/wav.odin index bfa11b1..89dd5eb 100644 --- a/src/wav/wav.odin +++ b/src/wav/wav.odin @@ -110,7 +110,7 @@ main :: proc() { /* Reads in the wav file metadata, without loading the sound data into ram. */ -read :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) #optional_ok { +read :: proc(path : string, allocator := context.allocator) -> (Wav, bool) #optional_ok { file : Wav file.path = path file.take = -1 @@ -126,11 +126,10 @@ read :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) #option return {}, false } - - temp_buf := new([BUFFER_SIZE]u8)[:] + temp_buf := make([]u8, BUFFER_SIZE) + defer delete(temp_buf) temp_bext : []u8 temp_ixml : string - defer delete(temp_buf) os.read(file.handle, temp_buf) @@ -318,7 +317,7 @@ read :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) #option */ interleave_set := false - xml_recurse :: proc(doc: ^xml.Document, element_id: xml.Element_ID, file: ^Wav, naming_channel: ^int, interleave_set: ^bool, allocator:=context.allocator, indent := 0) { + xml_recurse :: proc(doc: ^xml.Document, element_id: xml.Element_ID, file: ^Wav, naming_channel: ^int, interleave_set: ^bool, allocator:type_of(context.allocator), indent := 0) { naming_channel := naming_channel interleave_set := interleave_set @@ -448,6 +447,9 @@ read :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) #option parsed_ixml : ^xml.Document + prev_alloc := context.allocator + defer context.allocator = prev_alloc + context.allocator = context.temp_allocator parsed_ixml, _ = xml.parse(temp_ixml, xml.Options{ flags={.Ignore_Unsupported}, expected_doctype = "", @@ -469,7 +471,7 @@ read :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) #option description := string(temp_bext[:256]) for line in strings.split_lines(description) { - + if len(line)<1 do continue if file.channel_names[naming_channel] == "" && (strings.starts_with(line, "sTRK") || strings.starts_with(line, "zTRK")) { eq_index := strings.index(line, "=") @@ -545,7 +547,6 @@ read :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) #option // just here to make some printing prettier temp_bext = nil - temp_buf = nil return file, true } -- cgit v1.2.1