#include "audio_paths.nvgt" funcdef void learn_sounds_speak_callback(const string& in message, bool interrupt); funcdef void learn_sounds_tick_callback(); funcdef void learn_sounds_setup_callback(); learn_sounds_speak_callback @learnSoundsSpeakCallback = null; learn_sounds_tick_callback @learnSoundsTickCallback = null; learn_sounds_setup_callback @learnSoundsSetupCallback = null; string[] learnSoundsSkipList; string[] learnSoundsDescriptionPaths; string[] learnSoundsDescriptionTexts; string learnSoundsRootDir = "sounds"; string learnSoundsMenuSoundDir = "sounds/menu"; bool learnSoundsWrap = true; bool learnSoundsPlaySelectSound = true; bool learnSoundsSetupApplied = false; sound learnSoundsPreviewSound; sound learnSoundsMoveSound; sound learnSoundsSelectSound; void learn_sounds_set_speak_callback(learn_sounds_speak_callback @callback) { @learnSoundsSpeakCallback = @callback; } void learn_sounds_set_tick_callback(learn_sounds_tick_callback @callback) { @learnSoundsTickCallback = @callback; } void learn_sounds_set_setup_callback(learn_sounds_setup_callback @callback) { @learnSoundsSetupCallback = @callback; learnSoundsSetupApplied = false; } void learn_sounds_set_root_dir(const string rootDir) { learnSoundsRootDir = rootDir; } void learn_sounds_set_menu_sound_dir(const string menuSoundDir) { learnSoundsMenuSoundDir = menuSoundDir; } void learn_sounds_set_wrap(bool wrap) { learnSoundsWrap = wrap; } void learn_sounds_set_play_select_sound(bool playSelectSound) { learnSoundsPlaySelectSound = playSelectSound; } void learn_sounds_clear_skip_entries() { learnSoundsSkipList.resize(0); } void learn_sounds_add_skip_entry(const string entry) { if (entry == "") { return; } learnSoundsSkipList.insert_last(entry); } void learn_sounds_clear_descriptions() { learnSoundsDescriptionPaths.resize(0); learnSoundsDescriptionTexts.resize(0); } void learn_sounds_add_description(const string soundPath, const string description) { if (soundPath == "") { return; } learnSoundsDescriptionPaths.insert_last(soundPath); learnSoundsDescriptionTexts.insert_last(description); } void learn_sounds_reset_configuration() { learn_sounds_clear_skip_entries(); learn_sounds_clear_descriptions(); learnSoundsSetupApplied = false; } void learn_sounds_speak(const string& in message, bool interrupt) { if (@learnSoundsSpeakCallback !is null) { learnSoundsSpeakCallback(message, interrupt); return; } screen_reader_speak(message, interrupt); } void learn_sounds_tick() { if (@learnSoundsTickCallback !is null) { learnSoundsTickCallback(); } } void learn_sounds_apply_setup_once() { if (learnSoundsSetupApplied) { return; } if (@learnSoundsSetupCallback !is null) { learnSoundsSetupCallback(); } learnSoundsSetupApplied = true; } string learn_sounds_normalize_path(const string& in path) { return path.replace("\\", "/", true); } bool learn_sounds_is_directory_skip_entry(const string& in entry) { if (entry.length() == 0) return false; if (entry.substr(entry.length() - 1) == "/") return true; return directory_exists(entry); } bool learn_sounds_should_skip_path(const string& in path) { string normalizedPath = learn_sounds_normalize_path(path); for (uint skipIndex = 0; skipIndex < learnSoundsSkipList.length(); skipIndex++) { string entry = learn_sounds_normalize_path(learnSoundsSkipList[skipIndex]); if (entry.length() == 0) continue; bool isDirectory = learn_sounds_is_directory_skip_entry(entry); if (isDirectory) { if (entry.substr(entry.length() - 1) != "/") entry += "/"; if (normalizedPath.length() >= entry.length() && normalizedPath.substr(0, entry.length()) == entry) { return true; } continue; } if (normalizedPath == entry) return true; if (entry.find_first("/") < 0 && entry.find_first("\\") < 0) { if (normalizedPath.length() >= entry.length() + 1 && normalizedPath.substr(normalizedPath.length() - entry.length()) == entry) { return true; } } } return false; } string learn_sounds_get_description(const string& in path) { string normalizedPath = learn_sounds_normalize_path(path); uint descriptionCount = learnSoundsDescriptionPaths.length(); if (learnSoundsDescriptionTexts.length() < descriptionCount) { descriptionCount = learnSoundsDescriptionTexts.length(); } for (uint descriptionIndex = 0; descriptionIndex < descriptionCount; descriptionIndex++) { if (learn_sounds_normalize_path(learnSoundsDescriptionPaths[descriptionIndex]) == normalizedPath) { return learnSoundsDescriptionTexts[descriptionIndex]; } } return ""; } bool learn_sounds_contains_path_ci(string[] @paths, const string& in path) { string normalizedPath = learn_sounds_normalize_path(path).lower(); for (uint pathIndex = 0; pathIndex < paths.length(); pathIndex++) { if (learn_sounds_normalize_path(paths[pathIndex]).lower() == normalizedPath) { return true; } } return false; } void learn_sounds_add_path_if_missing(string[] @paths, const string& in path) { if (learn_sounds_contains_path_ci(paths, path)) { return; } paths.insert_last(path); } bool learn_sounds_has_audio_extension(const string& in path) { string lowerPath = path.lower(); if (lowerPath.length() < 4) return false; string suffix = lowerPath.substr(lowerPath.length() - 4); return suffix == ".ogg" || suffix == ".wav"; } void learn_sounds_gather_files_from_pack(const string& in basePath, string[] @outFiles) { pack @activePack = cast(sound_default_pack); if (@activePack is null) return; string[] @packFiles = activePack.list_files(); if (@packFiles is null) return; string normalizedBasePath = learn_sounds_normalize_path(basePath); if (normalizedBasePath != "" && normalizedBasePath.substr(normalizedBasePath.length() - 1) != "/") { normalizedBasePath += "/"; } string normalizedBasePathLower = normalizedBasePath.lower(); for (uint fileIndex = 0; fileIndex < packFiles.length(); fileIndex++) { string normalizedPath = learn_sounds_normalize_path(packFiles[fileIndex]); if (normalizedPath.find("./") == 0) { normalizedPath = normalizedPath.substr(2); } if (!learn_sounds_has_audio_extension(normalizedPath)) { continue; } if (normalizedBasePathLower != "") { if (normalizedPath.length() < normalizedBasePathLower.length()) { continue; } if (normalizedPath.substr(0, normalizedBasePathLower.length()).lower() != normalizedBasePathLower) { continue; } } learn_sounds_add_path_if_missing(outFiles, normalizedPath); } } void learn_sounds_gather_files_recursive(const string& in basePath, string[] @outFiles) { string[] @oggFiles = find_files(basePath + "/*.ogg"); if (@oggFiles !is null) { for (uint fileIndex = 0; fileIndex < oggFiles.length(); fileIndex++) { learn_sounds_add_path_if_missing(outFiles, basePath + "/" + oggFiles[fileIndex]); } } string[] @oggUpperFiles = find_files(basePath + "/*.OGG"); if (@oggUpperFiles !is null) { for (uint fileIndex = 0; fileIndex < oggUpperFiles.length(); fileIndex++) { learn_sounds_add_path_if_missing(outFiles, basePath + "/" + oggUpperFiles[fileIndex]); } } string[] @wavFiles = find_files(basePath + "/*.wav"); if (@wavFiles !is null) { for (uint fileIndex = 0; fileIndex < wavFiles.length(); fileIndex++) { learn_sounds_add_path_if_missing(outFiles, basePath + "/" + wavFiles[fileIndex]); } } string[] @wavUpperFiles = find_files(basePath + "/*.WAV"); if (@wavUpperFiles !is null) { for (uint fileIndex = 0; fileIndex < wavUpperFiles.length(); fileIndex++) { learn_sounds_add_path_if_missing(outFiles, basePath + "/" + wavUpperFiles[fileIndex]); } } string[] @folders = find_directories(basePath + "/*"); if (@folders !is null) { for (uint folderIndex = 0; folderIndex < folders.length(); folderIndex++) { learn_sounds_gather_files_recursive(basePath + "/" + folders[folderIndex], outFiles); } } } bool learn_sounds_sort_case_insensitive(const string& in a, const string& in b) { return a.lower() < b.lower(); } string learn_sounds_collapse_spaces(const string& in text) { string result = text; while (result.find_first(" ") > -1) { result = result.replace(" ", " ", true); } return result; } string learn_sounds_label_from_path(const string& in soundPath) { string normalizedPath = learn_sounds_normalize_path(soundPath); int slashPos = normalizedPath.find_last_of("/"); string name = (slashPos >= 0) ? normalizedPath.substr(slashPos + 1) : normalizedPath; string lowerName = name.lower(); if (lowerName.length() > 4 && lowerName.substr(lowerName.length() - 4) == ".ogg") { name = name.substr(0, name.length() - 4); } else if (lowerName.length() > 4 && lowerName.substr(lowerName.length() - 4) == ".wav") { name = name.substr(0, name.length() - 4); } name = name.replace("_", " ", true); name = name.replace("-", " ", true); name = learn_sounds_collapse_spaces(name); name = name.lower(); name.trim_whitespace_this(); if (name.length() == 0) return "Sound"; return name; } void learn_sounds_collect_entries(string[] @labels, string[] @soundPaths) { labels.resize(0); soundPaths.resize(0); string[] discoveredFiles; if (directory_exists(learnSoundsRootDir)) { learn_sounds_gather_files_recursive(learnSoundsRootDir, discoveredFiles); } learn_sounds_gather_files_from_pack(learnSoundsRootDir, discoveredFiles); if (discoveredFiles.length() > 1) { discoveredFiles.sort(learn_sounds_sort_case_insensitive); } for (uint fileIndex = 0; fileIndex < discoveredFiles.length(); fileIndex++) { string soundPath = learn_sounds_normalize_path(discoveredFiles[fileIndex]); if (learn_sounds_should_skip_path(soundPath)) { continue; } string label = learn_sounds_label_from_path(soundPath); string description = learn_sounds_get_description(soundPath); if (description.length() > 0) { label += " - " + description; } labels.insert_last(label); soundPaths.insert_last(soundPath); } } void learn_sounds_play_ui_sound(sound& inout soundObj, const string basePath) { soundObj.close(); // Avoid file_exists()-only path checks so packaged Android assets can still load. if (soundObj.load(basePath)) { soundObj.play(); return; } if (soundObj.load(basePath + ".ogg")) { soundObj.play(); return; } if (!soundObj.load(basePath + ".wav")) { return; } soundObj.play(); } void learn_sounds_run_menu() { learn_sounds_apply_setup_once(); string[] labels; string[] soundPaths; learn_sounds_collect_entries(labels, soundPaths); learn_sounds_speak("Learn sounds.", true); if (labels.length() == 0) { learn_sounds_speak("No sounds available.", true); return; } int selection = 0; learn_sounds_speak(labels[selection], true); while (true) { wait(5); learn_sounds_tick(); if (key_pressed(KEY_ESCAPE)) { learn_sounds_speak("Closed.", true); return; } if (key_pressed(KEY_DOWN)) { learn_sounds_play_ui_sound(learnSoundsMoveSound, learnSoundsMenuSoundDir + "/menu_move"); selection++; if (selection >= int(labels.length())) { selection = learnSoundsWrap ? 0 : int(labels.length()) - 1; } learn_sounds_speak(labels[selection], true); } if (key_pressed(KEY_UP)) { learn_sounds_play_ui_sound(learnSoundsMoveSound, learnSoundsMenuSoundDir + "/menu_move"); selection--; if (selection < 0) { selection = learnSoundsWrap ? int(labels.length()) - 1 : 0; } learn_sounds_speak(labels[selection], true); } if (key_pressed(KEY_RETURN)) { if (learnSoundsPlaySelectSound) { learn_sounds_play_ui_sound(learnSoundsSelectSound, learnSoundsMenuSoundDir + "/menu_select"); } string selectedPath = soundPaths[selection]; learnSoundsPreviewSound.close(); if (!learnSoundsPreviewSound.load(selectedPath)) { learn_sounds_speak("Unable to load sound.", true); continue; } learnSoundsPreviewSound.play(); } } }