From 57a77f38e001fccfa60d8f4cff0100f1d2522779 Mon Sep 17 00:00:00 2001 From: luboslenco Date: Sun, 5 Jul 2026 17:24:59 +0200 Subject: [PATCH] paint: add sounds tab --- base/sources/engine.c | 11 +- base/sources/engine.h | 34 ++--- base/sources/iron.h | 5 +- base/sources/iron_audio.c | 10 +- base/sources/iron_audio.h | 14 +- base/sources/iron_path.c | 22 ++-- base/sources/iron_path.h | 2 +- paint/project.js | 2 +- paint/sources/context.c | 16 +++ paint/sources/functions.h | 7 +- paint/sources/globals.h | 5 +- paint/sources/io/export_arm.c | 26 +++- paint/sources/io/import_arm.c | 16 +++ paint/sources/io/import_asset.c | 4 +- paint/sources/io/import_audio.c | 14 -- paint/sources/io/import_legacy.c | 15 ++- paint/sources/io/import_sound.c | 22 ++++ paint/sources/main.c | 5 +- paint/sources/minic_api.c | 4 + paint/sources/slot_sound.c | 20 +++ paint/sources/types.h | 17 +++ paint/sources/ui/tab_sounds.c | 220 +++++++++++++++++++++++++++++++ paint/sources/ui/ui_base.c | 5 +- paint/sources/util/util_encode.c | 4 +- 24 files changed, 419 insertions(+), 81 deletions(-) delete mode 100644 paint/sources/io/import_audio.c create mode 100644 paint/sources/io/import_sound.c create mode 100644 paint/sources/slot_sound.c create mode 100644 paint/sources/ui/tab_sounds.c diff --git a/base/sources/engine.c b/base/sources/engine.c index c9d58a97..929e5eec 100644 --- a/base/sources/engine.c +++ b/base/sources/engine.c @@ -1815,7 +1815,7 @@ any_map_t *data_cached_materials = NULL; any_map_t *data_cached_worlds = NULL; any_map_t *data_cached_shaders = NULL; any_map_t *data_cached_blobs = NULL; -any_map_t *data_cached_textures = NULL; +any_map_t *data_cached_textures = NULL; any_map_t *data_cached_videos = NULL; any_map_t *data_cached_fonts = NULL; any_map_t *data_cached_sounds = NULL; @@ -2013,7 +2013,6 @@ draw_font_t *data_get_font(char *file) { return b; } -#ifdef IRON_AUDIO sound_t *data_get_sound(char *file) { if (data_cached_sounds == NULL) { data_cached_sounds = any_map_create(); @@ -2023,8 +2022,7 @@ sound_t *data_get_sound(char *file) { if (cached != NULL) { return cached; } - sound_t *b = gc_alloc(sizeof(sound_t)); - b->sound_ = iron_load_sound(data_resolve_path(file)); + sound_t *b = iron_load_sound(data_resolve_path(file)); any_map_set(data_cached_sounds, file, b); data_assets_loaded++; return b; @@ -2038,10 +2036,11 @@ void data_delete_sound(char *handle) { if (sound == NULL) { return; } - iron_a1_sound_destroy(sound->sound_); +#ifdef IRON_AUDIO + iron_a1_sound_destroy(sound); +#endif map_delete(data_cached_sounds, handle); } -#endif void data_delete_mesh(char *handle) { if (data_cached_meshes == NULL) { diff --git a/base/sources/engine.h b/base/sources/engine.h index 9eebbc3c..ae0dd508 100644 --- a/base/sources/engine.h +++ b/base/sources/engine.h @@ -536,9 +536,7 @@ void uniforms_set_material_const(i32 location, shader_const_t *shade // ██████╔╝██║ ██║ ██║ ██║ ██║ // ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ -typedef struct sound { - void *sound_; -} sound_t; +typedef void sound_t; extern any_map_t *data_cached_scene_raws; extern any_map_t *data_cached_meshes; @@ -550,10 +548,8 @@ extern any_map_t *data_cached_blobs; extern any_map_t *data_cached_textures; extern any_map_t *data_cached_videos; extern any_map_t *data_cached_fonts; -#ifdef IRON_AUDIO extern any_map_t *data_cached_sounds; -#endif -extern i32 data_assets_loaded; +extern i32 data_assets_loaded; mesh_data_t *data_get_mesh(char *file, char *name); camera_data_t *data_get_camera(char *file, char *name); @@ -565,21 +561,17 @@ gpu_texture_t *data_get_texture(char *file); buffer_t *data_get_blob(char *file); video_t *data_get_video(char *file); draw_font_t *data_get_font(char *file); -#ifdef IRON_AUDIO -sound_t *data_get_sound(char *file); -#endif -void data_delete_mesh(char *handle); -void data_delete_blob(char *handle); -void data_delete_texture(char *handle); -void data_delete_video(char *handle); -void data_delete_font(char *handle); -#ifdef IRON_AUDIO -void data_delete_sound(char *handle); -#endif -bool data_is_abs(char *file); -bool data_is_up(char *file); -char *data_resolve_path(char *file); -char *data_path(void); +sound_t *data_get_sound(char *file); +void data_delete_mesh(char *handle); +void data_delete_blob(char *handle); +void data_delete_texture(char *handle); +void data_delete_video(char *handle); +void data_delete_font(char *handle); +void data_delete_sound(char *handle); +bool data_is_abs(char *file); +bool data_is_up(char *file); +char *data_resolve_path(char *file); +char *data_path(void); // ███████╗ ██████╗███████╗███╗ ██╗███████╗ // ██╔════╝██╔════╝██╔════╝████╗ ██║██╔════╝ diff --git a/base/sources/iron.h b/base/sources/iron.h index f15a6152..994f7516 100644 --- a/base/sources/iron.h +++ b/base/sources/iron.h @@ -867,13 +867,14 @@ gpu_texture_t *iron_load_texture(char *file) { return gpu_create_texture_from_encoded_bytes(&buf, file); } -#ifdef IRON_AUDIO void *iron_load_sound(char *file) { +#ifdef IRON_AUDIO iron_a1_init(); iron_a1_sound_t *sound = iron_a1_sound_create(file); return sound; -} #endif + return NULL; +} buffer_t *iron_load_blob(char *file) { #ifdef WITH_EMBED diff --git a/base/sources/iron_audio.c b/base/sources/iron_audio.c index 13ce4ffb..e109ce7a 100644 --- a/base/sources/iron_audio.c +++ b/base/sources/iron_audio.c @@ -17,6 +17,12 @@ #include #include +void iron_a2_init(void); +void iron_a2_set_callback(void (*iron_a2_audio_callback)(iron_a2_buffer_t *buffer, uint32_t samples, void *userdata), void *userdata); +uint32_t iron_a2_samples_per_second(void); +void iron_a2_set_sample_rate_callback(void (*iron_a2_sample_rate_callback)(void *userdata), void *userdata); +void iron_a2_internal_sample_rate_callback(void); + struct iron_a1_channel { iron_a1_sound_t *sound; float position; @@ -143,7 +149,9 @@ void iron_a1_init(void) { iron_a2_set_callback(iron_a2_on_a1_mix, NULL); } -iron_a1_channel_t *iron_a1_play_sound(iron_a1_sound_t *sound, bool loop, float pitch, bool unique) { +iron_a1_channel_t *audio_play(iron_a1_sound_t *sound, bool loop) { + float pitch = 1.0; + bool unique = false; iron_a1_channel_t *channel = NULL; iron_mutex_lock(&mutex); bool found = false; diff --git a/base/sources/iron_audio.h b/base/sources/iron_audio.h index 0eaf7bca..bd000a46 100644 --- a/base/sources/iron_audio.h +++ b/base/sources/iron_audio.h @@ -47,7 +47,7 @@ struct iron_a1_stream_channel; typedef struct iron_a1_stream_channel iron_a1_stream_channel_t; void iron_a1_init(void); -iron_a1_channel_t *iron_a1_play_sound(iron_a1_sound_t *sound, bool loop, float pitch, bool unique); +iron_a1_channel_t *audio_play(iron_a1_sound_t *sound, bool loop); void iron_a1_stop_sound(iron_a1_sound_t *sound); void iron_a1_play_sound_stream(iron_a1_sound_stream_t *stream); void iron_a1_stop_sound_stream(iron_a1_sound_stream_t *stream); @@ -71,15 +71,9 @@ float iron_a1_sound_stream_position(iron_a1_sound_stream_t *st void iron_a1_sound_stream_reset(iron_a1_sound_stream_t *stream); float iron_a1_sound_stream_volume(iron_a1_sound_stream_t *stream); void iron_a1_sound_stream_set_volume(iron_a1_sound_stream_t *stream, float value); - -void iron_a2_init(void); -void iron_a2_set_callback(void (*iron_a2_audio_callback)(iron_a2_buffer_t *buffer, uint32_t samples, void *userdata), void *userdata); -uint32_t iron_a2_samples_per_second(void); -void iron_a2_set_sample_rate_callback(void (*iron_a2_sample_rate_callback)(void *userdata), void *userdata); -void iron_a2_shutdown(void); -void iron_a2_internal_init(void); -bool iron_a2_internal_callback(iron_a2_buffer_t *buffer, int samples); -void iron_a2_internal_sample_rate_callback(void); +void iron_a2_internal_init(void); +void iron_a2_shutdown(void); +bool iron_a2_internal_callback(iron_a2_buffer_t *buffer, int samples); // struct iron_internal_video_channel; // typedef struct iron_internal_video_channel iron_internal_video_channel_t; diff --git a/base/sources/iron_path.c b/base/sources/iron_path.c index d441fad6..bf20b192 100644 --- a/base/sources/iron_path.c +++ b/base/sources/iron_path.c @@ -14,7 +14,7 @@ bool path_is_protected_linux = false; string_array_t *_path_mesh_formats = NULL; string_array_t *_path_texture_formats = NULL; -string_array_t *_path_audio_formats = NULL; +string_array_t *_path_sound_formats = NULL; static string_array_t *_path_base_color_ext = NULL; static string_array_t *_path_opacity_ext = NULL; static string_array_t *_path_normal_map_ext = NULL; @@ -50,14 +50,14 @@ string_array_t *path_texture_formats(void) { return _path_texture_formats; } -string_array_t *path_audio_formats(void) { - if (_path_audio_formats == NULL) { - _path_audio_formats = string_array_create(0); - gc_root(_path_audio_formats); - string_array_push(_path_audio_formats, "wav"); - string_array_push(_path_audio_formats, "ogg"); +string_array_t *path_sound_formats(void) { + if (_path_sound_formats == NULL) { + _path_sound_formats = string_array_create(0); + gc_root(_path_sound_formats); + string_array_push(_path_sound_formats, "wav"); + string_array_push(_path_sound_formats, "ogg"); } - return _path_audio_formats; + return _path_sound_formats; } string_array_t *path_base_color_ext(void) { @@ -251,9 +251,9 @@ bool path_is_texture(char *path) { return false; } -bool path_is_audio(char *path) { +bool path_is_sound(char *path) { char *p = to_lower_case(path); - string_array_t *formats = path_audio_formats(); + string_array_t *formats = path_sound_formats(); for (uint32_t i = 0; i < formats->length; ++i) { char *s = formats->buffer[i]; char *ext = string(".%s", s); @@ -300,7 +300,7 @@ bool path_is_lut(char *path) { } bool path_is_known(char *path) { - return path_is_mesh(path) || path_is_texture(path) || path_is_audio(path) || path_is_font(path) || path_is_project(path) || path_is_plugin(path) || + return path_is_mesh(path) || path_is_texture(path) || path_is_sound(path) || path_is_font(path) || path_is_project(path) || path_is_plugin(path) || path_is_text(path) || path_is_ext_format(path) || path_is_lut(path); } diff --git a/base/sources/iron_path.h b/base/sources/iron_path.h index a5507f81..9378049b 100644 --- a/base/sources/iron_path.h +++ b/base/sources/iron_path.h @@ -25,7 +25,7 @@ char *path_base_dir(char *path); char *path_base_name(char *path); bool path_is_mesh(char *path); bool path_is_texture(char *path); -bool path_is_audio(char *path); +bool path_is_sound(char *path); bool path_is_font(char *path); bool path_is_project(char *path); bool path_is_plugin(char *path); diff --git a/paint/project.js b/paint/project.js index 18c65214..b485d2e1 100644 --- a/paint/project.js +++ b/paint/project.js @@ -14,7 +14,7 @@ flags.with_plugins = true; flags.with_kong = true; flags.with_raytrace = true; flags.with_bc7 = true; -flags.with_audio = false; // platform == "linux"; +flags.with_audio = platform == "linux"; flags.idle_sleep = true; flags.export_version_info = true; flags.export_data_list = platform == "android"; // .apk contents diff --git a/paint/sources/context.c b/paint/sources/context.c index d5ac20cb..5e514a83 100644 --- a/paint/sources/context.c +++ b/paint/sources/context.c @@ -243,6 +243,22 @@ void context_select_font(i32 i) { context_set_font(g_project->_->fonts->buffer[i]); } +void context_set_sound(slot_sound_t *s) { + if (array_index_of(g_project->_->sounds, s) == -1) { + return; + } + g_context->sound = s; + ui_base_hwnds->buffer[TAB_AREA_STATUS]->redraws = 2; + ui_view2d_hwnd->redraws = 2; +} + +void context_select_sound(i32 i) { + if (g_project->_->sounds->length <= i) { + return; + } + context_set_sound(g_project->_->sounds->buffer[i]); +} + void context_select_layer(i32 i) { if (g_project->_->layers->length <= i) { return; diff --git a/paint/sources/functions.h b/paint/sources/functions.h index 6fe2a813..d0748b16 100644 --- a/paint/sources/functions.h +++ b/paint/sources/functions.h @@ -82,6 +82,7 @@ void layers_on_resized(); void layers_create_filter(); void layers_create_image_layer(asset_t *asset); slot_font_t *slot_font_create(char *name, draw_font_t *font, char *file); +slot_sound_t *slot_sound_create(char *name, sound_t *sound, char *file); scene_t *startup_get_scene(void); buffer_t *util_encode_scene(scene_t *raw); buffer_t *util_encode_project(project_t *raw); @@ -229,7 +230,10 @@ void context_select_material(i32 i); void context_set_material(slot_material_t *m); void context_select_brush(i32 i); void context_set_brush(slot_brush_t *b); +void context_set_font(slot_font_t *f); void context_select_font(i32 i); +void context_set_sound(slot_sound_t *s); +void context_select_sound(i32 i); void context_select_layer(i32 i); void context_set_layer(slot_layer_t *l); void context_select_tool(i32 i); @@ -335,7 +339,7 @@ void render_path_preview_commands_decal(); node_shader_context_t *make_paint_run(material_t *data, material_context_t *matcon); node_shader_context_t *make_mesh_preview_run(material_t *data, material_context_t *matcon, bool viewport); material_data_t *make_mesh_preview_viewport(slot_material_t *slot); -void import_audio_run(char *path); +void import_sound_run(char *path); void import_mesh_run(char *path, bool _clear_layers, bool replace_existing, bool keep_camera); void import_mesh_finish_import(void *_); void import_mesh_make_mesh(raw_mesh_t *mesh); @@ -377,6 +381,7 @@ void viewport_save_texture(gpu_texture_t *screenshot); void viewport_capture_video_begin(); void viewport_capture_video_end(); void tab_fonts_draw(ui_handle_t *htab); +void tab_sounds_draw(ui_handle_t *htab); physics_world_t *physics_world_create(); void physics_world_update(physics_world_t *world); physics_pair_t_array_t *physics_world_get_contact_pairs(physics_world_t *world, physics_body_t *body); diff --git a/paint/sources/globals.h b/paint/sources/globals.h index 0a72bb5a..b1abc2d9 100644 --- a/paint/sources/globals.h +++ b/paint/sources/globals.h @@ -7,7 +7,7 @@ char *manifest_title = "ArmorPaint"; char *manifest_version = "1.0 alpha"; -char *manifest_version_project = "10"; +char *manifest_version_project = "11"; char *manifest_version_config = "1"; char *manifest_url = "https://armorpaint.org"; char *manifest_url_android = "https://play.google.com/store/apps/details?id=org.armorpaint"; @@ -135,6 +135,7 @@ slot_material_t *base_drag_material = NULL; slot_layer_t *base_drag_layer = NULL; slot_brush_t *base_drag_brush = NULL; slot_font_t *base_drag_font = NULL; +slot_sound_t *base_drag_sound = NULL; mesh_object_t *base_drag_mesh = NULL; bool base_player_lock = false; @@ -196,7 +197,7 @@ f32 ui_view2d_pan_scale = 1.0; bool ui_view2d_tiled_show = false; bool ui_view2d_grid_redraw = true; ui_handle_t *ui_view2d_htab; -bool sim_running = false; +bool sim_running = false; bool sim_record = false; bool viewport_recording = false; bool node_shader_dump_to_script = false; diff --git a/paint/sources/io/export_arm.c b/paint/sources/io/export_arm.c index 84e90f07..c4457c44 100644 --- a/paint/sources/io/export_arm.c +++ b/paint/sources/io/export_arm.c @@ -95,6 +95,26 @@ string_array_t *export_arm_fonts_to_files(char *project_path, slot_font_t_array_ return font_files; } +string_array_t *export_arm_sounds_to_files(char *project_path, slot_sound_t_array_t *sounds) { + string_array_t *sound_files = any_array_create_from_raw((void *[]){}, 0); + for (i32 i = 0; i < sounds->length; ++i) { + slot_sound_t *f = sounds->buffer[i]; +#ifdef IRON_IOS + bool same_drive = false; +#else + bool same_drive = char_at(project_path, 0) == char_at(f->file, 0); +#endif + // Convert sound path from absolute to relative + if (same_drive) { + any_array_push(sound_files, path_to_relative(project_path, f->file)); + } + else { + any_array_push(sound_files, f->file); + } + } + return sound_files; +} + void export_arm_run_project() { tab_timeline_prepare_save(); @@ -172,8 +192,9 @@ void export_arm_run_project() { string_array_t *texture_files = export_arm_assets_to_files(g_project->_->filepath, g_project->_->assets); - string_array_t *font_files = export_arm_fonts_to_files(g_project->_->filepath, g_project->_->fonts); - string_array_t *mesh_files = export_arm_meshes_to_files(g_project->_->filepath); + string_array_t *font_files = export_arm_fonts_to_files(g_project->_->filepath, g_project->_->fonts); + string_array_t *sound_files = export_arm_sounds_to_files(g_project->_->filepath, g_project->_->sounds); + string_array_t *mesh_files = export_arm_meshes_to_files(g_project->_->filepath); i32 bits_pos = base_bits_handle->i; i32 bpp = bits_pos == TEXTURE_BITS_BITS8 ? 8 : bits_pos == TEXTURE_BITS_BITS16 ? 16 : 32; @@ -288,6 +309,7 @@ void export_arm_run_project() { g_project->brush_nodes = bnodes; g_project->layer_datas = ld; g_project->font_assets = font_files; + g_project->sound_assets = sound_files; g_project->mesh_assets = mesh_files; tab_timeline_export(g_project); diff --git a/paint/sources/io/import_arm.c b/paint/sources/io/import_arm.c index 6d18de6d..249fe147 100644 --- a/paint/sources/io/import_arm.c +++ b/paint/sources/io/import_arm.c @@ -359,6 +359,22 @@ void import_arm_run_project(char *path) { } } + if (g_project->sound_assets != NULL) { + for (i32 i = 0; i < g_project->sound_assets->length; ++i) { + char *file = g_project->sound_assets->buffer[i]; +#ifdef IRON_WINDOWS + file = string_copy(string_replace_all(file, "/", "\\")); +#else + file = string_copy(string_replace_all(file, "\\", "/")); +#endif + // Convert sound path from relative to absolute + char *abs = data_is_abs(file) ? file : string("%s%s", base, file); + if (iron_file_exists(abs)) { + import_sound_run(abs); + } + } + } + mesh_data_t *md = mesh_data_create(g_project->mesh_datas->buffer[0]); mesh_object_set_data(g_context->paint_object, md); diff --git a/paint/sources/io/import_asset.c b/paint/sources/io/import_asset.c index 4b284fa8..dc8d1220 100644 --- a/paint/sources/io/import_asset.c +++ b/paint/sources/io/import_asset.c @@ -63,8 +63,8 @@ void import_asset_run(char *path, f32 drop_x, f32 drop_y, bool show_box, bool hd g_context->ddirty = 2; } } - else if (path_is_audio(path)) { - import_audio_run(path); + else if (path_is_sound(path)) { + import_sound_run(path); } else if (path_is_lut(path)) { box_preferences_lut_picked(path); diff --git a/paint/sources/io/import_audio.c b/paint/sources/io/import_audio.c deleted file mode 100644 index 4754b72a..00000000 --- a/paint/sources/io/import_audio.c +++ /dev/null @@ -1,14 +0,0 @@ - -#include "../global.h" - -void import_audio_run(char *path) { - -#ifdef IRON_AUDIO - sound_t *s = data_get_sound(path); - iron_a1_play_sound(s->sound_, false, 1, false); - - string_array_t *ar = string_split(path, PATH_SEP); - char *name = ar->buffer[ar->length - 1]; - console_info(string("%s %s", tr("Audio imported:"), name)); -#endif -} diff --git a/paint/sources/io/import_legacy.c b/paint/sources/io/import_legacy.c index 42aeedd4..f0f7beb3 100644 --- a/paint/sources/io/import_legacy.c +++ b/paint/sources/io/import_legacy.c @@ -172,7 +172,8 @@ project_t *import_arm_from_map_to_arm(any_map_t *old) { } } - project->font_assets = any_map_get(old, "font_assets"); + project->font_assets = any_map_get(old, "font_assets"); + project->sound_assets = any_map_get(old, "sound_assets"); any_array_t *lds = any_map_get(old, "layer_datas"); if (lds != NULL) { @@ -299,6 +300,11 @@ project_t *import_arm_from_map_to_arm(any_map_t *old) { return project; } +project_t *import_arm_from_version_10(any_map_t *old) { + any_map_set(old, "sound_assets", NULL); + return import_arm_from_map_to_arm(old); +} + project_t *import_arm_from_version_9(any_map_t *old) { any_array_t *lds = any_map_get(old, "layer_datas"); if (lds != NULL) { @@ -307,7 +313,7 @@ project_t *import_arm_from_version_9(any_map_t *old) { any_map_set(ld, "texpaint_sculpt", NULL); } } - return import_arm_from_map_to_arm(old); + return import_arm_from_version_10(old); } project_t *import_arm_from_version_8(any_map_t *old) { @@ -412,8 +418,9 @@ project_t *import_arm_from_version_0(any_map_t *old) { project_t *import_arm_from_old(buffer_t *b) { any_map_t *old = armpack_decode_to_map(b); project_t *(*fns[])(any_map_t *) = { - import_arm_from_version_0, import_arm_from_version_1, import_arm_from_version_2, import_arm_from_version_3, import_arm_from_version_4, - import_arm_from_version_5, import_arm_from_version_6, import_arm_from_version_7, import_arm_from_version_8, import_arm_from_version_9, + import_arm_from_version_0, import_arm_from_version_1, import_arm_from_version_2, import_arm_from_version_3, + import_arm_from_version_4, import_arm_from_version_5, import_arm_from_version_6, import_arm_from_version_7, + import_arm_from_version_8, import_arm_from_version_9, import_arm_from_version_10, }; for (i32 v = sizeof(fns) / sizeof(fns[0]) - 1; v >= 0; --v) { if (import_arm_is_version(b, i32_to_string(v))) { diff --git a/paint/sources/io/import_sound.c b/paint/sources/io/import_sound.c new file mode 100644 index 00000000..a5d644ae --- /dev/null +++ b/paint/sources/io/import_sound.c @@ -0,0 +1,22 @@ + +#include "../global.h" + +void import_sound_run(char *path) { + for (i32 i = 0; i < g_project->_->sounds->length; ++i) { + slot_font_t *f = g_project->_->sounds->buffer[i]; + if (string_equals(f->file, path)) { + console_info(strings_asset_already_imported()); + return; + } + } + + string_array_t *ar = string_split(path, PATH_SEP); + char *name = ar->buffer[ar->length - 1]; + + sound_t *sound = data_get_sound(path); + slot_font_t *s = slot_sound_create(name, sound, path); + g_context->sound = s; + any_array_push(g_project->_->sounds, s); + + console_info(string("%s %s", tr("Sound imported:"), name)); +} diff --git a/paint/sources/main.c b/paint/sources/main.c index 4e98793e..efbaf7f1 100644 --- a/paint/sources/main.c +++ b/paint/sources/main.c @@ -9,7 +9,6 @@ #include "io/export_texture.c" #include "io/import_arm.c" #include "io/import_asset.c" -#include "io/import_audio.c" #include "io/import_blend_material.c" #include "io/import_blend_mesh.c" #include "io/import_envmap.c" @@ -21,6 +20,7 @@ #include "io/import_mesh.c" #include "io/import_obj.c" #include "io/import_plugin.c" +#include "io/import_sound.c" #include "io/import_texture.c" #include "io/import_theme.c" @@ -156,6 +156,7 @@ #include "ui/tab_meshes.c" #include "ui/tab_plugins.c" #include "ui/tab_scripts.c" +#include "ui/tab_sounds.c" #include "ui/tab_swatches.c" #include "ui/tab_textures.c" #include "ui/tab_timeline.c" @@ -223,6 +224,7 @@ #include "slot_font.c" #include "slot_layer.c" #include "slot_material.c" +#include "slot_sound.c" #include "strings.c" #include "translator.c" #include "uniforms.c" @@ -388,6 +390,7 @@ void _kickstart() { g_project->_->brushes = any_array_create_from_raw((void *[]){}, 0); g_project->_->layers = any_array_create_from_raw((void *[]){}, 0); g_project->_->fonts = any_array_create_from_raw((void *[]){}, 0); + g_project->_->sounds = any_array_create_from_raw((void *[]){}, 0); ui_view2d_hwnd = ui_handle_create(); gc_root(ui_view2d_hwnd); diff --git a/paint/sources/minic_api.c b/paint/sources/minic_api.c index 23af4976..fb24ac8f 100644 --- a/paint/sources/minic_api.c +++ b/paint/sources/minic_api.c @@ -816,6 +816,7 @@ void minic_register_builtins() { R(data_get_blob, "p(p)"); R(data_get_video, "p(p)"); R(data_get_font, "p(p)"); + R(data_get_sound, "p(p)"); R(data_delete_mesh, "v(p)"); R(data_delete_blob, "v(p)"); R(data_delete_texture, "v(p)"); @@ -999,6 +1000,9 @@ void minic_register_builtins() { R(draw_circle, "v(f,f,f,i,f)"); R(draw_cubic_bezier, "v(p,p,i,f)"); + // iron_audio + R(audio_play, "v(p,i)"); + // iron_string R(string_alloc, "p(i)"); R(string_copy, "p(p)"); diff --git a/paint/sources/slot_sound.c b/paint/sources/slot_sound.c new file mode 100644 index 00000000..72277451 --- /dev/null +++ b/paint/sources/slot_sound.c @@ -0,0 +1,20 @@ + +#include "global.h" + +slot_sound_t *slot_sound_create(char *name, sound_t *sound, char *file) { + slot_sound_t *raw = GC_ALLOC_INIT(slot_sound_t, {0}); + raw->id = 0; + + for (i32 i = 0; i < g_project->_->sounds->length; ++i) { + slot_sound_t *slot = g_project->_->sounds->buffer[i]; + if (slot->id >= raw->id) { + raw->id = slot->id + 1; + } + } + + raw->name = string_copy(name); + raw->sound = sound; + raw->file = string_copy(file); + + return raw; +} diff --git a/paint/sources/types.h b/paint/sources/types.h index 46caa7b3..9a48b84a 100644 --- a/paint/sources/types.h +++ b/paint/sources/types.h @@ -63,6 +63,14 @@ typedef struct slot_font { char *file; } slot_font_t; +typedef struct slot_sound { + struct gpu_texture *image; // 200px + i32 id; + sound_t *sound; + char *name; + char *file; +} slot_sound_t; + typedef struct config { char *version; char *sha; // Commit id @@ -250,6 +258,7 @@ typedef struct context { struct slot_layer *layer; struct slot_brush *brush; struct slot_font *font; + struct slot_sound *sound; tool_type_t tool; bool layer_preview_dirty; bool layers_preview_dirty; @@ -532,6 +541,7 @@ typedef struct { struct slot_brush_t_array *brushes; struct slot_layer_t_array *layers; struct slot_font_t_array *fonts; + struct slot_sound_t_array *sounds; struct node_group_t_array *material_groups; char *filepath; i32 next_asset_id; @@ -558,6 +568,7 @@ typedef struct project { struct buffer_t_array *material_icons; struct material_data2_t_array *material_datas; struct string_array *font_assets; + struct string_array *sound_assets; struct layer_data_t_array *layer_datas; struct mesh_data_t_array *mesh_datas; struct string_array *mesh_assets; @@ -793,6 +804,12 @@ typedef struct slot_font_t_array { int capacity; } slot_font_t_array_t; +typedef struct slot_sound_t_array { + slot_sound_t **buffer; + int length; + int capacity; +} slot_sound_t_array_t; + typedef struct slot_layer_t_array { slot_layer_t **buffer; int length; diff --git a/paint/sources/ui/tab_sounds.c b/paint/sources/ui/tab_sounds.c new file mode 100644 index 00000000..46f9a830 --- /dev/null +++ b/paint/sources/ui/tab_sounds.c @@ -0,0 +1,220 @@ + +#include "../global.h" + +i32 _tab_sounds_draw_i; +i32 tab_sounds_drag_pos = -1; + +void tab_sounds_delete_sound_on_next_frame(slot_sound_t *sound) { + i32 i = array_index_of(g_project->_->sounds, sound); + context_select_sound(i == g_project->_->sounds->length - 1 ? i - 1 : i + 1); + data_delete_sound(g_project->_->sounds->buffer[i]->file); + array_splice(g_project->_->sounds, i, 1); +} + +void tab_sounds_delete_sound(slot_sound_t *sound) { + sys_notify_on_next_frame(&tab_sounds_delete_sound_on_next_frame, sound); + ui_base_hwnds->buffer[2]->redraws = 2; +} + +void tab_sounds_draw_context_menu_draw() { + i32 i = _tab_sounds_draw_i; + if (ui_menu_button(tr("Delete"), "delete", ICON_DELETE)) { + tab_sounds_delete_sound(g_project->_->sounds->buffer[i]); + } +} + +void tab_sounds_draw_context_menu(void *_) { + context_select_sound(_tab_sounds_draw_i); + ui_menu_draw(&tab_sounds_draw_context_menu_draw, -1, -1); +} + +void tab_sounds_draw_select_sound(void *_) { + i32 i = _tab_sounds_draw_i; + context_select_sound(i); +} + +void tab_sounds_draw(ui_handle_t *htab) { + if (ui_tab(htab, tr("Sounds"), false, -1, false) && g_ui->_window_h > ui_statusbar_default_h * UI_SCALE()) { + + ui_begin_sticky(); + f32_array_t *row = f32_array_create_from_raw( + (f32[]){ + -100, + }, + 1); + ui_row(row); + + if (ui_icon_button(tr("Import"), ICON_IMPORT, UI_ALIGN_CENTER)) { + project_import_asset("wav,ogg", true); + } + + ui_end_sticky(); + ui_separator(3, false); + + i32 slotw = math_floor(51 * UI_SCALE()); + i32 num = math_floor(g_ui->_window_w / (float)slotw); + if (num == 0) { + return; + } + + bool drag_pos_set = false; + f32 uix = 0.0; + f32 uiy = 0.0; + i32 imgw_val = math_floor(50 * UI_SCALE()); + + for (i32 row = 0; row < math_floor(math_ceil(g_project->_->sounds->length / (float)num)); ++row) { + i32 mult = g_config->show_asset_names ? 2 : 1; + f32_array_t *ar = f32_array_create_from_raw((f32[]){}, 0); + for (i32 i = 0; i < num * mult; ++i) { + f32_array_push(ar, 1 / (float)num); + } + ui_row(ar); + + g_ui->_x += 2; + f32 off = g_config->show_asset_names ? UI_ELEMENT_OFFSET() * 10.0 : 6; + if (row > 0) { + g_ui->_y += off; + } + + for (i32 j = 0; j < num; ++j) { + i32 imgw = math_floor(50 * UI_SCALE()); + i32 i = j + row * num; + if (i >= g_project->_->sounds->length) { + ui_end_element_of_size(imgw); + if (g_config->show_asset_names) { + ui_end_element_of_size(0); + } + continue; + } + gpu_texture_t *img = g_project->_->sounds->buffer[i]->image; + + if (g_context->sound == g_project->_->sounds->buffer[i]) { + // ui_fill(1, -2, img.width + 3, img.height + 3, ui.ops.theme.HIGHLIGHT_COL); // TODO + i32 off = row % 2 == 1 ? 1 : 0; + i32 w = 50; + if (g_config->window_scale > 1) { + w += math_floor(g_config->window_scale * 2); + } + ui_fill(-1, -2, w + 3, 2, g_theme->HIGHLIGHT_COL); + ui_fill(-1, w - off, w + 3, 2 + off, g_theme->HIGHLIGHT_COL); + ui_fill(-1, -2, 2, w + 3, g_theme->HIGHLIGHT_COL); + ui_fill(w + 1, -2, 2, w + 4, g_theme->HIGHLIGHT_COL); + } + + uix = g_ui->_x; + uiy = g_ui->_y; + i32 tile = UI_SCALE() > 1 ? 100 : 50; + + if (base_drag_sound != NULL && tab_sounds_drag_pos == i) { + ui_fill(-1, -2, 2, imgw_val + 4, g_theme->HIGHLIGHT_COL); + } + + ui_state_t state = UI_STATE_IDLE; + // if (g_project->_->sounds->buffer[i]->preview_ready) { + // state = ui_image(img, 0xffffffff, -1.0); + // } + // else { + state = ui_sub_image(resource_get("icons.k"), -1, -1.0, tile * 7, tile * 9, tile, tile); + // } + + if (state == UI_STATE_HOVERED && base_drag_sound != NULL) { + tab_sounds_drag_pos = (mouse_x > uix + g_ui->_window_x + imgw_val / 2.0) ? i + 1 : i; + drag_pos_set = true; + } + + if (state == UI_STATE_STARTED) { + if (g_context->sound != g_project->_->sounds->buffer[i]) { + _tab_sounds_draw_i = i; + sys_notify_on_next_frame(&tab_sounds_draw_select_sound, NULL); + } + base_drag_off_x = -(mouse_x - uix - g_ui->_window_x - 3); + base_drag_off_y = -(mouse_y - uiy - g_ui->_window_y + 1); + gc_unroot(base_drag_sound); + base_drag_sound = g_project->_->sounds->buffer[i]; + gc_root(base_drag_sound); + // if (sys_time() - g_context->select_time < 0.2) { + // ui_base_show_2d_view(VIEW_2D_TYPE_FONT); + // gc_unroot(base_drag_sound); + // base_drag_sound = NULL; + // base_is_dragging = false; + // } + g_context->select_time = sys_time(); + } + if (g_ui->is_hovered && g_ui->input_released_r) { + _tab_sounds_draw_i = i; + sys_notify_on_next_frame(&tab_sounds_draw_context_menu, NULL); + } + if (g_ui->is_hovered) { + // if (img == NULL) { + // _tab_sounds_draw_i = i; + // sys_notify_on_next_frame(&tab_sounds_draw_make_font_preview, NULL); + // } + // else { + ui_tooltip_image(img, 0); + ui_tooltip(g_project->_->sounds->buffer[i]->name); + // } + } + + if (g_config->show_asset_names) { + g_ui->_x = uix; + g_ui->_y += slotw * 0.9; + ui_text(g_project->_->sounds->buffer[i]->name, UI_ALIGN_CENTER, 0x00000000); + if (g_ui->is_hovered) { + ui_tooltip(g_project->_->sounds->buffer[i]->name); + } + g_ui->_y -= slotw * 0.9; + if (i == g_project->_->sounds->length - 1) { + g_ui->_y += j == num - 1 ? imgw : imgw + UI_ELEMENT_H() + UI_ELEMENT_OFFSET(); + } + } + } + + g_ui->_y += 6; + } + + if (base_drag_sound != NULL && tab_sounds_drag_pos == g_project->_->sounds->length) { + g_ui->_x = uix; + g_ui->_y = uiy; + ui_fill(imgw_val + 1, -2, 2, imgw_val + 4, g_theme->HIGHLIGHT_COL); + } + + if (!drag_pos_set) { + tab_sounds_drag_pos = -1; + } + + bool in_focus = g_ui->input_x > g_ui->_window_x && g_ui->input_x < g_ui->_window_x + g_ui->_window_w && g_ui->input_y > g_ui->_window_y && + g_ui->input_y < g_ui->_window_y + g_ui->_window_h; + if (in_focus && g_ui->is_delete_down && g_project->_->sounds->length > 1 && !string_equals(g_context->sound->file, "")) { + g_ui->is_delete_down = false; + tab_sounds_delete_sound(g_context->sound); + } + if (in_focus) { + i32 i = array_index_of(g_project->_->sounds, g_context->sound); + if (g_ui->is_key_pressed && g_ui->key_code == KEY_CODE_UP) { + if (i > 0) { + _tab_sounds_draw_i = i - 1; + sys_notify_on_next_frame(&tab_sounds_draw_select_sound, NULL); + } + } + if (g_ui->is_key_pressed && g_ui->key_code == KEY_CODE_DOWN) { + if (i < g_project->_->sounds->length - 1) { + _tab_sounds_draw_i = i + 1; + sys_notify_on_next_frame(&tab_sounds_draw_select_sound, NULL); + } + } + } + } +} + +void tab_sounds_accept_sound_drop(slot_sound_t *sound) { + if (tab_sounds_drag_pos == -1) { + return; + } + + i32 sound_pos = array_index_of(g_project->_->sounds, sound); + if (sound_pos != -1 && math_abs(sound_pos - tab_sounds_drag_pos) > 0) { + array_remove(g_project->_->sounds, sound); + i32 new_pos = tab_sounds_drag_pos - sound_pos > 0 ? tab_sounds_drag_pos - 1 : tab_sounds_drag_pos; + array_insert(g_project->_->sounds, new_pos, sound); + } +} diff --git a/paint/sources/ui/ui_base.c b/paint/sources/ui/ui_base.c index 95787f5a..f6c4cf2e 100644 --- a/paint/sources/ui/ui_base.c +++ b/paint/sources/ui/ui_base.c @@ -344,23 +344,26 @@ tab_draw_array_t_array_t *ui_base_init_hwnd_tabs() { _draw_callback_create(tab_browser_draw), _draw_callback_create(tab_textures_draw), _draw_callback_create(tab_fonts_draw), + _draw_callback_create(tab_sounds_draw), _draw_callback_create(tab_swatches_draw), _draw_callback_create(tab_timeline_draw), _draw_callback_create(tab_console_draw), _draw_callback_create(ui_statusbar_draw_version_tab), }, - 7); + 8); #ifdef IRON_IOS if (config_is_iphone()) { array_splice(a2, 5, 1); // Timeline array_splice(a2, 4, 1); // Swatches + array_splice(a2, 3, 1); // Sounds } #endif #ifdef IRON_ANDROID if (iron_window_width() <= 1080) { array_splice(a2, 5, 1); // Timeline + array_splice(a2, 3, 1); // Sounds } #endif diff --git a/paint/sources/util/util_encode.c b/paint/sources/util/util_encode.c index be966651..86523ca7 100644 --- a/paint/sources/util/util_encode.c +++ b/paint/sources/util/util_encode.c @@ -206,7 +206,7 @@ buffer_t *util_encode_project(project_t *raw) { buffer_t *encoded = buffer_create(size); armpack_encode_start(encoded->buffer); - armpack_encode_map(35); + armpack_encode_map(36); armpack_encode_string("version"); armpack_encode_string(raw->version); @@ -359,6 +359,8 @@ buffer_t *util_encode_project(project_t *raw) { armpack_encode_string("font_assets"); armpack_encode_array_string(raw->font_assets); + armpack_encode_string("sound_assets"); + armpack_encode_array_string(raw->sound_assets); armpack_encode_string("layer_datas"); if (raw->layer_datas != NULL) {