aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/graphics/bg.pngbin0 -> 4858 bytes
-rw-r--r--src/graphics/logo.icnsbin0 -> 35597 bytes
-rw-r--r--src/graphics/logo.icobin0 -> 166515 bytes
-rw-r--r--src/graphics/logo.pngbin0 -> 1744 bytes
-rw-r--r--src/graphics/macos.rc1
-rw-r--r--src/graphics/windows.rc1
-rw-r--r--src/main.odin221
-rw-r--r--src/parts/footer.html2
-rw-r--r--src/wav/wav.odin16
9 files changed, 190 insertions, 51 deletions
diff --git a/src/graphics/bg.png b/src/graphics/bg.png
new file mode 100644
index 0000000..c3bdb5a
--- /dev/null
+++ b/src/graphics/bg.png
Binary files differ
diff --git a/src/graphics/logo.icns b/src/graphics/logo.icns
new file mode 100644
index 0000000..483e67a
--- /dev/null
+++ b/src/graphics/logo.icns
Binary files differ
diff --git a/src/graphics/logo.ico b/src/graphics/logo.ico
new file mode 100644
index 0000000..ef93518
--- /dev/null
+++ b/src/graphics/logo.ico
Binary files differ
diff --git a/src/graphics/logo.png b/src/graphics/logo.png
new file mode 100644
index 0000000..4c9f213
--- /dev/null
+++ b/src/graphics/logo.png
Binary files 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 e1b72ac..baf12d2 100644
--- a/src/main.odin
+++ b/src/main.odin
@@ -7,12 +7,13 @@ 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"
/*
-TODO: Simplify pre-allocation. Just allocate a bunch. It's probably fine.
- Maybe do it by just counting how many lines are longer than 2 characters.
-TODO: Drag-n-drop window if no files are specified
+TODO: Test on files from SD788t
*/
when ODIN_OS == .Windows {
@@ -21,9 +22,9 @@ when ODIN_OS == .Windows {
SEPARATOR :: "/"
}
-VERSION :: "1.6"
+VERSION :: "1.7"
-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 +38,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 +55,11 @@ Stages :: enum {
BODY,
}
+Brand :: enum {
+ NONE,
+ KNEKT,
+}
+
Info_Line :: struct {
field : string,
entry : string,
@@ -76,44 +85,163 @@ CSV :: string
Directory :: [dynamic]string
Job :: union {CSV, Directory}
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
+
+ info_txt, info_txt_ok := os.read_entire_file(header_fields_file, context.temp_allocator)
+ if info_txt_ok {
+ it := string(info_txt)
+ for line in strings.split_lines_iterator(&it) {
+ if len(line)==len("KNEKT") {
+ if line=="KNEKT" {
+ BRANDING=.KNEKT
+ break
+ }
+ }
+ }
+ }
+ }
+
+ 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
}
- fmt.printf("Input path: {}\n", os.args[1])
- input_file_name = os.args[1]
- path_info, error := os.stat(input_file_name)
+ // 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..<files.count {
+ path := string(files.paths[i])
+ scan(path)
+ free_all(context.temp_allocator)
+ clear(&job_list)
+ }
+ }
+
+ }
+ delete(job_list)
+ rl.CloseWindow()
+}
+
+
+scan :: proc(input_path : string) {
+ fmt.printf("Input path: {}\n", input_path)
+ path_info, error := os.stat(input_path)
+
+ previous_header_file := header_fields_file
+ defer header_fields_file = previous_header_file
file_count := 1
files_done := 0
+
if error == os.ERROR_NONE {
if(path_info.is_dir) {
@@ -162,18 +290,15 @@ main :: proc() {
}
}
render(parsed)
- free_all(context.temp_allocator)
files_done += 1
}
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)
+ fmt.printf("ERROR could not get path info for: {}\n", input_path)
}
-
}
-
parse_folder :: proc(paths : Directory) -> (Report, bool) {
// 888 888 888 8888b. 888 888
@@ -185,18 +310,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 +356,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 +379,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 +511,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 +741,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 +760,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 +780,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 +1086,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("<h1 class=\"day-title\">{}</h1>", report.title)
strings.write_string(&builder, title_section)
@@ -1000,7 +1132,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 +1145,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,22 +1165,25 @@ 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 {
indent_by(depth)
fmt.printf("ERROR [{}] reading dir: %s\n", okr, path)
- if okr == os.ERROR_FILE_IS_NOT_DIR do return true
+ when ODIN_OS == .Windows {
+ if okr == os.ERROR_FILE_IS_NOT_DIR do return true
+ }
return true
}
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 @@
<center>
- <p style="font-size:10px;opacity:70%">¤¤¤PROCESS¤¤¤ with 💜 using <a target="_blank" rel="noopener noreferrer" href="https://sparkburst.net/software/#Report">Report</a> by SJS of <a target="_blank" rel="noopener noreferrer" href="https://knektlyd.no/">Knekt Lyd</a><br>v¤¤¤VERSION¤¤¤</p>
+ <p style="font-size:10px;opacity:70%">¤¤¤PROCESS¤¤¤ with 💜 using <a target="_blank" rel="noopener noreferrer" href="https://sparkburst.net/software/#better-report">Better Report</a> by SJS at <a target="_blank" rel="noopener noreferrer" href="https://knektlyd.no/">Knekt Lyd</a><br>v¤¤¤VERSION¤¤¤</p>
</center> \ No newline at end of file
diff --git a/src/wav/wav.odin b/src/wav/wav.odin
index bfa11b1..0d148c7 100644
--- a/src/wav/wav.odin
+++ b/src/wav/wav.odin
@@ -9,6 +9,7 @@ import "xml"
/*
TODO: Support RF64
+TODO: Test on files from SD788t
TODO: Support music metadata
*/
@@ -110,7 +111,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 +127,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 +318,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 +448,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 +472,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 +548,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
}