diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..e9d7260 --- /dev/null +++ b/.clang-format @@ -0,0 +1,26 @@ +# Config file for clang-format, a C/C++/... code formatter. + +BasedOnStyle: Chromium +# TODO: Uncomment once clang-format 20 is out +# BreakBinaryOperations: RespectPrecedence +BreakConstructorInitializers: AfterColon +AccessModifierOffset: -4 +AlignAfterOpenBracket: BlockIndent +AlignOperands: DontAlign +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: WithoutElse +BinPackArguments: false +BreakBeforeBinaryOperators: NonAssignment +BreakStringLiterals: false +ColumnLimit: 100 +CompactNamespaces: true +IncludeBlocks: Regroup +IndentWidth: 4 +InsertNewlineAtEOF: true +LineEnding: LF +PackConstructorInitializers: Never +SeparateDefinitionBlocks: Always +SortIncludes: CaseInsensitive +SpacesBeforeTrailingComments: 1 diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..30936e6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# Config file for generic text editors. + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{js,ts,yaml,yml}] +indent_size = 2 diff --git a/.gersemirc b/.gersemirc new file mode 100644 index 0000000..25b76ae --- /dev/null +++ b/.gersemirc @@ -0,0 +1,4 @@ +# Config file for gersemi, a CMake code formatter. + +line_length: 100 +warn_about_unknown_commands: false diff --git a/.gitignore b/.gitignore index 3b6a86f..681f8b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ .vs/ +.vscode/ +*.user build/ -*.user \ No newline at end of file +venv/ + +__pycache__ +.doit.db.* diff --git a/.prettierrc.yml b/.prettierrc.yml new file mode 100644 index 0000000..248a4a0 --- /dev/null +++ b/.prettierrc.yml @@ -0,0 +1,11 @@ +# Config file for Prettier, a JavaScript/TypeScript code formatter. + +tabWidth: 2 +printWidth: 100 +singleQuote: true +arrowParens: avoid + +overrides: + - files: '*.jsx' # Adobe JSX, not React + options: + trailingComma: none diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 0000000..d6d37d8 --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,7 @@ +# Config file for Ruff, a Python code formatter. + +line-length = 100 + +[format] +quote-style = "single" +skip-magic-trailing-comma = true diff --git a/CMakeLists.txt b/CMakeLists.txt index b6ada99..ef5c2b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,14 +13,11 @@ add_subdirectory("extras/MagixVegas") add_subdirectory("extras/EsotericSoftwareSpine") # Install misc. files -install( - FILES README.adoc LICENSE.md CHANGELOG.md - DESTINATION . -) +install(FILES README.adoc LICENSE.md CHANGELOG.md DESTINATION .) # Configure CPack function(get_short_system_name variable) - if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") + if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") set(${variable} "macOS" PARENT_SCOPE) else() set(${variable} "${CMAKE_SYSTEM_NAME}" PARENT_SCOPE) diff --git a/dodo.py b/dodo.py new file mode 100644 index 0000000..8dd9292 --- /dev/null +++ b/dodo.py @@ -0,0 +1,117 @@ +"""Collection of tasks. Run using `doit `.""" + +from pathlib import Path +import subprocess +from functools import cache +from gitignore_parser import parse_gitignore +from typing import Dict, Optional, List +from enum import Enum + + +root_dir = Path(__file__).parent +rhubarb_dir = root_dir / 'rhubarb' + + +def task_format(): + """Format source files""" + + files_by_formatters = get_files_by_formatters() + for formatter, files in files_by_formatters.items(): + yield { + 'name': formatter.value, + 'actions': [(format, [files, formatter])], + 'file_dep': files, + } + + +def task_check_formatted(): + """Fails unless source files are formatted""" + + files_by_formatters = get_files_by_formatters() + for formatter, files in files_by_formatters.items(): + yield { + 'basename': 'check-formatted', + 'name': formatter.value, + 'actions': [(format, [files, formatter], {'check_only': True})], + } + + +class Formatter(Enum): + """A source code formatter.""" + + CLANG_FORMAT = 'clang-format' + GERSEMI = 'gersemi' + PRETTIER = 'prettier' + RUFF = 'ruff' + + +def format(files: List[Path], formatter: Formatter, *, check_only: bool = False): + match formatter: + case Formatter.CLANG_FORMAT: + subprocess.run( + ['clang-format', '--dry-run' if check_only else '-i', '--Werror', *files], + check=True, + ) + case Formatter.GERSEMI: + subprocess.run(['gersemi', '--check' if check_only else '-i', *files], check=True) + case Formatter.PRETTIER: + subprocess.run( + [ + *['deno', 'run', '-A', 'npm:prettier@3.4.2'], + *['--check' if check_only else '--write', '--log-level', 'warn', *files], + ], + check=True, + ) + case Formatter.RUFF: + subprocess.run( + ['ruff', '--quiet', 'format', *(['--check'] if check_only else []), *files], + check=True, + ) + case _: + raise ValueError(f'Unknown formatter: {formatter}') + + +@cache +def get_files_by_formatters() -> Dict[Formatter, List[Path]]: + """Returns a dict with all formattable code files grouped by formatter.""" + + is_gitignored = parse_gitignore(root_dir / '.gitignore') + + def is_hidden(path: Path): + return path.name.startswith('.') + + def is_third_party(path: Path): + return path.is_relative_to(rhubarb_dir / 'lib') or path.name == 'gradle' + + result = {formatter: [] for formatter in Formatter} + + def visit(dir: Path): + for path in dir.iterdir(): + if is_gitignored(path) or is_hidden(path) or is_third_party(path): + continue + + if path.is_file(): + formatter = get_formatter(path) + if formatter is not None: + result[formatter].append(path) + else: + visit(path) + + visit(root_dir) + return result + + +def get_formatter(path: Path) -> Optional[Formatter]: + """Returns the formatter to use for the given code file, if any.""" + + match path.suffix.lower(): + case '.c' | '.cpp' | '.h': + return Formatter.CLANG_FORMAT + case '.cmake': + return Formatter.GERSEMI + case _ if path.name.lower() == 'cmakelists.txt': + return Formatter.GERSEMI + case '.js' | '.jsx' | '.ts': + return Formatter.PRETTIER + case '.py': + return Formatter.RUFF diff --git a/extras/AdobeAfterEffects/CMakeLists.txt b/extras/AdobeAfterEffects/CMakeLists.txt index ee32c2b..44121a1 100644 --- a/extras/AdobeAfterEffects/CMakeLists.txt +++ b/extras/AdobeAfterEffects/CMakeLists.txt @@ -1,11 +1,5 @@ cmake_minimum_required(VERSION 3.2) -set(afterEffectsFiles - "Rhubarb Lip Sync.jsx" - "README.adoc" -) +set(afterEffectsFiles "Rhubarb Lip Sync.jsx" "README.adoc") -install( - FILES ${afterEffectsFiles} - DESTINATION "extras/AdobeAfterEffects" -) +install(FILES ${afterEffectsFiles} DESTINATION "extras/AdobeAfterEffects") diff --git a/extras/AdobeAfterEffects/Rhubarb Lip Sync.jsx b/extras/AdobeAfterEffects/Rhubarb Lip Sync.jsx index 6ec29b5..8c0f30a 100644 --- a/extras/AdobeAfterEffects/Rhubarb Lip Sync.jsx +++ b/extras/AdobeAfterEffects/Rhubarb Lip Sync.jsx @@ -1,4 +1,5 @@ -(function polyfill() { +// prettier-ignore +(function polyfill() { // Polyfill for Object.assign "function"!=typeof Object.assign&&(Object.assign=function(a,b){"use strict";if(null==a)throw new TypeError("Cannot convert undefined or null to object");for(var c=Object(a),d=1;d= basicMouthShapeCount; result += '\n00:' + pad(i, 2) + '\t' + mouthShapeName + (isOptional ? ' (optional)' : ''); }); @@ -294,7 +322,7 @@ function getMouthCompHelpTip() { function createExtendedShapeCheckboxes() { var result = {}; - extendedMouthShapeNames.forEach(function(shapeName) { + extendedMouthShapeNames.forEach(function (shapeName) { result[shapeName.toLowerCase()] = controlFunctions.Checkbox({ text: shapeName, helpTip: 'Controls whether to use the optional ' + shapeName + ' shape.' @@ -320,9 +348,10 @@ function createDialogWindow() { active: true }), value: DropDownList({ - helpTip: 'An audio file containing recorded dialog.\n' - + 'This field shows all audio files that exist in ' - + 'your After Effects project.' + helpTip: + 'An audio file containing recorded dialog.\n' + + 'This field shows all audio files that exist in ' + + 'your After Effects project.' }) }), recognizer: Group({ @@ -337,8 +366,9 @@ function createDialogWindow() { properties: { multiline: true }, characters: 60, minimumSize: [0, 100], - helpTip: 'For better animation results, you can specify the text of ' - + 'the recording here. This field is optional.' + helpTip: + 'For better animation results, you can specify the text of ' + + 'the recording here. This field is optional.' }) }), mouthComp: Group({ @@ -354,8 +384,9 @@ function createDialogWindow() { targetFolder: Group({ label: StaticText({ text: 'Target folder:' }), value: DropDownList({ - helpTip: 'The project folder in which to create the animation ' - + 'composition. The composition will be named like the audio file.' + helpTip: + 'The project folder in which to create the animation ' + + 'composition. The composition will be named like the audio file.' }) }), frameRate: Group({ @@ -366,8 +397,9 @@ function createDialogWindow() { }), auto: Checkbox({ text: 'From mouth composition', - helpTip: 'If checked, the animation will use the same frame rate as ' - + 'the mouth composition.' + helpTip: + 'If checked, the animation will use the same frame rate as ' + + 'the mouth composition.' }) }) }), @@ -400,13 +432,13 @@ function createDialogWindow() { animateButton: window.buttons.animate, cancelButton: window.buttons.cancel }; - extendedMouthShapeNames.forEach(function(shapeName) { + extendedMouthShapeNames.forEach(function (shapeName) { controls['mouthShape' + shapeName] = window.settings.extendedMouthShapes[shapeName.toLowerCase()]; }); // Add audio file options - getAudioFileProjectItems().forEach(function(projectItem) { + getAudioFileProjectItems().forEach(function (projectItem) { var listItem = controls.audioFile.add('item', getItemPath(projectItem)); listItem.projectItem = projectItem; }); @@ -416,7 +448,7 @@ function createDialogWindow() { { text: 'PocketSphinx (use for English recordings)', value: 'pocketSphinx' }, { text: 'Phonetic (use for non-English recordings)', value: 'phonetic' } ]; - recognizerOptions.forEach(function(option) { + recognizerOptions.forEach(function (option) { var listItem = controls.recognizer.add('item', option.text); listItem.value = option.value; }); @@ -425,7 +457,7 @@ function createDialogWindow() { var comps = toArrayBase1(app.project.items).filter(function (item) { return item instanceof CompItem; }); - comps.forEach(function(projectItem) { + comps.forEach(function (projectItem) { var listItem = controls.mouthComp.add('item', getItemPath(projectItem)); listItem.projectItem = projectItem; }); @@ -435,7 +467,7 @@ function createDialogWindow() { return item instanceof FolderItem; }); projectFolders.unshift(app.project.rootFolder); - projectFolders.forEach(function(projectFolder) { + projectFolders.forEach(function (projectFolder) { var listItem = controls.targetFolder.add('item', getItemPath(projectFolder)); listItem.projectItem = projectFolder; }); @@ -446,26 +478,29 @@ function createDialogWindow() { controls.dialogText.text = settings.dialogText || ''; selectByTextOrFirst(controls.recognizer, settings.recognizer); selectByTextOrFirst(controls.mouthComp, settings.mouthComp); - extendedMouthShapeNames.forEach(function(shapeName) { - controls['mouthShape' + shapeName].value = - (settings.extendedMouthShapes || {})[shapeName.toLowerCase()]; + extendedMouthShapeNames.forEach(function (shapeName) { + controls['mouthShape' + shapeName].value = (settings.extendedMouthShapes || {})[ + shapeName.toLowerCase() + ]; }); selectByTextOrFirst(controls.targetFolder, settings.targetFolder); controls.frameRate.text = settings.frameRate || ''; controls.autoFrameRate.value = settings.autoFrameRate; // Align controls - window.onShow = function() { + window.onShow = function () { // Give uniform width to all labels var groups = toArray(window.settings.children); - var labelWidths = groups.map(function(group) { return group.children[0].size.width; }); + var labelWidths = groups.map(function (group) { + return group.children[0].size.width; + }); var maxLabelWidth = Math.max.apply(Math, labelWidths); groups.forEach(function (group) { group.children[0].size.width = maxLabelWidth; }); // Give uniform width to inputs - var valueWidths = groups.map(function(group) { + var valueWidths = groups.map(function (group) { return last(group.children).bounds.right - group.children[1].bounds.left; }); var maxValueWidth = Math.max.apply(Math, valueWidths); @@ -512,7 +547,7 @@ function createDialogWindow() { frameRate: Number(controls.frameRate.text), autoFrameRate: controls.autoFrameRate.value }; - extendedMouthShapeNames.forEach(function(shapeName) { + extendedMouthShapeNames.forEach(function (shapeName) { settings.extendedMouthShapes[shapeName.toLowerCase()] = controls['mouthShape' + shapeName].value; }); @@ -541,18 +576,24 @@ function createDialogWindow() { var shapeName = mouthShapeNames[i]; var required = i < basicMouthShapeCount || controls['mouthShape' + shapeName].value; if (required && !isFrameVisible(comp, i)) { - return 'The mouth comp does not seem to contain an image for shape ' - + shapeName + ' at frame ' + i + '.'; + return ( + 'The mouth comp does not seem to contain an image for shape ' + + shapeName + + ' at frame ' + + i + + '.' + ); } } if (!comp.preserveNestedFrameRate) { var fix = Window.confirm( - 'The setting "Preserve frame rate when nested or in render queue" is not active ' - + 'for the mouth composition. This can result in incorrect animation.\n\n' - + 'Activate this setting now?', + 'The setting "Preserve frame rate when nested or in render queue" is not active ' + + 'for the mouth composition. This can result in incorrect animation.\n\n' + + 'Activate this setting now?', false, - 'Fix composition setting?'); + 'Fix composition setting?' + ); if (fix) { app.beginUndoGroup(appName + ': Mouth composition setting'); comp.preserveNestedFrameRate = true; @@ -567,10 +608,14 @@ function createDialogWindow() { var match = version.match(/Rhubarb Lip Sync version ((\d+)\.(\d+).(\d+)(-[0-9A-Za-z-.]+)?)/); if (!match) { var instructions = osIsWindows - ? 'Make sure your PATH environment variable contains the ' + appName + ' ' - + 'application directory.' - : 'Make sure you have created this file as a symbolic link to the ' + appName + ' ' - + 'executable (rhubarb).'; + ? 'Make sure your PATH environment variable contains the ' + + appName + + ' ' + + 'application directory.' + : 'Make sure you have created this file as a symbolic link to the ' + + appName + + ' ' + + 'executable (rhubarb).'; return 'Cannot find executable file "' + rhubarbPath + '". \n' + instructions; } var versionString = match[1]; @@ -579,15 +624,32 @@ function createDialogWindow() { var requiredMajor = 1; var minRequiredMinor = 9; if (major != requiredMajor || minor < minRequiredMinor) { - return 'This script requires ' + appName + ' ' + requiredMajor + '.' + minRequiredMinor - + '.0 or a later ' + requiredMajor + '.x version. ' - + 'Your installed version is ' + versionString + ', which is not compatible.'; + return ( + 'This script requires ' + + appName + + ' ' + + requiredMajor + + '.' + + minRequiredMinor + + '.0 or a later ' + + requiredMajor + + '.x version. ' + + 'Your installed version is ' + + versionString + + ', which is not compatible.' + ); } } - function generateMouthCues(audioFileFootage, recognizer, dialogText, mouthComp, extendedMouthShapeNames, - targetProjectFolder, frameRate) - { + function generateMouthCues( + audioFileFootage, + recognizer, + dialogText, + mouthComp, + extendedMouthShapeNames, + targetProjectFolder, + frameRate + ) { var basePath = Folder.temp.fsName + '/' + createGuid(); var dialogFile = new File(basePath + '.txt'); var logFile = new File(basePath + '.log'); @@ -597,15 +659,16 @@ function createDialogWindow() { writeTextFile(dialogFile, dialogText); // Create command line - var commandLine = rhubarbPath - + ' --dialogFile ' + cliEscape(dialogFile.fsName) - + ' --recognizer ' + recognizer - + ' --exportFormat json' - + ' --extendedShapes ' + cliEscape(extendedMouthShapeNames.join('')) - + ' --logFile ' + cliEscape(logFile.fsName) - + ' --logLevel fatal' - + ' --output ' + cliEscape(jsonFile.fsName) - + ' ' + cliEscape(audioFileFootage.file.fsName); + var commandLine = + rhubarbPath + + (' --dialogFile ' + cliEscape(dialogFile.fsName)) + + (' --recognizer ' + recognizer) + + ' --exportFormat json' + + (' --extendedShapes ' + cliEscape(extendedMouthShapeNames.join(''))) + + (' --logFile ' + cliEscape(logFile.fsName)) + + ' --logLevel fatal' + + (' --output ' + cliEscape(jsonFile.fsName)) + + (' ' + cliEscape(audioFileFootage.file.fsName)); // Run Rhubarb execInWindow(commandLine); @@ -635,9 +698,13 @@ function createDialogWindow() { } } - function animateMouthCues(mouthCues, audioFileFootage, mouthComp, targetProjectFolder, - frameRate) - { + function animateMouthCues( + mouthCues, + audioFileFootage, + mouthComp, + targetProjectFolder, + frameRate + ) { // Find an unconflicting comp name // ... strip extension, if present var baseName = audioFileFootage.name.match(/^(.*?)(\..*)?$/i)[1]; @@ -645,14 +712,24 @@ function createDialogWindow() { // ... add numeric suffix, if needed var existingItems = toArrayBase1(targetProjectFolder.items); var counter = 1; - while (existingItems.some(function(item) { return item.name === compName; })) { + while ( + existingItems.some(function (item) { + return item.name === compName; + }) + ) { counter++; compName = baseName + ' ' + counter; } // Create new comp - var comp = targetProjectFolder.items.addComp(compName, mouthComp.width, mouthComp.height, - mouthComp.pixelAspect, audioFileFootage.duration, frameRate); + var comp = targetProjectFolder.items.addComp( + compName, + mouthComp.width, + mouthComp.height, + mouthComp.pixelAspect, + audioFileFootage.duration, + frameRate + ); // Show new comp comp.openInViewer(); @@ -669,7 +746,7 @@ function createDialogWindow() { var timeRemap = mouthLayer['Time Remap']; // Enabling time remapping automatically adds two keys. Remove the second. timeRemap.removeKey(2); - mouthCues.mouthCues.forEach(function(mouthCue) { + mouthCues.mouthCues.forEach(function (mouthCue) { // Round down keyframe time. In animation, earlier is better than later. // Set keyframe time to *just before* the exact frame to prevent rounding errors var frame = Math.floor(timeToFrame(mouthCue.start, comp)); @@ -684,16 +761,28 @@ function createDialogWindow() { } } - function animate(audioFileFootage, recognizer, dialogText, mouthComp, extendedMouthShapeNames, - targetProjectFolder, frameRate) - { + function animate( + audioFileFootage, + recognizer, + dialogText, + mouthComp, + extendedMouthShapeNames, + targetProjectFolder, + frameRate + ) { try { - var mouthCues = generateMouthCues(audioFileFootage, recognizer, dialogText, mouthComp, - extendedMouthShapeNames, targetProjectFolder, frameRate); + var mouthCues = generateMouthCues( + audioFileFootage, + recognizer, + dialogText, + mouthComp, + extendedMouthShapeNames, + targetProjectFolder, + frameRate + ); app.beginUndoGroup(appName + ': Animation'); - animateMouthCues(mouthCues, audioFileFootage, mouthComp, targetProjectFolder, - frameRate); + animateMouthCues(mouthCues, audioFileFootage, mouthComp, targetProjectFolder, frameRate); app.endUndoGroup(); } catch (e) { Window.alert(e.message, appName, true); @@ -707,7 +796,7 @@ function createDialogWindow() { controls.recognizer.onChange = update; controls.dialogText.onChanging = update; controls.mouthComp.onChange = update; - extendedMouthShapeNames.forEach(function(shapeName) { + extendedMouthShapeNames.forEach(function (shapeName) { controls['mouthShape' + shapeName].onClick = update; }); controls.targetFolder.onChange = update; @@ -715,7 +804,7 @@ function createDialogWindow() { controls.autoFrameRate.onClick = update; // Handle animation - controls.animateButton.onClick = function() { + controls.animateButton.onClick = function () { var validationError = validate(); if (typeof validationError === 'string') { if (validationError) { @@ -728,7 +817,7 @@ function createDialogWindow() { controls.recognizer.selection.value, controls.dialogText.text || '', controls.mouthComp.selection.projectItem, - extendedMouthShapeNames.filter(function(shapeName) { + extendedMouthShapeNames.filter(function (shapeName) { return controls['mouthShape' + shapeName].value; }), controls.targetFolder.selection.projectItem, @@ -738,7 +827,7 @@ function createDialogWindow() { }; // Handle cancelation - controls.cancelButton.onClick = function() { + controls.cancelButton.onClick = function () { window.close(); }; @@ -747,9 +836,12 @@ function createDialogWindow() { function checkPreconditions() { if (!canWriteFiles()) { - Window.alert('This script requires file system access.\n\n' - + 'Please enable Preferences > General > Allow Scripts to Write Files and Access Network.', - appName, true); + Window.alert( + 'This script requires file system access.\n\n' + + 'Please enable Preferences > General > Allow Scripts to Write Files and Access Network.', + appName, + true + ); return false; } return true; diff --git a/extras/EsotericSoftwareSpine/CMakeLists.txt b/extras/EsotericSoftwareSpine/CMakeLists.txt index 56f2f42..d1f432d 100644 --- a/extras/EsotericSoftwareSpine/CMakeLists.txt +++ b/extras/EsotericSoftwareSpine/CMakeLists.txt @@ -1,18 +1,13 @@ cmake_minimum_required(VERSION 3.2) add_custom_target( - rhubarbForSpine ALL + rhubarbForSpine + ALL "./gradlew" "build" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMMENT "Building Rhubarb for Spine through Gradle." ) -install( - DIRECTORY "build/libs/" - DESTINATION "extras/EsotericSoftwareSpine" -) +install(DIRECTORY "build/libs/" DESTINATION "extras/EsotericSoftwareSpine") -install( - FILES README.adoc - DESTINATION "extras/EsotericSoftwareSpine" -) +install(FILES README.adoc DESTINATION "extras/EsotericSoftwareSpine") diff --git a/extras/MagixVegas/CMakeLists.txt b/extras/MagixVegas/CMakeLists.txt index 9ddf249..eadf7c3 100644 --- a/extras/MagixVegas/CMakeLists.txt +++ b/extras/MagixVegas/CMakeLists.txt @@ -8,7 +8,4 @@ set(vegasFiles "README.adoc" ) -install( - FILES ${vegasFiles} - DESTINATION "extras/MagixVegas" -) +install(FILES ${vegasFiles} DESTINATION "extras/MagixVegas") diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..428772c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +doit==0.36.0 +clang-format==19.1.5 +gersemi==0.17.1 +gitignore_parser==0.1.11 +ruff==0.8.3 diff --git a/rhubarb/CMakeLists.txt b/rhubarb/CMakeLists.txt index b33580e..1d0885e 100644 --- a/rhubarb/CMakeLists.txt +++ b/rhubarb/CMakeLists.txt @@ -10,7 +10,7 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Enable POSIX threads -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") endif() @@ -59,30 +59,31 @@ include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) link_libraries(${Boost_LIBRARIES}) # Just about every project needs Boost # ... C++ Format -FILE(GLOB cppFormatFiles "lib/cppformat/*.cc") +file(GLOB cppFormatFiles "lib/cppformat/*.cc") add_library(cppFormat ${cppFormatFiles}) target_include_directories(cppFormat SYSTEM PUBLIC "lib/cppformat") target_compile_options(cppFormat PRIVATE ${disableWarningsFlags}) set_target_properties(cppFormat PROPERTIES FOLDER lib) # ... sphinxbase -FILE(GLOB_RECURSE sphinxbaseFiles "lib/sphinxbase-rev13216/src/libsphinxbase/*.c") +file(GLOB_RECURSE sphinxbaseFiles "lib/sphinxbase-rev13216/src/libsphinxbase/*.c") add_library(sphinxbase ${sphinxbaseFiles}) -target_include_directories(sphinxbase SYSTEM PUBLIC - "lib/sphinxbase-rev13216/include" - "lib/sphinxbase-rev13216/src" - "lib/sphinx_config" +target_include_directories( + sphinxbase + SYSTEM + PUBLIC "lib/sphinxbase-rev13216/include" "lib/sphinxbase-rev13216/src" "lib/sphinx_config" ) target_compile_options(sphinxbase PRIVATE ${disableWarningsFlags}) target_compile_definitions(sphinxbase PUBLIC __SPHINXBASE_EXPORT_H__=1 SPHINXBASE_EXPORT=) # Compile as static lib set_target_properties(sphinxbase PROPERTIES FOLDER lib) # ... PocketSphinx -FILE(GLOB pocketSphinxFiles "lib/pocketsphinx-rev13216/src/libpocketsphinx/*.c") +file(GLOB pocketSphinxFiles "lib/pocketsphinx-rev13216/src/libpocketsphinx/*.c") add_library(pocketSphinx ${pocketSphinxFiles}) -target_include_directories(pocketSphinx SYSTEM PUBLIC - "lib/pocketsphinx-rev13216/include" - "lib/pocketsphinx-rev13216/src/libpocketsphinx" +target_include_directories( + pocketSphinx + SYSTEM + PUBLIC "lib/pocketsphinx-rev13216/include" "lib/pocketsphinx-rev13216/src/libpocketsphinx" ) target_link_libraries(pocketSphinx sphinxbase) target_compile_options(pocketSphinx PRIVATE ${disableWarningsFlags}) @@ -129,10 +130,10 @@ set(webRtcFiles add_library(webRtc ${webRtcFiles}) target_include_directories(webRtc SYSTEM PUBLIC "lib/webrtc-8d2248ff") target_compile_options(webRtc PRIVATE ${disableWarningsFlags}) -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - target_compile_options(webRtc PRIVATE -pthread -lpthread) +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + target_compile_options(webRtc PRIVATE -pthread -lpthread) endif() -if (NOT WIN32) +if(NOT WIN32) target_compile_definitions(webRtc PRIVATE WEBRTC_POSIX) endif() set_target_properties(webRtc PROPERTIES FOLDER lib) @@ -203,34 +204,26 @@ set(fliteFiles lib/flite-1.4/src/utils/cst_val_user.c ) add_library(flite ${fliteFiles}) -target_include_directories(flite SYSTEM PUBLIC - "lib/flite-1.4/include" - "lib/flite-1.4" -) +target_include_directories(flite SYSTEM PUBLIC "lib/flite-1.4/include" "lib/flite-1.4") target_compile_options(flite PRIVATE ${disableWarningsFlags}) set_target_properties(flite PROPERTIES FOLDER lib) # ... UTF8-CPP -add_library(utfcpp - lib/header-only.c - lib/utfcpp-2.3.5/source/utf8.h -) +add_library(utfcpp lib/header-only.c lib/utfcpp-2.3.5/source/utf8.h) target_include_directories(utfcpp SYSTEM PUBLIC "lib/utfcpp-2.3.5/source") target_compile_options(utfcpp PRIVATE ${disableWarningsFlags}) set_target_properties(utfcpp PROPERTIES FOLDER lib) # ... utf8proc -add_library(utf8proc - lib/utf8proc-2.2.0/utf8proc.c - lib/utf8proc-2.2.0/utf8proc.h -) +add_library(utf8proc lib/utf8proc-2.2.0/utf8proc.c lib/utf8proc-2.2.0/utf8proc.h) target_include_directories(utf8proc SYSTEM PUBLIC "lib/utf8proc-2.2.0") target_compile_options(utf8proc PRIVATE ${disableWarningsFlags}) target_compile_definitions(utf8proc PUBLIC UTF8PROC_STATIC=1) # Compile as static lib set_target_properties(utf8proc PROPERTIES FOLDER lib) # ... Ogg -add_library(ogg +add_library( + ogg lib/ogg-1.3.3/include/ogg/ogg.h lib/ogg-1.3.3/src/bitwise.c lib/ogg-1.3.3/src/framing.c @@ -240,7 +233,8 @@ target_compile_options(ogg PRIVATE ${disableWarningsFlags}) set_target_properties(ogg PROPERTIES FOLDER lib) # ... Vorbis -add_library(vorbis +add_library( + vorbis lib/vorbis-1.3.6/include/vorbis/vorbisfile.h lib/vorbis-1.3.6/lib/bitrate.c lib/vorbis-1.3.6/lib/block.c @@ -263,9 +257,7 @@ add_library(vorbis lib/vorbis-1.3.6/lib/window.c ) target_include_directories(vorbis SYSTEM PUBLIC "lib/vorbis-1.3.6/include") -target_link_libraries(vorbis - ogg -) +target_link_libraries(vorbis ogg) target_compile_options(vorbis PRIVATE ${disableWarningsFlags}) set_target_properties(vorbis PROPERTIES FOLDER lib) @@ -274,7 +266,8 @@ set_target_properties(vorbis PROPERTIES FOLDER lib) include_directories("src") # ... rhubarb-animation -add_library(rhubarb-animation +add_library( + rhubarb-animation src/animation/animationRules.cpp src/animation/animationRules.h src/animation/mouthAnimation.cpp @@ -296,14 +289,11 @@ add_library(rhubarb-animation src/animation/tweening.h ) target_include_directories(rhubarb-animation PRIVATE "src/animation") -target_link_libraries(rhubarb-animation - rhubarb-core - rhubarb-logging - rhubarb-time -) +target_link_libraries(rhubarb-animation rhubarb-core rhubarb-logging rhubarb-time) # ... rhubarb-audio -add_library(rhubarb-audio +add_library( + rhubarb-audio src/audio/AudioClip.cpp src/audio/AudioClip.h src/audio/audioFileReading.cpp @@ -327,7 +317,8 @@ add_library(rhubarb-audio src/audio/waveFileWriting.h ) target_include_directories(rhubarb-audio PRIVATE "src/audio") -target_link_libraries(rhubarb-audio +target_link_libraries( + rhubarb-audio webRtc vorbis rhubarb-logging @@ -337,7 +328,8 @@ target_link_libraries(rhubarb-audio # ... rhubarb-core configure_file(src/core/appInfo.cpp.in appInfo.cpp ESCAPE_QUOTES) -add_library(rhubarb-core +add_library( + rhubarb-core ${CMAKE_CURRENT_BINARY_DIR}/appInfo.cpp src/core/appInfo.h src/core/Phone.cpp @@ -346,12 +338,11 @@ add_library(rhubarb-core src/core/Shape.h ) target_include_directories(rhubarb-core PRIVATE "src/core") -target_link_libraries(rhubarb-core - rhubarb-tools -) +target_link_libraries(rhubarb-core rhubarb-tools) # ... rhubarb-exporters -add_library(rhubarb-exporters +add_library( + rhubarb-exporters src/exporters/DatExporter.cpp src/exporters/DatExporter.h src/exporters/Exporter.h @@ -365,19 +356,13 @@ add_library(rhubarb-exporters src/exporters/XmlExporter.h ) target_include_directories(rhubarb-exporters PRIVATE "src/exporters") -target_link_libraries(rhubarb-exporters - rhubarb-animation - rhubarb-core - rhubarb-time -) +target_link_libraries(rhubarb-exporters rhubarb-animation rhubarb-core rhubarb-time) # ... rhubarb-lib -add_library(rhubarb-lib - src/lib/rhubarbLib.cpp - src/lib/rhubarbLib.h -) +add_library(rhubarb-lib src/lib/rhubarbLib.cpp src/lib/rhubarbLib.h) target_include_directories(rhubarb-lib PRIVATE "src/lib") -target_link_libraries(rhubarb-lib +target_link_libraries( + rhubarb-lib rhubarb-animation rhubarb-audio rhubarb-core @@ -387,7 +372,8 @@ target_link_libraries(rhubarb-lib ) # ... rhubarb-logging -add_library(rhubarb-logging +add_library( + rhubarb-logging src/logging/Entry.cpp src/logging/Entry.h src/logging/Formatter.h @@ -402,12 +388,11 @@ add_library(rhubarb-logging src/logging/sinks.h ) target_include_directories(rhubarb-logging PRIVATE "src/logging") -target_link_libraries(rhubarb-logging - rhubarb-tools -) +target_link_libraries(rhubarb-logging rhubarb-tools) # ... rhubarb-recognition -add_library(rhubarb-recognition +add_library( + rhubarb-recognition src/recognition/g2p.cpp src/recognition/g2p.h src/recognition/languageModels.cpp @@ -423,7 +408,8 @@ add_library(rhubarb-recognition src/recognition/tokenization.h ) target_include_directories(rhubarb-recognition PRIVATE "src/recognition") -target_link_libraries(rhubarb-recognition +target_link_libraries( + rhubarb-recognition flite pocketSphinx rhubarb-audio @@ -432,7 +418,8 @@ target_link_libraries(rhubarb-recognition ) # ... rhubarb-time -add_library(rhubarb-time +add_library( + rhubarb-time src/time/BoundedTimeline.h src/time/centiseconds.cpp src/time/centiseconds.h @@ -444,13 +431,11 @@ add_library(rhubarb-time src/time/TimeRange.h ) target_include_directories(rhubarb-time PRIVATE "src/time") -target_link_libraries(rhubarb-time - cppFormat - rhubarb-logging -) +target_link_libraries(rhubarb-time cppFormat rhubarb-logging) # ... rhubarb-tools -add_library(rhubarb-tools +add_library( + rhubarb-tools src/tools/array.h src/tools/EnumConverter.h src/tools/exceptions.cpp @@ -481,15 +466,11 @@ add_library(rhubarb-tools src/tools/tupleHash.h ) target_include_directories(rhubarb-tools PRIVATE "src/tools") -target_link_libraries(rhubarb-tools - cppFormat - whereami - utfcpp - utf8proc -) +target_link_libraries(rhubarb-tools cppFormat whereami utfcpp utf8proc) # Define Rhubarb executable -add_executable(rhubarb +add_executable( + rhubarb src/rhubarb/main.cpp src/rhubarb/ExportFormat.cpp src/rhubarb/ExportFormat.h @@ -501,10 +482,7 @@ add_executable(rhubarb src/rhubarb/sinks.h ) target_include_directories(rhubarb PUBLIC "src/rhubarb") -target_link_libraries(rhubarb - rhubarb-exporters - rhubarb-lib -) +target_link_libraries(rhubarb rhubarb-exporters rhubarb-lib) target_compile_options(rhubarb PUBLIC ${enableWarningsFlags}) # Define test project @@ -521,7 +499,8 @@ set(TEST_FILES tests/WaveFileReaderTests.cpp ) add_executable(runTests ${TEST_FILES}) -target_link_libraries(runTests +target_link_libraries( + runTests gtest gmock gmock_main @@ -534,23 +513,24 @@ target_link_libraries(runTests function(copy_and_install sourceGlob relativeTargetDirectory) # Set `sourcePaths` file(GLOB sourcePaths "${sourceGlob}") - + foreach(sourcePath ${sourcePaths}) if(NOT IS_DIRECTORY ${sourcePath}) # Set `fileName` get_filename_component(fileName "${sourcePath}" NAME) # Copy file during build - add_custom_command(TARGET rhubarb POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${sourcePath}" "$/${relativeTargetDirectory}/${fileName}" + add_custom_command( + TARGET rhubarb + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy "${sourcePath}" + "$/${relativeTargetDirectory}/${fileName}" COMMENT "Creating '${relativeTargetDirectory}/${fileName}'" ) # Install file - install( - FILES "${sourcePath}" - DESTINATION "${relativeTargetDirectory}" - ) + install(FILES "${sourcePath}" DESTINATION "${relativeTargetDirectory}") endif() endforeach() endfunction() @@ -559,15 +539,19 @@ endfunction() function(copy sourceGlob relativeTargetDirectory) # Set `sourcePaths` file(GLOB sourcePaths "${sourceGlob}") - + foreach(sourcePath ${sourcePaths}) if(NOT IS_DIRECTORY ${sourcePath}) # Set `fileName` get_filename_component(fileName "${sourcePath}" NAME) # Copy file during build - add_custom_command(TARGET rhubarb POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${sourcePath}" "$/${relativeTargetDirectory}/${fileName}" + add_custom_command( + TARGET rhubarb + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy "${sourcePath}" + "$/${relativeTargetDirectory}/${fileName}" COMMENT "Creating '${relativeTargetDirectory}/${fileName}'" ) endif() @@ -579,8 +563,4 @@ copy_and_install("lib/cmusphinx-en-us-5.2/*" "res/sphinx/acoustic-model") copy_and_install("tests/resources/*" "tests/resources") -install( - TARGETS rhubarb - RUNTIME - DESTINATION . -) +install(TARGETS rhubarb RUNTIME DESTINATION .) diff --git a/rhubarb/src/animation/ShapeRule.cpp b/rhubarb/src/animation/ShapeRule.cpp index 29f18e4..0ab3359 100644 --- a/rhubarb/src/animation/ShapeRule.cpp +++ b/rhubarb/src/animation/ShapeRule.cpp @@ -1,12 +1,14 @@ #include "ShapeRule.h" + #include #include + #include "time/ContinuousTimeline.h" using boost::optional; using boost::adaptors::transformed; -template +template ContinuousTimeline, AutoJoin> boundedTimelinetoContinuousOptional( const BoundedTimeline& timeline ) { @@ -19,18 +21,13 @@ ContinuousTimeline, AutoJoin> boundedTimelinetoContinuousOptional( }; } -ShapeRule::ShapeRule( - ShapeSet shapeSet, - optional phone, - TimeRange phoneTiming -) : +ShapeRule::ShapeRule(ShapeSet shapeSet, optional phone, TimeRange phoneTiming) : shapeSet(std::move(shapeSet)), phone(std::move(phone)), - phoneTiming(phoneTiming) -{} + phoneTiming(phoneTiming) {} ShapeRule ShapeRule::getInvalid() { - return { {}, boost::none, { 0_cs, 0_cs } }; + return {{}, boost::none, {0_cs, 0_cs}}; } bool ShapeRule::operator==(const ShapeRule& rhs) const { @@ -42,8 +39,7 @@ bool ShapeRule::operator!=(const ShapeRule& rhs) const { } bool ShapeRule::operator<(const ShapeRule& rhs) const { - return shapeSet < rhs.shapeSet - || phone < rhs.phone + return shapeSet < rhs.shapeSet || phone < rhs.phone || phoneTiming.getStart() < rhs.phoneTiming.getStart() || phoneTiming.getEnd() < rhs.phoneTiming.getEnd(); } @@ -54,8 +50,7 @@ ContinuousTimeline getShapeRules(const BoundedTimeline& phones // Create timeline of shape rules ContinuousTimeline shapeRules( - phones.getRange(), - { { Shape::X }, boost::none, { 0_cs, 0_cs } } + phones.getRange(), {{Shape::X}, boost::none, {0_cs, 0_cs}} ); centiseconds previousDuration = 0_cs; for (const auto& timedPhone : continuousPhones) { diff --git a/rhubarb/src/animation/ShapeRule.h b/rhubarb/src/animation/ShapeRule.h index df34378..41d7a85 100644 --- a/rhubarb/src/animation/ShapeRule.h +++ b/rhubarb/src/animation/ShapeRule.h @@ -1,7 +1,7 @@ #pragma once -#include "core/Phone.h" #include "animationRules.h" +#include "core/Phone.h" #include "time/BoundedTimeline.h" #include "time/ContinuousTimeline.h" #include "time/TimeRange.h" diff --git a/rhubarb/src/animation/animationRules.cpp b/rhubarb/src/animation/animationRules.cpp index bf21363..7a2697e 100644 --- a/rhubarb/src/animation/animationRules.cpp +++ b/rhubarb/src/animation/animationRules.cpp @@ -1,15 +1,17 @@ #include "animationRules.h" -#include -#include "shapeShorthands.h" -#include "tools/array.h" -#include "time/ContinuousTimeline.h" -using std::chrono::duration_cast; -using boost::algorithm::clamp; +#include + +#include "shapeShorthands.h" +#include "time/ContinuousTimeline.h" +#include "tools/array.h" + using boost::optional; +using boost::algorithm::clamp; using std::array; -using std::pair; using std::map; +using std::pair; +using std::chrono::duration_cast; constexpr size_t shapeValueCount = static_cast(Shape::EndSentinel); @@ -32,17 +34,18 @@ Shape getClosestShape(Shape reference, ShapeSet shapes) { // A matrix that for each shape contains all shapes in ascending order of effort required to // move to them - constexpr static array, shapeValueCount> effortMatrix = make_array( - /* A */ make_array(A, X, G, B, C, H, E, D, F), - /* B */ make_array(B, G, A, X, C, H, E, D, F), - /* C */ make_array(C, H, B, G, D, A, X, E, F), - /* D */ make_array(D, C, H, B, G, A, X, E, F), - /* E */ make_array(E, C, H, B, G, A, X, D, F), - /* F */ make_array(F, B, G, A, X, C, H, E, D), - /* G */ make_array(G, A, B, C, H, X, E, D, F), - /* H */ make_array(H, C, B, G, D, A, X, E, F), // Like C - /* X */ make_array(X, A, G, B, C, H, E, D, F) // Like A - ); + constexpr static array, shapeValueCount> effortMatrix = + make_array( + /* A */ make_array(A, X, G, B, C, H, E, D, F), + /* B */ make_array(B, G, A, X, C, H, E, D, F), + /* C */ make_array(C, H, B, G, D, A, X, E, F), + /* D */ make_array(D, C, H, B, G, A, X, E, F), + /* E */ make_array(E, C, H, B, G, A, X, D, F), + /* F */ make_array(F, B, G, A, X, C, H, E, D), + /* G */ make_array(G, A, B, C, H, X, E, D, F), + /* H */ make_array(H, C, B, G, D, A, X, E, F), // Like C + /* X */ make_array(X, A, G, B, C, H, E, D, F) // Like A + ); auto& closestShapes = effortMatrix.at(static_cast(reference)); for (Shape closestShape : closestShapes) { @@ -58,32 +61,31 @@ optional> getTween(Shape first, Shape second) { // Note that most of the following rules work in one direction only. // That's because in animation, the mouth should usually "pop" open without inbetweens, // then close slowly. - static const map, pair> lookup { - { { D, A }, { C, TweenTiming::Early } }, - { { D, B }, { C, TweenTiming::Centered } }, - { { D, G }, { C, TweenTiming::Early } }, - { { D, X }, { C, TweenTiming::Late } }, - { { C, F }, { E, TweenTiming::Centered } }, { { F, C }, { E, TweenTiming::Centered } }, - { { D, F }, { E, TweenTiming::Centered } }, - { { H, F }, { E, TweenTiming::Late } }, { { F, H }, { E, TweenTiming::Early } } + static const map, pair> lookup{ + {{D, A}, {C, TweenTiming::Early}}, + {{D, B}, {C, TweenTiming::Centered}}, + {{D, G}, {C, TweenTiming::Early}}, + {{D, X}, {C, TweenTiming::Late}}, + {{C, F}, {E, TweenTiming::Centered}}, + {{F, C}, {E, TweenTiming::Centered}}, + {{D, F}, {E, TweenTiming::Centered}}, + {{H, F}, {E, TweenTiming::Late}}, + {{F, H}, {E, TweenTiming::Early}} }; - const auto it = lookup.find({ first, second }); + const auto it = lookup.find({first, second}); return it != lookup.end() ? it->second : optional>(); } Timeline getShapeSets(Phone phone, centiseconds duration, centiseconds previousDuration) { // Returns a timeline with a single shape set const auto single = [duration](ShapeSet value) { - return Timeline { { 0_cs, duration, value } }; + return Timeline{{0_cs, duration, value}}; }; // Returns a timeline with two shape sets, timed as a diphthong const auto diphthong = [duration](ShapeSet first, ShapeSet second) { const centiseconds firstDuration = duration_cast(duration * 0.6); - return Timeline { - { 0_cs, firstDuration, first }, - { firstDuration, duration, second } - }; + return Timeline{{0_cs, firstDuration, first}, {firstDuration, duration, second}}; }; // Returns a timeline with two shape sets, timed as a plosive @@ -92,10 +94,7 @@ Timeline getShapeSets(Phone phone, centiseconds duration, centiseconds const centiseconds maxOcclusionDuration = 12_cs; const centiseconds occlusionDuration = clamp(previousDuration / 2, minOcclusionDuration, maxOcclusionDuration); - return Timeline { - { -occlusionDuration, 0_cs, first }, - { 0_cs, duration, second } - }; + return Timeline{{-occlusionDuration, 0_cs, first}, {0_cs, duration, second}}; }; // Returns the result of `getShapeSets` when called with identical arguments @@ -104,8 +103,8 @@ Timeline getShapeSets(Phone phone, centiseconds duration, centiseconds return getShapeSets(referencePhone, duration, previousDuration); }; - static const ShapeSet any { A, B, C, D, E, F, G, H, X }; - static const ShapeSet anyOpen { B, C, D, E, F, G, H }; + static const ShapeSet any{A, B, C, D, E, F, G, H, X}; + static const ShapeSet anyOpen{B, C, D, E, F, G, H}; // Note: // The shapes {A, B, G, X} are very similar. You should avoid regular shape sets containing more @@ -114,52 +113,52 @@ Timeline getShapeSets(Phone phone, centiseconds duration, centiseconds // As an exception, a very flexible rule may contain *all* these shapes. switch (phone) { - case Phone::AO: return single({ E }); - case Phone::AA: return single({ D }); - case Phone::IY: return single({ B }); - case Phone::UW: return single({ F }); - case Phone::EH: return single({ C }); - case Phone::IH: return single({ B }); - case Phone::UH: return single({ F }); - case Phone::AH: return duration < 20_cs ? single({ C }) : single({ D }); - case Phone::Schwa: return single({ B, C }); - case Phone::AE: return single({ C }); - case Phone::EY: return diphthong({ C }, { B }); - case Phone::AY: return duration < 20_cs ? diphthong({ C }, { B }) : diphthong({ D }, { B }); - case Phone::OW: return diphthong({ E }, { F }); - case Phone::AW: return duration < 30_cs ? diphthong({ C }, { E }) : diphthong({ D }, { E }); - case Phone::OY: return diphthong({ E }, { B }); - case Phone::ER: return duration < 7_cs ? like(Phone::Schwa) : single({ E }); + case Phone::AO: return single({E}); + case Phone::AA: return single({D}); + case Phone::IY: return single({B}); + case Phone::UW: return single({F}); + case Phone::EH: return single({C}); + case Phone::IH: return single({B}); + case Phone::UH: return single({F}); + case Phone::AH: return duration < 20_cs ? single({C}) : single({D}); + case Phone::Schwa: return single({B, C}); + case Phone::AE: return single({C}); + case Phone::EY: return diphthong({C}, {B}); + case Phone::AY: return duration < 20_cs ? diphthong({C}, {B}) : diphthong({D}, {B}); + case Phone::OW: return diphthong({E}, {F}); + case Phone::AW: return duration < 30_cs ? diphthong({C}, {E}) : diphthong({D}, {E}); + case Phone::OY: return diphthong({E}, {B}); + case Phone::ER: return duration < 7_cs ? like(Phone::Schwa) : single({E}); case Phone::P: - case Phone::B: return plosive({ A }, any); + case Phone::B: return plosive({A}, any); case Phone::T: - case Phone::D: return plosive({ B, F }, anyOpen); + case Phone::D: return plosive({B, F}, anyOpen); case Phone::K: - case Phone::G: return plosive({ B, C, E, F, H }, anyOpen); + case Phone::G: return plosive({B, C, E, F, H}, anyOpen); case Phone::CH: - case Phone::JH: return single({ B, F }); + case Phone::JH: return single({B, F}); case Phone::F: - case Phone::V: return single({ G }); + case Phone::V: return single({G}); case Phone::TH: case Phone::DH: case Phone::S: case Phone::Z: case Phone::SH: - case Phone::ZH: return single({ B, F }); + case Phone::ZH: return single({B, F}); case Phone::HH: return single(any); // think "m-hm" - case Phone::M: return single({ A }); - case Phone::N: return single({ B, C, F, H }); - case Phone::NG: return single({ B, C, E, F }); - case Phone::L: return duration < 20_cs ? single({ B, E, F, H }) : single({ H }); - case Phone::R: return single({ B, E, F }); - case Phone::Y: return single({ B, C, F }); - case Phone::W: return single({ F }); + case Phone::M: return single({A}); + case Phone::N: return single({B, C, F, H}); + case Phone::NG: return single({B, C, E, F}); + case Phone::L: return duration < 20_cs ? single({B, E, F, H}) : single({H}); + case Phone::R: return single({B, E, F}); + case Phone::Y: return single({B, C, F}); + case Phone::W: return single({F}); case Phone::Breath: case Phone::Cough: - case Phone::Smack: return single({ C }); - case Phone::Noise: return single({ B }); + case Phone::Smack: return single({C}); + case Phone::Noise: return single({B}); default: throw std::invalid_argument("Unexpected phone."); } diff --git a/rhubarb/src/animation/animationRules.h b/rhubarb/src/animation/animationRules.h index 1829835..3ed395e 100644 --- a/rhubarb/src/animation/animationRules.h +++ b/rhubarb/src/animation/animationRules.h @@ -1,9 +1,10 @@ #pragma once #include + +#include "core/Phone.h" #include "core/Shape.h" #include "time/Timeline.h" -#include "core/Phone.h" // Returns the basic shape (A-F) that most closely resembles the specified shape. Shape getBasicShape(Shape shape); diff --git a/rhubarb/src/animation/mouthAnimation.cpp b/rhubarb/src/animation/mouthAnimation.cpp index 5bdc045..89b7139 100644 --- a/rhubarb/src/animation/mouthAnimation.cpp +++ b/rhubarb/src/animation/mouthAnimation.cpp @@ -1,16 +1,16 @@ #include "mouthAnimation.h" -#include "time/timedLogging.h" -#include "ShapeRule.h" -#include "roughAnimation.h" + #include "pauseAnimation.h" -#include "tweening.h" -#include "timingOptimization.h" -#include "targetShapeSet.h" +#include "roughAnimation.h" +#include "ShapeRule.h" #include "staticSegments.h" +#include "targetShapeSet.h" +#include "time/timedLogging.h" +#include "timingOptimization.h" +#include "tweening.h" JoiningContinuousTimeline animate( - const BoundedTimeline& phones, - const ShapeSet& targetShapeSet + const BoundedTimeline& phones, const ShapeSet& targetShapeSet ) { // Create timeline of shape rules ContinuousTimeline shapeRules = getShapeRules(phones); diff --git a/rhubarb/src/animation/mouthAnimation.h b/rhubarb/src/animation/mouthAnimation.h index 1fbece6..32f370f 100644 --- a/rhubarb/src/animation/mouthAnimation.h +++ b/rhubarb/src/animation/mouthAnimation.h @@ -2,10 +2,9 @@ #include "core/Phone.h" #include "core/Shape.h" -#include "time/ContinuousTimeline.h" #include "targetShapeSet.h" +#include "time/ContinuousTimeline.h" JoiningContinuousTimeline animate( - const BoundedTimeline& phones, - const ShapeSet& targetShapeSet + const BoundedTimeline& phones, const ShapeSet& targetShapeSet ); diff --git a/rhubarb/src/animation/pauseAnimation.cpp b/rhubarb/src/animation/pauseAnimation.cpp index 38a56be..40cdccc 100644 --- a/rhubarb/src/animation/pauseAnimation.cpp +++ b/rhubarb/src/animation/pauseAnimation.cpp @@ -1,4 +1,5 @@ #include "pauseAnimation.h" + #include "animationRules.h" Shape getPauseShape(Shape previous, Shape next, centiseconds duration) { @@ -30,7 +31,7 @@ Shape getPauseShape(Shape previous, Shape next, centiseconds duration) { JoiningContinuousTimeline animatePauses(const JoiningContinuousTimeline& animation) { JoiningContinuousTimeline result(animation); - + for_each_adjacent( animation.begin(), animation.end(), diff --git a/rhubarb/src/animation/roughAnimation.cpp b/rhubarb/src/animation/roughAnimation.cpp index 1fe574e..9b34bc6 100644 --- a/rhubarb/src/animation/roughAnimation.cpp +++ b/rhubarb/src/animation/roughAnimation.cpp @@ -1,4 +1,5 @@ #include "roughAnimation.h" + #include // Create timeline of shapes using a bidirectional algorithm. @@ -22,9 +23,8 @@ JoiningContinuousTimeline animateRough(const ContinuousTimelinegetValue(); const Shape shape = getClosestShape(referenceShape, shapeRule.shapeSet); animation.set(it->getTimeRange(), shape); - const bool anticipateShape = shapeRule.phone - && isVowel(*shapeRule.phone) - && shapeRule.shapeSet.size() == 1; + const bool anticipateShape = + shapeRule.phone && isVowel(*shapeRule.phone) && shapeRule.shapeSet.size() == 1; if (anticipateShape) { // Animate backwards a little const Shape anticipatedShape = shape; diff --git a/rhubarb/src/animation/staticSegments.cpp b/rhubarb/src/animation/staticSegments.cpp index dc162f0..3ad8787 100644 --- a/rhubarb/src/animation/staticSegments.cpp +++ b/rhubarb/src/animation/staticSegments.cpp @@ -1,6 +1,8 @@ #include "staticSegments.h" -#include + #include +#include + #include "tools/nextCombination.h" using std::vector; @@ -49,7 +51,7 @@ vector getStaticSegments( result.push_back(timeRange); } } - + return result; } @@ -67,8 +69,8 @@ ShapeRule getChangedShapeRule(const ShapeRule& rule) { ShapeRule result(rule); // So far, I've only encountered B as a static shape. // If there is ever a problem with another static shape, this function can easily be extended. - if (rule.shapeSet == ShapeSet { Shape::B }) { - result.shapeSet = { Shape::C }; + if (rule.shapeSet == ShapeSet{Shape::B}) { + result.shapeSet = {Shape::C}; } return result; } @@ -78,8 +80,7 @@ using RuleChanges = vector; // Replaces the indicated shape rules with slightly different ones, breaking up long static segments ContinuousTimeline applyChanges( - const ContinuousTimeline& shapeRules, - const RuleChanges& changes + const ContinuousTimeline& shapeRules, const RuleChanges& changes ) { ContinuousTimeline result(shapeRules); for (centiseconds changedRuleStart : changes) { @@ -99,8 +100,7 @@ public: ) : changedRules(applyChanges(originalRules, changes)), animation(animate(changedRules)), - staticSegments(getStaticSegments(changedRules, animation)) - {} + staticSegments(getStaticSegments(changedRules, animation)) {} bool isBetterThan(const RuleChangeScenario& rhs) const { // We want zero static segments @@ -132,8 +132,9 @@ private: 0.0, [](const double sum, const Timed& timedShape) { const double duration = std::chrono::duration_cast>( - timedShape.getDuration() - ).count(); + timedShape.getDuration() + ) + .count(); return sum + duration * duration; } ); @@ -152,8 +153,7 @@ RuleChanges getPossibleRuleChanges(const ContinuousTimeline& shapeRul } ContinuousTimeline fixStaticSegmentRules( - const ContinuousTimeline& shapeRules, - const AnimationFunction& animate + const ContinuousTimeline& shapeRules, const AnimationFunction& animate ) { // The complexity of this function is exponential with the number of replacements. // So let's cap that value. @@ -164,23 +164,26 @@ ContinuousTimeline fixStaticSegmentRules( // Find best solution. Start with a single replacement, then increase as necessary. RuleChangeScenario bestScenario(shapeRules, {}, animate); - for ( - int replacementCount = 1; - bestScenario.getStaticSegmentCount() > 0 && replacementCount <= std::min(static_cast(possibleRuleChanges.size()), maxReplacementCount); - ++replacementCount - ) { + for (int replacementCount = 1; bestScenario.getStaticSegmentCount() > 0 + && replacementCount + <= std::min(static_cast(possibleRuleChanges.size()), maxReplacementCount); + ++replacementCount) { // Only the first elements of `currentRuleChanges` count auto currentRuleChanges(possibleRuleChanges); do { RuleChangeScenario currentScenario( shapeRules, - { currentRuleChanges.begin(), currentRuleChanges.begin() + replacementCount }, + {currentRuleChanges.begin(), currentRuleChanges.begin() + replacementCount}, animate ); if (currentScenario.isBetterThan(bestScenario)) { bestScenario = currentScenario; } - } while (next_combination(currentRuleChanges.begin(), currentRuleChanges.begin() + replacementCount, currentRuleChanges.end())); + } while (next_combination( + currentRuleChanges.begin(), + currentRuleChanges.begin() + replacementCount, + currentRuleChanges.end() + )); } return bestScenario.getChangedRules(); @@ -194,8 +197,7 @@ bool isFlexible(const ShapeRule& rule) { // Extends the specified time range until it starts and ends with a non-flexible shape rule, if // possible TimeRange extendToFixedRules( - const TimeRange& timeRange, - const ContinuousTimeline& shapeRules + const TimeRange& timeRange, const ContinuousTimeline& shapeRules ) { auto first = shapeRules.find(timeRange.getStart()); while (first != shapeRules.begin() && isFlexible(first->getValue())) { @@ -205,12 +207,11 @@ TimeRange extendToFixedRules( while (std::next(last) != shapeRules.end() && isFlexible(last->getValue())) { ++last; } - return { first->getStart(), last->getEnd() }; + return {first->getStart(), last->getEnd()}; } JoiningContinuousTimeline avoidStaticSegments( - const ContinuousTimeline& shapeRules, - const AnimationFunction& animate + const ContinuousTimeline& shapeRules, const AnimationFunction& animate ) { const auto animation = animate(shapeRules); const vector staticSegments = getStaticSegments(shapeRules, animation); @@ -227,8 +228,7 @@ JoiningContinuousTimeline avoidStaticSegments( // Fix shape rules within the static segment const auto fixedSegmentShapeRules = fixStaticSegmentRules( - { extendedStaticSegment, ShapeRule::getInvalid(), fixedShapeRules }, - animate + {extendedStaticSegment, ShapeRule::getInvalid(), fixedShapeRules}, animate ); for (const auto& timedShapeRule : fixedSegmentShapeRules) { fixedShapeRules.set(timedShapeRule); diff --git a/rhubarb/src/animation/staticSegments.h b/rhubarb/src/animation/staticSegments.h index 4cfa8d2..a4cc002 100644 --- a/rhubarb/src/animation/staticSegments.h +++ b/rhubarb/src/animation/staticSegments.h @@ -1,18 +1,20 @@ #pragma once -#include "core/Shape.h" -#include "time/ContinuousTimeline.h" -#include "ShapeRule.h" #include -using AnimationFunction = std::function(const ContinuousTimeline&)>; +#include "core/Shape.h" +#include "ShapeRule.h" +#include "time/ContinuousTimeline.h" + +using AnimationFunction = + std::function(const ContinuousTimeline&)>; // Calls the specified animation function with the specified shape rules. // If the resulting animation contains long static segments, the shape rules are tweaked and // animated again. // Static segments happen rather often. -// See http://animateducated.blogspot.de/2016/10/lip-sync-animation-2.html?showComment=1478861729702#c2940729096183546458. +// See +// http://animateducated.blogspot.de/2016/10/lip-sync-animation-2.html?showComment=1478861729702#c2940729096183546458. JoiningContinuousTimeline avoidStaticSegments( - const ContinuousTimeline& shapeRules, - const AnimationFunction& animate + const ContinuousTimeline& shapeRules, const AnimationFunction& animate ); diff --git a/rhubarb/src/animation/targetShapeSet.cpp b/rhubarb/src/animation/targetShapeSet.cpp index d0fb75e..77070a9 100644 --- a/rhubarb/src/animation/targetShapeSet.cpp +++ b/rhubarb/src/animation/targetShapeSet.cpp @@ -7,7 +7,8 @@ Shape convertToTargetShapeSet(Shape shape, const ShapeSet& targetShapeSet) { const Shape basicShape = getBasicShape(shape); if (targetShapeSet.find(basicShape) == targetShapeSet.end()) { throw std::invalid_argument( - fmt::format("Target shape set must contain basic shape {}.", basicShape)); + fmt::format("Target shape set must contain basic shape {}.", basicShape) + ); } return basicShape; } @@ -21,8 +22,7 @@ ShapeSet convertToTargetShapeSet(const ShapeSet& shapes, const ShapeSet& targetS } ContinuousTimeline convertToTargetShapeSet( - const ContinuousTimeline& shapeRules, - const ShapeSet& targetShapeSet + const ContinuousTimeline& shapeRules, const ShapeSet& targetShapeSet ) { ContinuousTimeline result(shapeRules); for (const auto& timedShapeRule : shapeRules) { @@ -34,8 +34,7 @@ ContinuousTimeline convertToTargetShapeSet( } JoiningContinuousTimeline convertToTargetShapeSet( - const JoiningContinuousTimeline& animation, - const ShapeSet& targetShapeSet + const JoiningContinuousTimeline& animation, const ShapeSet& targetShapeSet ) { JoiningContinuousTimeline result(animation); for (const auto& timedShape : animation) { diff --git a/rhubarb/src/animation/targetShapeSet.h b/rhubarb/src/animation/targetShapeSet.h index 5d2286d..af01cf5 100644 --- a/rhubarb/src/animation/targetShapeSet.h +++ b/rhubarb/src/animation/targetShapeSet.h @@ -12,13 +12,11 @@ ShapeSet convertToTargetShapeSet(const ShapeSet& shapes, const ShapeSet& targetS // Replaces each shape in each rule with the closest shape that occurs in the target shape set. ContinuousTimeline convertToTargetShapeSet( - const ContinuousTimeline& shapeRules, - const ShapeSet& targetShapeSet + const ContinuousTimeline& shapeRules, const ShapeSet& targetShapeSet ); // Replaces each shape in the specified animation with the closest shape that occurs in the target // shape set. JoiningContinuousTimeline convertToTargetShapeSet( - const JoiningContinuousTimeline& animation, - const ShapeSet& targetShapeSet + const JoiningContinuousTimeline& animation, const ShapeSet& targetShapeSet ); diff --git a/rhubarb/src/animation/timingOptimization.cpp b/rhubarb/src/animation/timingOptimization.cpp index c28f602..4eb4606 100644 --- a/rhubarb/src/animation/timingOptimization.cpp +++ b/rhubarb/src/animation/timingOptimization.cpp @@ -1,12 +1,14 @@ #include "timingOptimization.h" -#include "time/timedLogging.h" + +#include #include #include -#include -#include "ShapeRule.h" -using std::string; +#include "ShapeRule.h" +#include "time/timedLogging.h" + using std::map; +using std::string; string getShapesString(const JoiningContinuousTimeline& shapes) { string result; @@ -32,8 +34,9 @@ Shape getRepresentativeShape(const JoiningTimeline& timeline) { // Select shape with highest total duration within the candidate range const Shape bestShape = std::max_element( - candidateShapeWeights.begin(), candidateShapeWeights.end(), - [](auto a, auto b) { return a.second < b.second; } + candidateShapeWeights.begin(), + candidateShapeWeights.end(), + [](auto a, auto b) { return a.second < b.second; } )->first; // Shapes C and D are similar, but D is more interesting. @@ -55,8 +58,11 @@ struct ShapeReduction { // Returns a time range of candidate shapes for the next shape to draw. // Guaranteed to be non-empty. -TimeRange getNextMinimalCandidateRange(const JoiningContinuousTimeline& sourceShapes, - const TimeRange targetRange, const centiseconds writePosition) { +TimeRange getNextMinimalCandidateRange( + const JoiningContinuousTimeline& sourceShapes, + const TimeRange targetRange, + const centiseconds writePosition +) { if (sourceShapes.empty()) { throw std::invalid_argument("Cannot determine candidate range for empty source timeline."); } @@ -69,9 +75,8 @@ TimeRange getNextMinimalCandidateRange(const JoiningContinuousTimeline& s const centiseconds remainingTargetDuration = writePosition - targetRange.getStart(); const bool canFitOneOrLess = remainingTargetDuration <= minShapeDuration; const bool canFitTwo = remainingTargetDuration >= 2 * minShapeDuration; - const centiseconds duration = canFitOneOrLess || canFitTwo - ? minShapeDuration - : remainingTargetDuration / 2; + const centiseconds duration = + canFitOneOrLess || canFitTwo ? minShapeDuration : remainingTargetDuration / 2; TimeRange candidateRange(writePosition - duration, writePosition); if (writePosition == targetRange.getEnd()) { @@ -102,22 +107,24 @@ ShapeReduction getNextShapeReduction( // Determine the next time range of candidate shapes. Consider two scenarios: // ... the shortest-possible candidate range - const ShapeReduction minReduction(sourceShapes, - getNextMinimalCandidateRange(sourceShapes, targetRange, writePosition)); + const ShapeReduction minReduction( + sourceShapes, getNextMinimalCandidateRange(sourceShapes, targetRange, writePosition) + ); // ... a candidate range extended to the left to fully encompass its left-most shape - const ShapeReduction extendedReduction(sourceShapes, - { - minReduction.sourceShapes.begin()->getStart(), - minReduction.sourceShapes.getRange().getEnd() - } + const ShapeReduction extendedReduction( + sourceShapes, + {minReduction.sourceShapes.begin()->getStart(), + minReduction.sourceShapes.getRange().getEnd()} ); // Determine the shape that might be picked *next* if we choose the shortest-possible candidate // range now const ShapeReduction nextReduction( sourceShapes, - getNextMinimalCandidateRange(sourceShapes, targetRange, minReduction.sourceShapes.getRange().getStart()) + getNextMinimalCandidateRange( + sourceShapes, targetRange, minReduction.sourceShapes.getRange().getStart() + ) ); const bool minEqualsExtended = minReduction.shape == extendedReduction.shape; @@ -129,8 +136,9 @@ ShapeReduction getNextShapeReduction( // Modifies the timing of the given animation to fit into the specified target time range without // jitter. -JoiningContinuousTimeline retime(const JoiningContinuousTimeline& sourceShapes, - const TimeRange targetRange) { +JoiningContinuousTimeline retime( + const JoiningContinuousTimeline& sourceShapes, const TimeRange targetRange +) { logTimedEvent("segment", targetRange, getShapesString(sourceShapes)); JoiningContinuousTimeline result(targetRange, Shape::X); @@ -139,7 +147,6 @@ JoiningContinuousTimeline retime(const JoiningContinuousTimeline& // Animate backwards centiseconds writePosition = targetRange.getEnd(); while (writePosition > targetRange.getStart()) { - // Decide which shape to show next, possibly discarding short shapes const ShapeReduction shapeReduction = getNextShapeReduction(sourceShapes, targetRange, writePosition); @@ -162,31 +169,22 @@ JoiningContinuousTimeline retime(const JoiningContinuousTimeline& } JoiningContinuousTimeline retime( - const JoiningContinuousTimeline& animation, - TimeRange sourceRange, - TimeRange targetRange + const JoiningContinuousTimeline& animation, TimeRange sourceRange, TimeRange targetRange ) { const auto sourceShapes = JoiningContinuousTimeline(sourceRange, Shape::X, animation); return retime(sourceShapes, targetRange); } -enum class MouthState { - Idle, - Closed, - Open -}; +enum class MouthState { Idle, Closed, Open }; JoiningContinuousTimeline optimizeTiming(const JoiningContinuousTimeline& animation) { // Identify segments with idle, closed, and open mouth shapes JoiningContinuousTimeline segments(animation.getRange(), MouthState::Idle); for (const auto& timedShape : animation) { const Shape shape = timedShape.getValue(); - const MouthState mouthState = - shape == Shape::X - ? MouthState::Idle - : shape == Shape::A - ? MouthState::Closed - : MouthState::Open; + const MouthState mouthState = shape == Shape::X ? MouthState::Idle + : shape == Shape::A ? MouthState::Closed + : MouthState::Open; segments.set(timedShape.getTimeRange(), mouthState); } @@ -219,11 +217,8 @@ JoiningContinuousTimeline optimizeTiming(const JoiningContinuousTimeline< // evenly. const auto begin = segmentIt; auto end = std::next(begin); - while ( - end != segments.rend() - && end->getValue() != MouthState::Idle - && end->getDuration() < minSegmentDuration - ) { + while (end != segments.rend() && end->getValue() != MouthState::Idle + && end->getDuration() < minSegmentDuration) { ++end; } @@ -232,20 +227,19 @@ JoiningContinuousTimeline optimizeTiming(const JoiningContinuousTimeline< const centiseconds desiredDuration = minSegmentDuration * shortSegmentCount; const centiseconds currentDuration = begin->getEnd() - std::prev(end)->getStart(); const centiseconds desiredExtensionDuration = desiredDuration - currentDuration; - const centiseconds availableExtensionDuration = end != segments.rend() - ? end->getDuration() - 1_cs - : 0_cs; - const centiseconds extensionDuration = std::min({ - desiredExtensionDuration, availableExtensionDuration, maxExtensionDuration - }); + const centiseconds availableExtensionDuration = + end != segments.rend() ? end->getDuration() - 1_cs : 0_cs; + const centiseconds extensionDuration = std::min( + {desiredExtensionDuration, availableExtensionDuration, maxExtensionDuration} + ); // Distribute available time range evenly among all short segments const centiseconds shortSegmentsTargetStart = std::prev(end)->getStart() - extensionDuration; for (auto shortSegmentIt = begin; shortSegmentIt != end; ++shortSegmentIt) { size_t remainingShortSegmentCount = std::distance(shortSegmentIt, end); - const centiseconds segmentDuration = (resultStart - shortSegmentsTargetStart) / - remainingShortSegmentCount; + const centiseconds segmentDuration = + (resultStart - shortSegmentsTargetStart) / remainingShortSegmentCount; const TimeRange segmentTargetRange(resultStart - segmentDuration, resultStart); const auto retimedSegment = retime(animation, shortSegmentIt->getTimeRange(), segmentTargetRange); diff --git a/rhubarb/src/animation/tweening.cpp b/rhubarb/src/animation/tweening.cpp index 15f4af3..1b24bbe 100644 --- a/rhubarb/src/animation/tweening.cpp +++ b/rhubarb/src/animation/tweening.cpp @@ -1,4 +1,5 @@ #include "tweening.h" + #include "animationRules.h" JoiningContinuousTimeline insertTweens(const JoiningContinuousTimeline& animation) { @@ -7,48 +8,50 @@ JoiningContinuousTimeline insertTweens(const JoiningContinuousTimeline result(animation); - for_each_adjacent(animation.begin(), animation.end(), [&](const auto& first, const auto& second) { - auto pair = getTween(first.getValue(), second.getValue()); - if (!pair) return; + for_each_adjacent( + animation.begin(), + animation.end(), + [&](const auto& first, const auto& second) { + auto pair = getTween(first.getValue(), second.getValue()); + if (!pair) return; - Shape tweenShape; - TweenTiming tweenTiming; - std::tie(tweenShape, tweenTiming) = *pair; - TimeRange firstTimeRange = first.getTimeRange(); - TimeRange secondTimeRange = second.getTimeRange(); + Shape tweenShape; + TweenTiming tweenTiming; + std::tie(tweenShape, tweenTiming) = *pair; + TimeRange firstTimeRange = first.getTimeRange(); + TimeRange secondTimeRange = second.getTimeRange(); - centiseconds tweenStart, tweenDuration; - switch (tweenTiming) { - case TweenTiming::Early: - { - tweenDuration = std::min(firstTimeRange.getDuration() / 3, maxTweenDuration); - tweenStart = firstTimeRange.getEnd() - tweenDuration; - break; - } - case TweenTiming::Centered: - { - tweenDuration = std::min({ - firstTimeRange.getDuration() / 4, secondTimeRange.getDuration() / 4, maxTweenDuration - }); - tweenStart = firstTimeRange.getEnd() - tweenDuration / 2; - break; - } - case TweenTiming::Late: - { - tweenDuration = std::min(secondTimeRange.getDuration() / 3, maxTweenDuration); - tweenStart = secondTimeRange.getStart(); - break; - } - default: - { - throw std::runtime_error("Unexpected tween timing."); + centiseconds tweenStart, tweenDuration; + switch (tweenTiming) { + case TweenTiming::Early: { + tweenDuration = std::min(firstTimeRange.getDuration() / 3, maxTweenDuration); + tweenStart = firstTimeRange.getEnd() - tweenDuration; + break; + } + case TweenTiming::Centered: { + tweenDuration = std::min( + {firstTimeRange.getDuration() / 4, + secondTimeRange.getDuration() / 4, + maxTweenDuration} + ); + tweenStart = firstTimeRange.getEnd() - tweenDuration / 2; + break; + } + case TweenTiming::Late: { + tweenDuration = std::min(secondTimeRange.getDuration() / 3, maxTweenDuration); + tweenStart = secondTimeRange.getStart(); + break; + } + default: { + throw std::runtime_error("Unexpected tween timing."); + } } + + if (tweenDuration < minTweenDuration) return; + + result.set(tweenStart, tweenStart + tweenDuration, tweenShape); } - - if (tweenDuration < minTweenDuration) return; - - result.set(tweenStart, tweenStart + tweenDuration, tweenShape); - }); + ); return result; } diff --git a/rhubarb/src/audio/AudioClip.cpp b/rhubarb/src/audio/AudioClip.cpp index 78c0e3c..aa485c1 100644 --- a/rhubarb/src/audio/AudioClip.cpp +++ b/rhubarb/src/audio/AudioClip.cpp @@ -1,4 +1,5 @@ #include "AudioClip.h" + #include using std::invalid_argument; @@ -11,6 +12,7 @@ class SafeSampleReader { public: SafeSampleReader(SampleReader unsafeRead, AudioClip::size_type size); AudioClip::value_type operator()(AudioClip::size_type index); + private: SampleReader unsafeRead; AudioClip::size_type size; @@ -20,19 +22,16 @@ private: SafeSampleReader::SafeSampleReader(SampleReader unsafeRead, AudioClip::size_type size) : unsafeRead(unsafeRead), - size(size) -{} + size(size) {} inline AudioClip::value_type SafeSampleReader::operator()(AudioClip::size_type index) { if (index < 0) { throw invalid_argument(fmt::format("Cannot read from sample index {}. Index < 0.", index)); } if (index >= size) { - throw invalid_argument(fmt::format( - "Cannot read from sample index {}. Clip size is {}.", - index, - size - )); + throw invalid_argument( + fmt::format("Cannot read from sample index {}. Clip size is {}.", index, size) + ); } if (index == lastIndex) { return lastSample; @@ -60,10 +59,8 @@ std::unique_ptr operator|(std::unique_ptr clip, const Audi } SampleIterator::SampleIterator() : - sampleIndex(0) -{} + sampleIndex(0) {} SampleIterator::SampleIterator(const AudioClip& audioClip, size_type sampleIndex) : sampleReader([&audioClip] { return audioClip.createSampleReader(); }), - sampleIndex(sampleIndex) -{} + sampleIndex(sampleIndex) {} diff --git a/rhubarb/src/audio/AudioClip.h b/rhubarb/src/audio/AudioClip.h index 22902ed..f542c7f 100644 --- a/rhubarb/src/audio/AudioClip.h +++ b/rhubarb/src/audio/AudioClip.h @@ -1,8 +1,9 @@ #pragma once -#include -#include "time/TimeRange.h" #include +#include + +#include "time/TimeRange.h" #include "tools/Lazy.h" class AudioClip; @@ -17,6 +18,7 @@ public: using SampleReader = std::function; virtual ~AudioClip() {} + virtual std::unique_ptr clone() const = 0; virtual int getSampleRate() const = 0; virtual size_type size() const = 0; @@ -24,6 +26,7 @@ public: SampleReader createSampleReader() const; iterator begin() const; iterator end() const; + private: virtual SampleReader createUnsafeSampleReader() const = 0; }; @@ -137,6 +140,8 @@ inline SampleIterator operator-(const SampleIterator& it, SampleIterator::differ return result; } -inline SampleIterator::difference_type operator-(const SampleIterator& lhs, const SampleIterator& rhs) { +inline SampleIterator::difference_type operator-( + const SampleIterator& lhs, const SampleIterator& rhs +) { return lhs.getSampleIndex() - rhs.getSampleIndex(); } diff --git a/rhubarb/src/audio/AudioSegment.cpp b/rhubarb/src/audio/AudioSegment.cpp index 503c242..c87a4a9 100644 --- a/rhubarb/src/audio/AudioSegment.cpp +++ b/rhubarb/src/audio/AudioSegment.cpp @@ -1,13 +1,16 @@ #include "AudioSegment.h" -using std::unique_ptr; using std::make_unique; +using std::unique_ptr; AudioSegment::AudioSegment(std::unique_ptr inputClip, const TimeRange& range) : inputClip(std::move(inputClip)), - sampleOffset(static_cast(range.getStart().count()) * this->inputClip->getSampleRate() / 100), - sampleCount(static_cast(range.getDuration().count()) * this->inputClip->getSampleRate() / 100) -{ + sampleOffset( + static_cast(range.getStart().count()) * this->inputClip->getSampleRate() / 100 + ), + sampleCount( + static_cast(range.getDuration().count()) * this->inputClip->getSampleRate() / 100 + ) { if (sampleOffset < 0 || sampleOffset + sampleCount > this->inputClip->size()) { throw std::invalid_argument("Segment extends beyond input clip."); } diff --git a/rhubarb/src/audio/DcOffset.cpp b/rhubarb/src/audio/DcOffset.cpp index d4b8e9a..d76ddfe 100644 --- a/rhubarb/src/audio/DcOffset.cpp +++ b/rhubarb/src/audio/DcOffset.cpp @@ -1,28 +1,26 @@ #include "DcOffset.h" + #include -using std::unique_ptr; using std::make_unique; +using std::unique_ptr; DcOffset::DcOffset(unique_ptr inputClip, float offset) : inputClip(std::move(inputClip)), offset(offset), - factor(1 / (1 + std::abs(offset))) -{} + factor(1 / (1 + std::abs(offset))) {} unique_ptr DcOffset::clone() const { return make_unique(*this); } SampleReader DcOffset::createUnsafeSampleReader() const { - return [ - read = inputClip->createSampleReader(), - factor = factor, - offset = offset - ](size_type index) { - const float sample = read(index); - return sample * factor + offset; - }; + return + [read = inputClip->createSampleReader(), factor = factor, offset = offset](size_type index + ) { + const float sample = read(index); + return sample * factor + offset; + }; } float getDcOffset(const AudioClip& audioClip) { diff --git a/rhubarb/src/audio/DcOffset.h b/rhubarb/src/audio/DcOffset.h index d5090da..c46fcb8 100644 --- a/rhubarb/src/audio/DcOffset.h +++ b/rhubarb/src/audio/DcOffset.h @@ -10,6 +10,7 @@ public: std::unique_ptr clone() const override; int getSampleRate() const override; size_type size() const override; + private: SampleReader createUnsafeSampleReader() const override; diff --git a/rhubarb/src/audio/OggVorbisFileReader.cpp b/rhubarb/src/audio/OggVorbisFileReader.cpp index f9107bd..0035741 100644 --- a/rhubarb/src/audio/OggVorbisFileReader.cpp +++ b/rhubarb/src/audio/OggVorbisFileReader.cpp @@ -1,47 +1,40 @@ #include "OggVorbisFileReader.h" +#include + +#include "tools/fileTools.h" +#include "tools/tools.h" #include "vorbis/codec.h" #include "vorbis/vorbisfile.h" -#include "tools/tools.h" -#include -#include "tools/fileTools.h" -using std::filesystem::path; -using std::vector; -using std::make_shared; using std::ifstream; using std::ios_base; +using std::make_shared; +using std::vector; +using std::filesystem::path; std::string vorbisErrorToString(int64_t errorCode) { switch (errorCode) { - case OV_EREAD: - return "Read error while fetching compressed data for decode."; - case OV_EFAULT: - return "Internal logic fault; indicates a bug or heap/stack corruption."; - case OV_EIMPL: - return "Feature not implemented"; + case OV_EREAD: return "Read error while fetching compressed data for decode."; + case OV_EFAULT: return "Internal logic fault; indicates a bug or heap/stack corruption."; + case OV_EIMPL: return "Feature not implemented"; case OV_EINVAL: return "Either an invalid argument, or incompletely initialized argument passed to a call."; - case OV_ENOTVORBIS: - return "The given file/data was not recognized as Ogg Vorbis data."; + case OV_ENOTVORBIS: return "The given file/data was not recognized as Ogg Vorbis data."; case OV_EBADHEADER: return "The file/data is apparently an Ogg Vorbis stream, but contains a corrupted or undecipherable header."; case OV_EVERSION: return "The bitstream format revision of the given Vorbis stream is not supported."; - case OV_ENOTAUDIO: - return "Packet is not an audio packet."; - case OV_EBADPACKET: - return "Error in packet."; + case OV_ENOTAUDIO: return "Packet is not an audio packet."; + case OV_EBADPACKET: return "Error in packet."; case OV_EBADLINK: return "The given link exists in the Vorbis data stream, but is not decipherable due to garbage or corruption."; - case OV_ENOSEEK: - return "The given stream is not seekable."; - default: - return "An unexpected Vorbis error occurred."; + case OV_ENOSEEK: return "The given stream is not seekable."; + default: return "An unexpected Vorbis error occurred."; } } -template +template T throwOnError(T code) { // OV_HOLE, though technically an error code, is only informational const bool error = code < 0 && code != OV_HOLE; @@ -64,7 +57,7 @@ size_t readCallback(void* buffer, size_t elementSize, size_t elementCount, void* } int seekCallback(void* dataSource, ogg_int64_t offset, int origin) { - static const vector seekDirections { + static const vector seekDirections{ ios_base::beg, ios_base::cur, ios_base::end }; @@ -104,8 +97,7 @@ private: OggVorbisFile::OggVorbisFile(const path& filePath) : oggVorbisHandle(), - stream(openFile(filePath)) -{ + stream(openFile(filePath)) { // Throw only on badbit, not on failbit. // Ogg Vorbis expects read operations past the end of the file to // succeed, not to throw. @@ -114,19 +106,18 @@ OggVorbisFile::OggVorbisFile(const path& filePath) : // Ogg Vorbis normally uses the `FILE` API from the C standard library. // This doesn't handle Unicode paths on Windows. // Use wrapper functions around `ifstream` instead. - const ov_callbacks callbacks { readCallback, seekCallback, nullptr, tellCallback }; + const ov_callbacks callbacks{readCallback, seekCallback, nullptr, tellCallback}; throwOnError(ov_open_callbacks(&stream, &oggVorbisHandle, nullptr, 0, callbacks)); } OggVorbisFileReader::OggVorbisFileReader(const path& filePath) : - filePath(filePath) -{ + filePath(filePath) { OggVorbisFile file(filePath); - + vorbis_info* vorbisInfo = ov_info(file.get(), -1); sampleRate = vorbisInfo->rate; channelCount = vorbisInfo->channels; - + sampleCount = throwOnError(ov_pcm_total(file.get(), -1)); } @@ -135,13 +126,11 @@ std::unique_ptr OggVorbisFileReader::clone() const { } SampleReader OggVorbisFileReader::createUnsafeSampleReader() const { - return [ - channelCount = channelCount, - file = make_shared(filePath), - buffer = static_cast(nullptr), - bufferStart = size_type(0), - bufferSize = size_type(0) - ](size_type index) mutable { + return [channelCount = channelCount, + file = make_shared(filePath), + buffer = static_cast(nullptr), + bufferStart = size_type(0), + bufferSize = size_type(0)](size_type index) mutable { if (index < bufferStart || index >= bufferStart + bufferSize) { // Seek throwOnError(ov_pcm_seek(file->get(), index)); diff --git a/rhubarb/src/audio/OggVorbisFileReader.h b/rhubarb/src/audio/OggVorbisFileReader.h index 0950f6e..cb2c85f 100644 --- a/rhubarb/src/audio/OggVorbisFileReader.h +++ b/rhubarb/src/audio/OggVorbisFileReader.h @@ -1,14 +1,21 @@ #pragma once -#include "AudioClip.h" #include +#include "AudioClip.h" + class OggVorbisFileReader : public AudioClip { public: OggVorbisFileReader(const std::filesystem::path& filePath); std::unique_ptr clone() const override; - int getSampleRate() const override { return sampleRate; } - size_type size() const override { return sampleCount; } + + int getSampleRate() const override { + return sampleRate; + } + + size_type size() const override { + return sampleCount; + } private: SampleReader createUnsafeSampleReader() const override; diff --git a/rhubarb/src/audio/SampleRateConverter.cpp b/rhubarb/src/audio/SampleRateConverter.cpp index e4d9395..9a52b80 100644 --- a/rhubarb/src/audio/SampleRateConverter.cpp +++ b/rhubarb/src/audio/SampleRateConverter.cpp @@ -1,25 +1,25 @@ -#include #include "SampleRateConverter.h" -#include + #include +#include +#include + using std::invalid_argument; -using std::unique_ptr; using std::make_unique; +using std::unique_ptr; SampleRateConverter::SampleRateConverter(unique_ptr inputClip, int outputSampleRate) : inputClip(std::move(inputClip)), downscalingFactor(static_cast(this->inputClip->getSampleRate()) / outputSampleRate), outputSampleRate(outputSampleRate), - outputSampleCount(std::lround(this->inputClip->size() / downscalingFactor)) -{ + outputSampleCount(std::lround(this->inputClip->size() / downscalingFactor)) { if (outputSampleRate <= 0) { throw invalid_argument("Sample rate must be positive."); } if (this->inputClip->getSampleRate() < outputSampleRate) { throw invalid_argument(fmt::format( - "Upsampling not supported. Input sample rate must not be below {}Hz.", - outputSampleRate + "Upsampling not supported. Input sample rate must not be below {}Hz.", outputSampleRate )); } } @@ -51,11 +51,9 @@ float mean(double inputStart, double inputEnd, const SampleReader& read) { } SampleReader SampleRateConverter::createUnsafeSampleReader() const { - return [ - read = inputClip->createSampleReader(), - downscalingFactor = downscalingFactor, - size = inputClip->size() - ](size_type index) { + return [read = inputClip->createSampleReader(), + downscalingFactor = downscalingFactor, + size = inputClip->size()](size_type index) { const double inputStart = index * downscalingFactor; const double inputEnd = std::min((index + 1) * downscalingFactor, static_cast(size)); diff --git a/rhubarb/src/audio/SampleRateConverter.h b/rhubarb/src/audio/SampleRateConverter.h index 8888767..06d6732 100644 --- a/rhubarb/src/audio/SampleRateConverter.h +++ b/rhubarb/src/audio/SampleRateConverter.h @@ -1,6 +1,7 @@ #pragma once #include + #include "AudioClip.h" class SampleRateConverter : public AudioClip { @@ -9,6 +10,7 @@ public: std::unique_ptr clone() const override; int getSampleRate() const override; size_type size() const override; + private: SampleReader createUnsafeSampleReader() const override; diff --git a/rhubarb/src/audio/WaveFileReader.cpp b/rhubarb/src/audio/WaveFileReader.cpp index bebae1a..72e32f1 100644 --- a/rhubarb/src/audio/WaveFileReader.cpp +++ b/rhubarb/src/audio/WaveFileReader.cpp @@ -1,19 +1,22 @@ -#include #include "WaveFileReader.h" -#include "ioTools.h" -#include -#include "tools/platformTools.h" -#include "tools/fileTools.h" -using std::runtime_error; +#include + +#include + +#include "ioTools.h" +#include "tools/fileTools.h" +#include "tools/platformTools.h" + using fmt::format; +using std::runtime_error; using std::string; using namespace little_endian; -using std::unique_ptr; -using std::make_unique; using std::make_shared; -using std::filesystem::path; +using std::make_unique; using std::streamoff; +using std::unique_ptr; +using std::filesystem::path; #define INT24_MIN (-8388608) #define INT24_MAX 8388607 @@ -31,15 +34,15 @@ streamoff roundUpToEven(streamoff i) { } namespace Codec { - constexpr int Pcm = 0x01; - constexpr int Float = 0x03; - constexpr int Extensible = 0xFFFE; -}; +constexpr int Pcm = 0x01; +constexpr int Float = 0x03; +constexpr int Extensible = 0xFFFE; +}; // namespace Codec string codecToString(int codec); WaveFormatInfo getWaveFormatInfo(const path& filePath) { - WaveFormatInfo formatInfo {}; + WaveFormatInfo formatInfo{}; auto file = openFile(filePath); @@ -74,8 +77,7 @@ WaveFormatInfo getWaveFormatInfo(const path& filePath) { const streamoff chunkSize = read(file); const streamoff chunkEnd = roundUpToEven(file.tellg() + chunkSize); switch (chunkId) { - case fourcc('f', 'm', 't', ' '): - { + case fourcc('f', 'm', 't', ' '): { // Read relevant data uint16_t codec = read(file); formatInfo.channelCount = read(file); @@ -118,7 +120,8 @@ WaveFormatInfo getWaveFormatInfo(const path& filePath) { bytesPerSample = 4; } else { throw runtime_error( - format("Unsupported sample format: {}-bit PCM.", bitsPerSample)); + format("Unsupported sample format: {}-bit PCM.", bitsPerSample) + ); } if (bytesPerSample != bytesPerFrame / formatInfo.channelCount) { throw runtime_error("Unsupported sample organization."); @@ -132,30 +135,30 @@ WaveFormatInfo getWaveFormatInfo(const path& filePath) { formatInfo.sampleFormat = SampleFormat::Float64; bytesPerSample = 8; } else { - throw runtime_error( - format("Unsupported sample format: {}-bit IEEE Float.", bitsPerSample) - ); + throw runtime_error(format( + "Unsupported sample format: {}-bit IEEE Float.", bitsPerSample + )); } break; default: throw runtime_error(format( "Unsupported audio codec: '{}'. Only uncompressed codecs ('{}' and '{}') are supported.", - codecToString(codec), codecToString(Codec::Pcm), codecToString(Codec::Float) + codecToString(codec), + codecToString(Codec::Pcm), + codecToString(Codec::Float) )); } formatInfo.bytesPerFrame = bytesPerSample * formatInfo.channelCount; processedFormatChunk = true; break; } - case fourcc('d', 'a', 't', 'a'): - { + case fourcc('d', 'a', 't', 'a'): { formatInfo.dataOffset = file.tellg(); formatInfo.frameCount = chunkSize / formatInfo.bytesPerFrame; processedDataChunk = true; break; } - default: - { + default: { // Ignore unknown chunk break; } @@ -180,45 +183,37 @@ unique_ptr WaveFileReader::clone() const { } inline AudioClip::value_type readSample( - std::ifstream& file, - SampleFormat sampleFormat, - int channelCount + std::ifstream& file, SampleFormat sampleFormat, int channelCount ) { float sum = 0; for (int channelIndex = 0; channelIndex < channelCount; channelIndex++) { switch (sampleFormat) { - case SampleFormat::UInt8: - { + case SampleFormat::UInt8: { const uint8_t raw = read(file); sum += toNormalizedFloat(raw, 0, UINT8_MAX); break; } - case SampleFormat::Int16: - { + case SampleFormat::Int16: { const int16_t raw = read(file); sum += toNormalizedFloat(raw, INT16_MIN, INT16_MAX); break; } - case SampleFormat::Int24: - { + case SampleFormat::Int24: { int raw = read(file); if (raw & 0x800000) raw |= 0xFF000000; // Fix two's complement sum += toNormalizedFloat(raw, INT24_MIN, INT24_MAX); break; } - case SampleFormat::Int32: - { + case SampleFormat::Int32: { const int32_t raw = read(file); sum += toNormalizedFloat(raw, INT32_MIN, INT32_MAX); break; } - case SampleFormat::Float32: - { + case SampleFormat::Float32: { sum += read(file); break; } - case SampleFormat::Float64: - { + case SampleFormat::Float64: { sum += static_cast(read(file)); break; } @@ -229,14 +224,11 @@ inline AudioClip::value_type readSample( } SampleReader WaveFileReader::createUnsafeSampleReader() const { - return - [ - formatInfo = formatInfo, + return [formatInfo = formatInfo, file = std::make_shared(openFile(filePath)), - filePos = std::streampos(0) - ](size_type index) mutable { - const std::streampos newFilePos = formatInfo.dataOffset - + static_cast(index * formatInfo.bytesPerFrame); + filePos = std::streampos(0)](size_type index) mutable { + const std::streampos newFilePos = + formatInfo.dataOffset + static_cast(index * formatInfo.bytesPerFrame); if (newFilePos != filePos) { file->seekg(newFilePos); } @@ -491,7 +483,6 @@ string codecToString(int codec) { case 0xf1ac: return "Free Lossless Audio Codec FLAC"; case 0xfffe: return "Extensible"; case 0xffff: return "Development"; - default: - return format("{0:#x}", codec); + default: return format("{0:#x}", codec); } } diff --git a/rhubarb/src/audio/WaveFileReader.h b/rhubarb/src/audio/WaveFileReader.h index 1cafc5e..c412e33 100644 --- a/rhubarb/src/audio/WaveFileReader.h +++ b/rhubarb/src/audio/WaveFileReader.h @@ -1,16 +1,10 @@ #pragma once #include + #include "AudioClip.h" -enum class SampleFormat { - UInt8, - Int16, - Int24, - Int32, - Float32, - Float64 -}; +enum class SampleFormat { UInt8, Int16, Int24, Int32, Float32, Float64 }; struct WaveFormatInfo { int bytesPerFrame; diff --git a/rhubarb/src/audio/audioFileReading.cpp b/rhubarb/src/audio/audioFileReading.cpp index 9df3e75..4da04db 100644 --- a/rhubarb/src/audio/audioFileReading.cpp +++ b/rhubarb/src/audio/audioFileReading.cpp @@ -1,18 +1,20 @@ #include "audioFileReading.h" -#include -#include "WaveFileReader.h" -#include -#include "OggVorbisFileReader.h" -using std::filesystem::path; -using std::string; -using std::runtime_error; +#include + +#include + +#include "OggVorbisFileReader.h" +#include "WaveFileReader.h" + using fmt::format; +using std::runtime_error; +using std::string; +using std::filesystem::path; std::unique_ptr createAudioFileClip(path filePath) { try { - const string extension = - boost::algorithm::to_lower_copy(filePath.extension().u8string()); + const string extension = boost::algorithm::to_lower_copy(filePath.extension().u8string()); if (extension == ".wav") { return std::make_unique(filePath); } @@ -24,6 +26,8 @@ std::unique_ptr createAudioFileClip(path filePath) { extension )); } catch (...) { - std::throw_with_nested(runtime_error(format("Could not open sound file {}.", filePath.u8string()))); + std::throw_with_nested( + runtime_error(format("Could not open sound file {}.", filePath.u8string())) + ); } } diff --git a/rhubarb/src/audio/audioFileReading.h b/rhubarb/src/audio/audioFileReading.h index 78beef2..ba5fb37 100644 --- a/rhubarb/src/audio/audioFileReading.h +++ b/rhubarb/src/audio/audioFileReading.h @@ -1,7 +1,8 @@ #pragma once -#include -#include "AudioClip.h" #include +#include + +#include "AudioClip.h" std::unique_ptr createAudioFileClip(std::filesystem::path filePath); diff --git a/rhubarb/src/audio/ioTools.h b/rhubarb/src/audio/ioTools.h index 3534d3a..8eb0b9f 100644 --- a/rhubarb/src/audio/ioTools.h +++ b/rhubarb/src/audio/ioTools.h @@ -4,43 +4,38 @@ namespace little_endian { - template - Type read(std::istream& stream) { - static_assert(bitsToRead % 8 == 0, "Cannot read fractional bytes."); - static_assert(bitsToRead <= sizeof(Type) * 8, "Bits to read exceed target type size."); +template +Type read(std::istream& stream) { + static_assert(bitsToRead % 8 == 0, "Cannot read fractional bytes."); + static_assert(bitsToRead <= sizeof(Type) * 8, "Bits to read exceed target type size."); - Type result = 0; - char* p = reinterpret_cast(&result); - const int bytesToRead = bitsToRead / 8; - for (int byteIndex = 0; byteIndex < bytesToRead; byteIndex++) { - *(p + byteIndex) = static_cast(stream.get()); - } - return result; + Type result = 0; + char* p = reinterpret_cast(&result); + const int bytesToRead = bitsToRead / 8; + for (int byteIndex = 0; byteIndex < bytesToRead; byteIndex++) { + *(p + byteIndex) = static_cast(stream.get()); } - - template - void write(Type value, std::ostream& stream) { - static_assert(bitsToWrite % 8 == 0, "Cannot write fractional bytes."); - static_assert(bitsToWrite <= sizeof(Type) * 8, "Bits to write exceed target type size."); - - char* p = reinterpret_cast(&value); - const int bytesToWrite = bitsToWrite / 8; - for (int byteIndex = 0; byteIndex < bytesToWrite; byteIndex++) { - stream.put(*(p + byteIndex)); - } - } - - constexpr uint32_t fourcc( - unsigned char c0, - unsigned char c1, - unsigned char c2, - unsigned char c3 - ) { - return c0 | (c1 << 8) | (c2 << 16) | (c3 << 24); - } - - inline std::string fourccToString(uint32_t fourcc) { - return std::string(reinterpret_cast(&fourcc), 4); - } - + return result; } + +template +void write(Type value, std::ostream& stream) { + static_assert(bitsToWrite % 8 == 0, "Cannot write fractional bytes."); + static_assert(bitsToWrite <= sizeof(Type) * 8, "Bits to write exceed target type size."); + + char* p = reinterpret_cast(&value); + const int bytesToWrite = bitsToWrite / 8; + for (int byteIndex = 0; byteIndex < bytesToWrite; byteIndex++) { + stream.put(*(p + byteIndex)); + } +} + +constexpr uint32_t fourcc(unsigned char c0, unsigned char c1, unsigned char c2, unsigned char c3) { + return c0 | (c1 << 8) | (c2 << 16) | (c3 << 24); +} + +inline std::string fourccToString(uint32_t fourcc) { + return std::string(reinterpret_cast(&fourcc), 4); +} + +} // namespace little_endian diff --git a/rhubarb/src/audio/processing.cpp b/rhubarb/src/audio/processing.cpp index 3afb673..80890dd 100644 --- a/rhubarb/src/audio/processing.cpp +++ b/rhubarb/src/audio/processing.cpp @@ -1,4 +1,5 @@ #include "processing.h" + #include using std::function; @@ -35,7 +36,9 @@ void process16bitAudioClip( processBuffer(buffer); sampleCount += buffer.size(); - progressSink.reportProgress(static_cast(sampleCount) / static_cast(audioClip.size())); + progressSink.reportProgress( + static_cast(sampleCount) / static_cast(audioClip.size()) + ); } while (!buffer.empty()); } diff --git a/rhubarb/src/audio/processing.h b/rhubarb/src/audio/processing.h index 5ce2488..30da988 100644 --- a/rhubarb/src/audio/processing.h +++ b/rhubarb/src/audio/processing.h @@ -1,7 +1,8 @@ #pragma once -#include #include +#include + #include "AudioClip.h" #include "tools/progress.h" @@ -18,4 +19,4 @@ void process16bitAudioClip( ProgressSink& progressSink ); -std::vector copyTo16bitBuffer(const AudioClip& audioClip); \ No newline at end of file +std::vector copyTo16bitBuffer(const AudioClip& audioClip); diff --git a/rhubarb/src/audio/voiceActivityDetection.cpp b/rhubarb/src/audio/voiceActivityDetection.cpp index d57c4c6..abc1a35 100644 --- a/rhubarb/src/audio/voiceActivityDetection.cpp +++ b/rhubarb/src/audio/voiceActivityDetection.cpp @@ -1,30 +1,31 @@ #include "voiceActivityDetection.h" -#include "DcOffset.h" -#include "SampleRateConverter.h" -#include "logging/logging.h" -#include "tools/pairs.h" -#include -#include -#include "processing.h" + #include -#include "tools/parallel.h" +#include #include -using std::vector; +#include + +#include "DcOffset.h" +#include "logging/logging.h" +#include "processing.h" +#include "SampleRateConverter.h" +#include "tools/pairs.h" +#include "tools/parallel.h" + using boost::adaptors::transformed; using fmt::format; using std::runtime_error; using std::unique_ptr; +using std::vector; JoiningBoundedTimeline detectVoiceActivity( - const AudioClip& inputAudioClip, - ProgressSink& progressSink + const AudioClip& inputAudioClip, ProgressSink& progressSink ) { // Prepare audio for VAD constexpr int webRtcSamplingRate = 8000; - const unique_ptr audioClip = inputAudioClip.clone() - | resample(webRtcSamplingRate) - | removeDcOffset(); + const unique_ptr audioClip = + inputAudioClip.clone() | resample(webRtcSamplingRate) | removeDcOffset(); VadInst* vadHandle = WebRtcVad_Create(); if (!vadHandle) throw runtime_error("Error creating WebRTC VAD handle."); @@ -46,12 +47,8 @@ JoiningBoundedTimeline detectVoiceActivity( // WebRTC is picky regarding buffer size if (buffer.size() < frameSize) return; - const int result = WebRtcVad_Process( - vadHandle, - webRtcSamplingRate, - buffer.data(), - buffer.size() - ); + const int result = + WebRtcVad_Process(vadHandle, webRtcSamplingRate, buffer.data(), buffer.size()); if (result == -1) throw runtime_error("Error processing audio buffer using WebRTC VAD."); // Ignore the result of WebRtcVad_Process, instead directly interpret the internal VAD flag. @@ -86,9 +83,12 @@ JoiningBoundedTimeline detectVoiceActivity( logging::debugFormat( "Found {} sections of voice activity: {}", activity.size(), - join(activity | transformed([](const Timed& t) { - return format("{0}-{1}", t.getStart(), t.getEnd()); - }), ", ") + join( + activity | transformed([](const Timed& t) { + return format("{0}-{1}", t.getStart(), t.getEnd()); + }), + ", " + ) ); return activity; diff --git a/rhubarb/src/audio/voiceActivityDetection.h b/rhubarb/src/audio/voiceActivityDetection.h index 6cbd06a..2b2337b 100644 --- a/rhubarb/src/audio/voiceActivityDetection.h +++ b/rhubarb/src/audio/voiceActivityDetection.h @@ -4,6 +4,5 @@ #include "tools/progress.h" JoiningBoundedTimeline detectVoiceActivity( - const AudioClip& audioClip, - ProgressSink& progressSink + const AudioClip& audioClip, ProgressSink& progressSink ); diff --git a/rhubarb/src/audio/waveFileWriting.cpp b/rhubarb/src/audio/waveFileWriting.cpp index fc6fe7a..de552ff 100644 --- a/rhubarb/src/audio/waveFileWriting.cpp +++ b/rhubarb/src/audio/waveFileWriting.cpp @@ -1,5 +1,7 @@ -#include #include "waveFileWriting.h" + +#include + #include "ioTools.h" using namespace little_endian; diff --git a/rhubarb/src/core/Phone.cpp b/rhubarb/src/core/Phone.cpp index e3aee81..8ff5632 100644 --- a/rhubarb/src/core/Phone.cpp +++ b/rhubarb/src/core/Phone.cpp @@ -1,7 +1,7 @@ #include "Phone.h" -using std::string; using boost::optional; +using std::string; PhoneConverter& PhoneConverter::get() { static PhoneConverter converter; @@ -13,54 +13,24 @@ string PhoneConverter::getTypeName() { } EnumConverter::member_data PhoneConverter::getMemberData() { - return member_data { - { Phone::AO, "AO" }, - { Phone::AA, "AA" }, - { Phone::IY, "IY" }, - { Phone::UW, "UW" }, - { Phone::EH, "EH" }, - { Phone::IH, "IH" }, - { Phone::UH, "UH" }, - { Phone::AH, "AH" }, - { Phone::Schwa, "Schwa" }, - { Phone::AE, "AE" }, - { Phone::EY, "EY" }, - { Phone::AY, "AY" }, - { Phone::OW, "OW" }, - { Phone::AW, "AW" }, - { Phone::OY, "OY" }, - { Phone::ER, "ER" }, + return member_data{{Phone::AO, "AO"}, {Phone::AA, "AA"}, {Phone::IY, "IY"}, + {Phone::UW, "UW"}, {Phone::EH, "EH"}, {Phone::IH, "IH"}, + {Phone::UH, "UH"}, {Phone::AH, "AH"}, {Phone::Schwa, "Schwa"}, + {Phone::AE, "AE"}, {Phone::EY, "EY"}, {Phone::AY, "AY"}, + {Phone::OW, "OW"}, {Phone::AW, "AW"}, {Phone::OY, "OY"}, + {Phone::ER, "ER"}, - { Phone::P, "P" }, - { Phone::B, "B" }, - { Phone::T, "T" }, - { Phone::D, "D" }, - { Phone::K, "K" }, - { Phone::G, "G" }, - { Phone::CH, "CH" }, - { Phone::JH, "JH" }, - { Phone::F, "F" }, - { Phone::V, "V" }, - { Phone::TH, "TH" }, - { Phone::DH, "DH" }, - { Phone::S, "S" }, - { Phone::Z, "Z" }, - { Phone::SH, "SH" }, - { Phone::ZH, "ZH" }, - { Phone::HH, "HH" }, - { Phone::M, "M" }, - { Phone::N, "N" }, - { Phone::NG, "NG" }, - { Phone::L, "L" }, - { Phone::R, "R" }, - { Phone::Y, "Y" }, - { Phone::W, "W" }, + {Phone::P, "P"}, {Phone::B, "B"}, {Phone::T, "T"}, + {Phone::D, "D"}, {Phone::K, "K"}, {Phone::G, "G"}, + {Phone::CH, "CH"}, {Phone::JH, "JH"}, {Phone::F, "F"}, + {Phone::V, "V"}, {Phone::TH, "TH"}, {Phone::DH, "DH"}, + {Phone::S, "S"}, {Phone::Z, "Z"}, {Phone::SH, "SH"}, + {Phone::ZH, "ZH"}, {Phone::HH, "HH"}, {Phone::M, "M"}, + {Phone::N, "N"}, {Phone::NG, "NG"}, {Phone::L, "L"}, + {Phone::R, "R"}, {Phone::Y, "Y"}, {Phone::W, "W"}, - { Phone::Breath, "Breath" }, - { Phone::Cough, "Cough" }, - { Phone::Smack, "Smack" }, - { Phone::Noise, "Noise" } - }; + {Phone::Breath, "Breath"}, {Phone::Cough, "Cough"}, {Phone::Smack, "Smack"}, + {Phone::Noise, "Noise"}}; } optional PhoneConverter::tryParse(const string& s) { diff --git a/rhubarb/src/core/Phone.h b/rhubarb/src/core/Phone.h index 1a9b986..a59fb38 100644 --- a/rhubarb/src/core/Phone.h +++ b/rhubarb/src/core/Phone.h @@ -8,66 +8,66 @@ enum class Phone { // Vowels // ... monophthongs - AO, // [ɔ] as in [o]ff, f[a]ll, fr[o]st - AA, // [ɑ] as in f[a]ther - IY, // [i] as in b[ee], sh[e] - UW, // [u] as in y[ou], n[ew], f[oo]d - EH, // [ɛ] as in r[e]d, m[e]n - IH, // [ɪ] as in b[i]g, w[i]n - UH, // [ʊ] as in sh[ou]ld, c[ou]ld - AH, // [ʌ] as in b[u]t, s[u]n - Schwa, // [ə] as in [a]lone, disc[u]s - AE, // [æ] as in [a]t, b[a]t + AO, // [ɔ] as in [o]ff, f[a]ll, fr[o]st + AA, // [ɑ] as in f[a]ther + IY, // [i] as in b[ee], sh[e] + UW, // [u] as in y[ou], n[ew], f[oo]d + EH, // [ɛ] as in r[e]d, m[e]n + IH, // [ɪ] as in b[i]g, w[i]n + UH, // [ʊ] as in sh[ou]ld, c[ou]ld + AH, // [ʌ] as in b[u]t, s[u]n + Schwa, // [ə] as in [a]lone, disc[u]s + AE, // [æ] as in [a]t, b[a]t // ... diphthongs - EY, // [eɪ] as in s[ay], [ei]ght - AY, // [aɪ] as in m[y], wh[y], r[i]de - OW, // [oʊ] as in sh[ow], c[oa]t - AW, // [aʊ] as in h[ow], n[ow] - OY, // [ɔɪ] as in b[oy], t[oy] + EY, // [eɪ] as in s[ay], [ei]ght + AY, // [aɪ] as in m[y], wh[y], r[i]de + OW, // [oʊ] as in sh[ow], c[oa]t + AW, // [aʊ] as in h[ow], n[ow] + OY, // [ɔɪ] as in b[oy], t[oy] // ... r-colored - ER, // [ɝ] as in h[er], b[ir]d, h[ur]t + ER, // [ɝ] as in h[er], b[ir]d, h[ur]t LastVowel = ER, ///////////// // Consonants // ... stops - P, // [p] as in [p]ay - B, // [b] as in [b]uy - T, // [t] as in [t]ake - D, // [d] as in [d]ay - K, // [k] as in [k]ey - G, // [g] as in [g]o + P, // [p] as in [p]ay + B, // [b] as in [b]uy + T, // [t] as in [t]ake + D, // [d] as in [d]ay + K, // [k] as in [k]ey + G, // [g] as in [g]o // ... affricates - CH, // [tʃ] as in [ch]air - JH, // [dʒ] as in [j]ust + CH, // [tʃ] as in [ch]air + JH, // [dʒ] as in [j]ust // ... fricatives - F, // [f] as in [f]or - V, // [v] as in [v]ery - TH, // [θ] as in [th]anks - DH, // [ð] as in [th]at - S, // [s] as in [s]ay - Z, // [z] as in [z]oo - SH, // [ʃ] as in [sh]ow - ZH, // [ʒ] as in mea[s]ure, plea[s]ure - HH, // [h] as in [h]ouse + F, // [f] as in [f]or + V, // [v] as in [v]ery + TH, // [θ] as in [th]anks + DH, // [ð] as in [th]at + S, // [s] as in [s]ay + Z, // [z] as in [z]oo + SH, // [ʃ] as in [sh]ow + ZH, // [ʒ] as in mea[s]ure, plea[s]ure + HH, // [h] as in [h]ouse // ... nasals - M, // [m] as in [m]an - N, // [n] as in [no] - NG, // [ŋ] as in si[ng] + M, // [m] as in [m]an + N, // [n] as in [no] + NG, // [ŋ] as in si[ng] // ... liquids - L, // [ɫ] as in [l]ate - R, // [r, ɹ] as in [r]un + L, // [ɫ] as in [l]ate + R, // [r, ɹ] as in [r]un // ... semivowels - Y, // [j] as in [y]es - W, // [w] as in [w]ay + Y, // [j] as in [y]es + W, // [w] as in [w]ay ///////////// // Misc. @@ -81,9 +81,11 @@ enum class Phone { class PhoneConverter : public EnumConverter { public: static PhoneConverter& get(); + protected: std::string getTypeName() override; member_data getMemberData() override; + public: boost::optional tryParse(const std::string& s) override; }; @@ -92,4 +94,4 @@ std::ostream& operator<<(std::ostream& stream, Phone value); std::istream& operator>>(std::istream& stream, Phone& value); -bool isVowel(Phone phone); \ No newline at end of file +bool isVowel(Phone phone); diff --git a/rhubarb/src/core/Shape.cpp b/rhubarb/src/core/Shape.cpp index 8ae8c64..8b5a218 100644 --- a/rhubarb/src/core/Shape.cpp +++ b/rhubarb/src/core/Shape.cpp @@ -1,7 +1,7 @@ #include "Shape.h" -using std::string; using std::set; +using std::string; ShapeConverter& ShapeConverter::get() { static ShapeConverter converter; @@ -22,7 +22,9 @@ set ShapeConverter::getBasicShapes() { set ShapeConverter::getExtendedShapes() { static const set result = [] { set result; - for (int i = static_cast(Shape::LastBasicShape) + 1; i < static_cast(Shape::EndSentinel); ++i) { + for (int i = static_cast(Shape::LastBasicShape) + 1; + i < static_cast(Shape::EndSentinel); + ++i) { result.insert(static_cast(i)); } return result; @@ -35,16 +37,16 @@ string ShapeConverter::getTypeName() { } EnumConverter::member_data ShapeConverter::getMemberData() { - return member_data { - { Shape::A, "A" }, - { Shape::B, "B" }, - { Shape::C, "C" }, - { Shape::D, "D" }, - { Shape::E, "E" }, - { Shape::F, "F" }, - { Shape::G, "G" }, - { Shape::H, "H" }, - { Shape::X, "X" } + return member_data{ + {Shape::A, "A"}, + {Shape::B, "B"}, + {Shape::C, "C"}, + {Shape::D, "D"}, + {Shape::E, "E"}, + {Shape::F, "F"}, + {Shape::G, "G"}, + {Shape::H, "H"}, + {Shape::X, "X"} }; } diff --git a/rhubarb/src/core/Shape.h b/rhubarb/src/core/Shape.h index 58c500f..5f70eab 100644 --- a/rhubarb/src/core/Shape.h +++ b/rhubarb/src/core/Shape.h @@ -1,27 +1,28 @@ #pragma once -#include "tools/EnumConverter.h" #include +#include "tools/EnumConverter.h" + // The classic Hanna-Barbera mouth shapes A-F plus the common supplements G-H // For reference, see http://sunewatts.dk/lipsync/lipsync/article_02.php // For visual examples, see https://flic.kr/s/aHsj86KR4J. Their shapes "BMP".."L" map to A..H. enum class Shape { // Basic shapes - - A, // Closed mouth (M, B, P) - B, // Clenched teeth (most consonants, some vowels like EE as in b[ee]) - C, // Open mouth (vowels like m[e]n, s[u]n, s[a]y) - D, // Mouth wide open (vowels like f[a]ther, b[a]t, wh[y]) - E, // Rounded mouth (vowels like [o]ff) - F, // Puckered lips (y[ou], b[o]y, [w]ay) + + A, // Closed mouth (M, B, P) + B, // Clenched teeth (most consonants, some vowels like EE as in b[ee]) + C, // Open mouth (vowels like m[e]n, s[u]n, s[a]y) + D, // Mouth wide open (vowels like f[a]ther, b[a]t, wh[y]) + E, // Rounded mouth (vowels like [o]ff) + F, // Puckered lips (y[ou], b[o]y, [w]ay) LastBasicShape = F, // Extended shapes - G, // "F", "V" - H, // "L" - X, // Idle + G, // "F", "V" + H, // "L" + X, // Idle EndSentinel }; @@ -31,6 +32,7 @@ public: static ShapeConverter& get(); static std::set getBasicShapes(); static std::set getExtendedShapes(); + protected: std::string getTypeName() override; member_data getMemberData() override; diff --git a/rhubarb/src/exporters/DatExporter.cpp b/rhubarb/src/exporters/DatExporter.cpp index c4078cd..4c22abc 100644 --- a/rhubarb/src/exporters/DatExporter.cpp +++ b/rhubarb/src/exporters/DatExporter.cpp @@ -1,26 +1,29 @@ #include "DatExporter.h" -#include "animation/targetShapeSet.h" + #include +#include "animation/targetShapeSet.h" + +using std::string; using std::chrono::duration; using std::chrono::duration_cast; -using std::string; -DatExporter::DatExporter(const ShapeSet& targetShapeSet, double frameRate, bool convertToPrestonBlair) : +DatExporter::DatExporter( + const ShapeSet& targetShapeSet, double frameRate, bool convertToPrestonBlair +) : frameRate(frameRate), convertToPrestonBlair(convertToPrestonBlair), - prestonBlairShapeNames { - { Shape::A, "MBP" }, - { Shape::B, "etc" }, - { Shape::C, "E" }, - { Shape::D, "AI" }, - { Shape::E, "O" }, - { Shape::F, "U" }, - { Shape::G, "FV" }, - { Shape::H, "L" }, - { Shape::X, "rest" }, - } -{ + prestonBlairShapeNames{ + {Shape::A, "MBP"}, + {Shape::B, "etc"}, + {Shape::C, "E"}, + {Shape::D, "AI"}, + {Shape::E, "O"}, + {Shape::F, "U"}, + {Shape::G, "FV"}, + {Shape::H, "L"}, + {Shape::X, "rest"}, + } { // Animation works with a fixed frame rate of 100. // Downsampling to much less than 25 fps may result in dropped frames. // Upsampling to more than 100 fps doesn't make sense. @@ -28,13 +31,17 @@ DatExporter::DatExporter(const ShapeSet& targetShapeSet, double frameRate, bool const double maxFrameRate = 100.0; if (frameRate < minFrameRate || frameRate > maxFrameRate) { - throw std::runtime_error(fmt::format("Frame rate must be between {} and {} fps.", minFrameRate, maxFrameRate)); + throw std::runtime_error( + fmt::format("Frame rate must be between {} and {} fps.", minFrameRate, maxFrameRate) + ); } if (convertToPrestonBlair) { for (Shape shape : targetShapeSet) { if (prestonBlairShapeNames.find(shape) == prestonBlairShapeNames.end()) { - throw std::runtime_error(fmt::format("Mouth shape {} cannot be converted to Preston Blair shape names.")); + throw std::runtime_error( + fmt::format("Mouth shape {} cannot be converted to Preston Blair shape names.") + ); } } } @@ -62,9 +69,8 @@ void DatExporter::exportAnimation(const ExporterInput& input, std::ostream& outp } string DatExporter::toString(Shape shape) const { - return convertToPrestonBlair - ? prestonBlairShapeNames.at(shape) - : boost::lexical_cast(shape); + return convertToPrestonBlair ? prestonBlairShapeNames.at(shape) + : boost::lexical_cast(shape); } int DatExporter::toFrameNumber(centiseconds time) const { diff --git a/rhubarb/src/exporters/DatExporter.h b/rhubarb/src/exporters/DatExporter.h index 510352b..b7f1474 100644 --- a/rhubarb/src/exporters/DatExporter.h +++ b/rhubarb/src/exporters/DatExporter.h @@ -1,10 +1,11 @@ #pragma once -#include "Exporter.h" -#include "core/Shape.h" #include #include +#include "core/Shape.h" +#include "Exporter.h" + // Exporter for Moho's switch data file format class DatExporter : public Exporter { public: diff --git a/rhubarb/src/exporters/Exporter.h b/rhubarb/src/exporters/Exporter.h index a2353ac..4bc09a6 100644 --- a/rhubarb/src/exporters/Exporter.h +++ b/rhubarb/src/exporters/Exporter.h @@ -1,15 +1,17 @@ #pragma once +#include + #include "core/Shape.h" #include "time/ContinuousTimeline.h" -#include class ExporterInput { public: ExporterInput( const std::filesystem::path& inputFilePath, const JoiningContinuousTimeline& animation, - const ShapeSet& targetShapeSet) : + const ShapeSet& targetShapeSet + ) : inputFilePath(inputFilePath), animation(animation), targetShapeSet(targetShapeSet) {} @@ -22,5 +24,6 @@ public: class Exporter { public: virtual ~Exporter() {} + virtual void exportAnimation(const ExporterInput& input, std::ostream& outputStream) = 0; }; diff --git a/rhubarb/src/exporters/JsonExporter.cpp b/rhubarb/src/exporters/JsonExporter.cpp index 65a7c36..c44fb4b 100644 --- a/rhubarb/src/exporters/JsonExporter.cpp +++ b/rhubarb/src/exporters/JsonExporter.cpp @@ -1,4 +1,5 @@ #include "JsonExporter.h" + #include "exporterTools.h" #include "tools/stringTools.h" @@ -10,8 +11,10 @@ void JsonExporter::exportAnimation(const ExporterInput& input, std::ostream& out // the formatting. outputStream << "{\n"; outputStream << " \"metadata\": {\n"; - outputStream << " \"soundFile\": \"" << escapeJsonString(absolute(input.inputFilePath).u8string()) << "\",\n"; - outputStream << " \"duration\": " << formatDuration(input.animation.getRange().getDuration()) << "\n"; + outputStream << " \"soundFile\": \"" + << escapeJsonString(absolute(input.inputFilePath).u8string()) << "\",\n"; + outputStream << " \"duration\": " << formatDuration(input.animation.getRange().getDuration()) + << "\n"; outputStream << " },\n"; outputStream << " \"mouthCues\": [\n"; bool isFirst = true; @@ -19,8 +22,8 @@ void JsonExporter::exportAnimation(const ExporterInput& input, std::ostream& out if (!isFirst) outputStream << ",\n"; isFirst = false; outputStream << " { \"start\": " << formatDuration(timedShape.getStart()) - << ", \"end\": " << formatDuration(timedShape.getEnd()) - << ", \"value\": \"" << timedShape.getValue() << "\" }"; + << ", \"end\": " << formatDuration(timedShape.getEnd()) << ", \"value\": \"" + << timedShape.getValue() << "\" }"; } outputStream << "\n"; outputStream << " ]\n"; diff --git a/rhubarb/src/exporters/TsvExporter.cpp b/rhubarb/src/exporters/TsvExporter.cpp index b9c1a0c..94c366f 100644 --- a/rhubarb/src/exporters/TsvExporter.cpp +++ b/rhubarb/src/exporters/TsvExporter.cpp @@ -1,20 +1,15 @@ #include "TsvExporter.h" + #include "animation/targetShapeSet.h" void TsvExporter::exportAnimation(const ExporterInput& input, std::ostream& outputStream) { // Output shapes with start times for (auto& timedShape : input.animation) { - outputStream - << formatDuration(timedShape.getStart()) - << "\t" - << timedShape.getValue() - << "\n"; + outputStream << formatDuration(timedShape.getStart()) << "\t" << timedShape.getValue() + << "\n"; } // Output closed mouth with end time - outputStream - << formatDuration(input.animation.getRange().getEnd()) - << "\t" - << convertToTargetShapeSet(Shape::X, input.targetShapeSet) - << "\n"; + outputStream << formatDuration(input.animation.getRange().getEnd()) << "\t" + << convertToTargetShapeSet(Shape::X, input.targetShapeSet) << "\n"; } diff --git a/rhubarb/src/exporters/TsvExporter.h b/rhubarb/src/exporters/TsvExporter.h index 49de8e0..795bec5 100644 --- a/rhubarb/src/exporters/TsvExporter.h +++ b/rhubarb/src/exporters/TsvExporter.h @@ -6,4 +6,3 @@ class TsvExporter : public Exporter { public: void exportAnimation(const ExporterInput& input, std::ostream& outputStream) override; }; - diff --git a/rhubarb/src/exporters/XmlExporter.cpp b/rhubarb/src/exporters/XmlExporter.cpp index 2e0d9c0..2b04c92 100644 --- a/rhubarb/src/exporters/XmlExporter.cpp +++ b/rhubarb/src/exporters/XmlExporter.cpp @@ -1,11 +1,13 @@ #include "XmlExporter.h" + #include #include #include + #include "exporterTools.h" -using std::string; using boost::property_tree::ptree; +using std::string; void XmlExporter::exportAnimation(const ExporterInput& input, std::ostream& outputStream) { ptree tree; @@ -13,22 +15,19 @@ void XmlExporter::exportAnimation(const ExporterInput& input, std::ostream& outp // Add metadata tree.put("rhubarbResult.metadata.soundFile", absolute(input.inputFilePath).u8string()); tree.put( - "rhubarbResult.metadata.duration", - formatDuration(input.animation.getRange().getDuration()) + "rhubarbResult.metadata.duration", formatDuration(input.animation.getRange().getDuration()) ); // Add mouth cues for (auto& timedShape : dummyShapeIfEmpty(input.animation, input.targetShapeSet)) { - ptree& mouthCueElement = tree.add( - "rhubarbResult.mouthCues.mouthCue", - timedShape.getValue() - ); + ptree& mouthCueElement = + tree.add("rhubarbResult.mouthCues.mouthCue", timedShape.getValue()); mouthCueElement.put(".start", formatDuration(timedShape.getStart())); mouthCueElement.put(".end", formatDuration(timedShape.getEnd())); } -#ifndef BOOST_VERSION //present in version.hpp - #error "Could not detect Boost version." +#ifndef BOOST_VERSION // present in version.hpp +#error "Could not detect Boost version." #endif #if BOOST_VERSION < 105600 // Support legacy syntax diff --git a/rhubarb/src/exporters/exporterTools.cpp b/rhubarb/src/exporters/exporterTools.cpp index f459236..61667c2 100644 --- a/rhubarb/src/exporters/exporterTools.cpp +++ b/rhubarb/src/exporters/exporterTools.cpp @@ -1,10 +1,10 @@ #include "exporterTools.h" + #include "animation/targetShapeSet.h" // Makes sure there is at least one mouth shape std::vector> dummyShapeIfEmpty( - const JoiningTimeline& animation, - const ShapeSet& targetShapeSet + const JoiningTimeline& animation, const ShapeSet& targetShapeSet ) { std::vector> result; std::copy(animation.begin(), animation.end(), std::back_inserter(result)); diff --git a/rhubarb/src/exporters/exporterTools.h b/rhubarb/src/exporters/exporterTools.h index d3b2f8b..534666b 100644 --- a/rhubarb/src/exporters/exporterTools.h +++ b/rhubarb/src/exporters/exporterTools.h @@ -5,6 +5,5 @@ // Makes sure there is at least one mouth shape std::vector> dummyShapeIfEmpty( - const JoiningTimeline& animation, - const ShapeSet& targetShapeSet + const JoiningTimeline& animation, const ShapeSet& targetShapeSet ); diff --git a/rhubarb/src/lib/rhubarbLib.cpp b/rhubarb/src/lib/rhubarbLib.cpp index 127fb16..4ed0ab2 100644 --- a/rhubarb/src/lib/rhubarbLib.cpp +++ b/rhubarb/src/lib/rhubarbLib.cpp @@ -1,8 +1,9 @@ #include "rhubarbLib.h" -#include "core/Phone.h" -#include "tools/textFiles.h" + #include "animation/mouthAnimation.h" #include "audio/audioFileReading.h" +#include "core/Phone.h" +#include "tools/textFiles.h" using boost::optional; using std::string; @@ -14,8 +15,8 @@ JoiningContinuousTimeline animateAudioClip( const Recognizer& recognizer, const ShapeSet& targetShapeSet, int maxThreadCount, - ProgressSink& progressSink) -{ + ProgressSink& progressSink +) { const BoundedTimeline phones = recognizer.recognizePhones(audioClip, dialog, maxThreadCount, progressSink); JoiningContinuousTimeline result = animate(phones, targetShapeSet); @@ -28,8 +29,10 @@ JoiningContinuousTimeline animateWaveFile( const Recognizer& recognizer, const ShapeSet& targetShapeSet, int maxThreadCount, - ProgressSink& progressSink) -{ + ProgressSink& progressSink +) { const auto audioClip = createAudioFileClip(filePath); - return animateAudioClip(*audioClip, dialog, recognizer, targetShapeSet, maxThreadCount, progressSink); + return animateAudioClip( + *audioClip, dialog, recognizer, targetShapeSet, maxThreadCount, progressSink + ); } diff --git a/rhubarb/src/lib/rhubarbLib.h b/rhubarb/src/lib/rhubarbLib.h index 84493b9..b310f95 100644 --- a/rhubarb/src/lib/rhubarbLib.h +++ b/rhubarb/src/lib/rhubarbLib.h @@ -1,12 +1,13 @@ #pragma once -#include "core/Shape.h" -#include "time/ContinuousTimeline.h" -#include "audio/AudioClip.h" -#include "tools/progress.h" #include + #include "animation/targetShapeSet.h" +#include "audio/AudioClip.h" +#include "core/Shape.h" #include "recognition/Recognizer.h" +#include "time/ContinuousTimeline.h" +#include "tools/progress.h" JoiningContinuousTimeline animateAudioClip( const AudioClip& audioClip, @@ -14,7 +15,8 @@ JoiningContinuousTimeline animateAudioClip( const Recognizer& recognizer, const ShapeSet& targetShapeSet, int maxThreadCount, - ProgressSink& progressSink); + ProgressSink& progressSink +); JoiningContinuousTimeline animateWaveFile( std::filesystem::path filePath, @@ -22,4 +24,5 @@ JoiningContinuousTimeline animateWaveFile( const Recognizer& recognizer, const ShapeSet& targetShapeSet, int maxThreadCount, - ProgressSink& progressSink); + ProgressSink& progressSink +); diff --git a/rhubarb/src/logging/Entry.cpp b/rhubarb/src/logging/Entry.cpp index a0e4f40..626f0e8 100644 --- a/rhubarb/src/logging/Entry.cpp +++ b/rhubarb/src/logging/Entry.cpp @@ -1,39 +1,38 @@ #include "Entry.h" -#include #include +#include #include using std::lock_guard; -using std::unordered_map; using std::string; +using std::unordered_map; namespace logging { - // Returns an int representing the current thread. - // This used to be a simple thread_local variable, but Xcode doesn't support that yet - int getThreadCounter() { - using thread_id = std::thread::id; +// Returns an int representing the current thread. +// This used to be a simple thread_local variable, but Xcode doesn't support that yet +int getThreadCounter() { + using thread_id = std::thread::id; - static std::mutex counterMutex; - lock_guard lock(counterMutex); + static std::mutex counterMutex; + lock_guard lock(counterMutex); - static unordered_map threadCounters; - static int lastThreadId = 0; - thread_id threadId = std::this_thread::get_id(); - if (threadCounters.find(threadId) == threadCounters.end()) { - threadCounters.insert({ threadId, ++lastThreadId }); - } - return threadCounters.find(threadId)->second; + static unordered_map threadCounters; + static int lastThreadId = 0; + thread_id threadId = std::this_thread::get_id(); + if (threadCounters.find(threadId) == threadCounters.end()) { + threadCounters.insert({threadId, ++lastThreadId}); } - - Entry::Entry(Level level, const string& message) : - timestamp(), - level(level), - message(message) - { - time(×tamp); - this->threadCounter = getThreadCounter(); - } - + return threadCounters.find(threadId)->second; } + +Entry::Entry(Level level, const string& message) : + timestamp(), + level(level), + message(message) { + time(×tamp); + this->threadCounter = getThreadCounter(); +} + +} // namespace logging diff --git a/rhubarb/src/logging/Entry.h b/rhubarb/src/logging/Entry.h index 3067d4e..aef398c 100644 --- a/rhubarb/src/logging/Entry.h +++ b/rhubarb/src/logging/Entry.h @@ -3,15 +3,15 @@ #include "Level.h" namespace logging { - - struct Entry { - Entry(Level level, const std::string& message); - virtual ~Entry() = default; - time_t timestamp; - int threadCounter; - Level level; - std::string message; - }; +struct Entry { + Entry(Level level, const std::string& message); + virtual ~Entry() = default; -} + time_t timestamp; + int threadCounter; + Level level; + std::string message; +}; + +} // namespace logging diff --git a/rhubarb/src/logging/Formatter.h b/rhubarb/src/logging/Formatter.h index b49c083..29127b6 100644 --- a/rhubarb/src/logging/Formatter.h +++ b/rhubarb/src/logging/Formatter.h @@ -1,14 +1,15 @@ #pragma once #include + #include "Entry.h" namespace logging { - class Formatter { - public: - virtual ~Formatter() = default; - virtual std::string format(const Entry& entry) = 0; - }; +class Formatter { +public: + virtual ~Formatter() = default; + virtual std::string format(const Entry& entry) = 0; +}; -} +} // namespace logging diff --git a/rhubarb/src/logging/Level.cpp b/rhubarb/src/logging/Level.cpp index c756b39..baf817b 100644 --- a/rhubarb/src/logging/Level.cpp +++ b/rhubarb/src/logging/Level.cpp @@ -4,32 +4,32 @@ using std::string; namespace logging { - LevelConverter& LevelConverter::get() { - static LevelConverter converter; - return converter; - } - - string LevelConverter::getTypeName() { - return "Level"; - } - - EnumConverter::member_data LevelConverter::getMemberData() { - return member_data { - { Level::Trace, "Trace" }, - { Level::Debug, "Debug" }, - { Level::Info, "Info" }, - { Level::Warn, "Warn" }, - { Level::Error, "Error" }, - { Level::Fatal, "Fatal" } - }; - } - - std::ostream& operator<<(std::ostream& stream, Level value) { - return LevelConverter::get().write(stream, value); - } - - std::istream& operator >>(std::istream& stream, Level& value) { - return LevelConverter::get().read(stream, value); - } - +LevelConverter& LevelConverter::get() { + static LevelConverter converter; + return converter; } + +string LevelConverter::getTypeName() { + return "Level"; +} + +EnumConverter::member_data LevelConverter::getMemberData() { + return member_data{ + {Level::Trace, "Trace"}, + {Level::Debug, "Debug"}, + {Level::Info, "Info"}, + {Level::Warn, "Warn"}, + {Level::Error, "Error"}, + {Level::Fatal, "Fatal"} + }; +} + +std::ostream& operator<<(std::ostream& stream, Level value) { + return LevelConverter::get().write(stream, value); +} + +std::istream& operator>>(std::istream& stream, Level& value) { + return LevelConverter::get().read(stream, value); +} + +} // namespace logging diff --git a/rhubarb/src/logging/Level.h b/rhubarb/src/logging/Level.h index 20e3e4b..0854488 100644 --- a/rhubarb/src/logging/Level.h +++ b/rhubarb/src/logging/Level.h @@ -4,26 +4,19 @@ namespace logging { - enum class Level { - Trace, - Debug, - Info, - Warn, - Error, - Fatal, - EndSentinel - }; +enum class Level { Trace, Debug, Info, Warn, Error, Fatal, EndSentinel }; - class LevelConverter : public EnumConverter { - public: - static LevelConverter& get(); - protected: - std::string getTypeName() override; - member_data getMemberData() override; - }; +class LevelConverter : public EnumConverter { +public: + static LevelConverter& get(); - std::ostream& operator<<(std::ostream& stream, Level value); +protected: + std::string getTypeName() override; + member_data getMemberData() override; +}; - std::istream& operator >>(std::istream& stream, Level& value); +std::ostream& operator<<(std::ostream& stream, Level value); -} +std::istream& operator>>(std::istream& stream, Level& value); + +} // namespace logging diff --git a/rhubarb/src/logging/Sink.h b/rhubarb/src/logging/Sink.h index 2dd12ac..e1e517a 100644 --- a/rhubarb/src/logging/Sink.h +++ b/rhubarb/src/logging/Sink.h @@ -4,10 +4,10 @@ namespace logging { - class Sink { - public: - virtual ~Sink() = default; - virtual void receive(const Entry& entry) = 0; - }; +class Sink { +public: + virtual ~Sink() = default; + virtual void receive(const Entry& entry) = 0; +}; -} +} // namespace logging diff --git a/rhubarb/src/logging/formatters.cpp b/rhubarb/src/logging/formatters.cpp index 6436e03..4a9c073 100644 --- a/rhubarb/src/logging/formatters.cpp +++ b/rhubarb/src/logging/formatters.cpp @@ -1,5 +1,7 @@ #include "formatters.h" + #include + #include "Entry.h" #include "tools/tools.h" @@ -7,17 +9,17 @@ using std::string; namespace logging { - string SimpleConsoleFormatter::format(const Entry& entry) { - return fmt::format("[{0}] {1}", entry.level, entry.message); - } - - string SimpleFileFormatter::format(const Entry& entry) { - return fmt::format( - "[{0}] {1} {2}", - formatTime(entry.timestamp, "%F %H:%M:%S"), - entry.threadCounter, - consoleFormatter.format(entry) - ); - } - +string SimpleConsoleFormatter::format(const Entry& entry) { + return fmt::format("[{0}] {1}", entry.level, entry.message); } + +string SimpleFileFormatter::format(const Entry& entry) { + return fmt::format( + "[{0}] {1} {2}", + formatTime(entry.timestamp, "%F %H:%M:%S"), + entry.threadCounter, + consoleFormatter.format(entry) + ); +} + +} // namespace logging diff --git a/rhubarb/src/logging/formatters.h b/rhubarb/src/logging/formatters.h index 39dc493..dfda01f 100644 --- a/rhubarb/src/logging/formatters.h +++ b/rhubarb/src/logging/formatters.h @@ -4,16 +4,17 @@ namespace logging { - class SimpleConsoleFormatter : public Formatter { - public: - std::string format(const Entry& entry) override; - }; +class SimpleConsoleFormatter : public Formatter { +public: + std::string format(const Entry& entry) override; +}; - class SimpleFileFormatter : public Formatter { - public: - std::string format(const Entry& entry) override; - private: - SimpleConsoleFormatter consoleFormatter; - }; +class SimpleFileFormatter : public Formatter { +public: + std::string format(const Entry& entry) override; -} +private: + SimpleConsoleFormatter consoleFormatter; +}; + +} // namespace logging diff --git a/rhubarb/src/logging/logging.cpp b/rhubarb/src/logging/logging.cpp index ba24856..0e92366 100644 --- a/rhubarb/src/logging/logging.cpp +++ b/rhubarb/src/logging/logging.cpp @@ -1,13 +1,15 @@ #include "logging.h" -#include "tools/tools.h" + #include + #include "Entry.h" +#include "tools/tools.h" using namespace logging; +using std::lock_guard; +using std::shared_ptr; using std::string; using std::vector; -using std::shared_ptr; -using std::lock_guard; std::mutex& getLogMutex() { static std::mutex mutex; diff --git a/rhubarb/src/logging/logging.h b/rhubarb/src/logging/logging.h index ef05555..d921e52 100644 --- a/rhubarb/src/logging/logging.h +++ b/rhubarb/src/logging/logging.h @@ -1,37 +1,37 @@ #pragma once -#include "tools/EnumConverter.h" -#include "Sink.h" #include "Level.h" +#include "Sink.h" +#include "tools/EnumConverter.h" namespace logging { - bool addSink(std::shared_ptr sink); +bool addSink(std::shared_ptr sink); - bool removeSink(std::shared_ptr sink); +bool removeSink(std::shared_ptr sink); - void log(const Entry& entry); +void log(const Entry& entry); - void log(Level level, const std::string& message); +void log(Level level, const std::string& message); - template - void logFormat(Level level, fmt::CStringRef format, const Args&... args) { - log(level, fmt::format(format, args...)); - } - -#define LOG_WITH_LEVEL(levelName, levelEnum) \ - inline void levelName(const std::string& message) { \ - log(Level::levelEnum, message); \ - } \ - template \ - void levelName ## Format(fmt::CStringRef format, const Args&... args) { \ - logFormat(Level::levelEnum, format, args...); \ - } - - LOG_WITH_LEVEL(trace, Trace) - LOG_WITH_LEVEL(debug, Debug) - LOG_WITH_LEVEL(info, Info) - LOG_WITH_LEVEL(warn, Warn) - LOG_WITH_LEVEL(error, Error) - LOG_WITH_LEVEL(fatal, Fatal) +template +void logFormat(Level level, fmt::CStringRef format, const Args&... args) { + log(level, fmt::format(format, args...)); } + +#define LOG_WITH_LEVEL(levelName, levelEnum) \ + inline void levelName(const std::string& message) { \ + log(Level::levelEnum, message); \ + } \ + template \ + void levelName##Format(fmt::CStringRef format, const Args&... args) { \ + logFormat(Level::levelEnum, format, args...); \ + } + +LOG_WITH_LEVEL(trace, Trace) +LOG_WITH_LEVEL(debug, Debug) +LOG_WITH_LEVEL(info, Info) +LOG_WITH_LEVEL(warn, Warn) +LOG_WITH_LEVEL(error, Error) +LOG_WITH_LEVEL(fatal, Fatal) +} // namespace logging diff --git a/rhubarb/src/logging/sinks.cpp b/rhubarb/src/logging/sinks.cpp index 2f213b4..2dddf9f 100644 --- a/rhubarb/src/logging/sinks.cpp +++ b/rhubarb/src/logging/sinks.cpp @@ -1,35 +1,34 @@ #include "sinks.h" + #include + #include "Entry.h" -using std::string; using std::shared_ptr; +using std::string; namespace logging { - LevelFilter::LevelFilter(shared_ptr innerSink, Level minLevel) : - innerSink(innerSink), - minLevel(minLevel) - {} +LevelFilter::LevelFilter(shared_ptr innerSink, Level minLevel) : + innerSink(innerSink), + minLevel(minLevel) {} - void LevelFilter::receive(const Entry& entry) { - if (entry.level >= minLevel) { - innerSink->receive(entry); - } +void LevelFilter::receive(const Entry& entry) { + if (entry.level >= minLevel) { + innerSink->receive(entry); } - - StreamSink::StreamSink(shared_ptr stream, shared_ptr formatter) : - stream(stream), - formatter(formatter) - {} - - void StreamSink::receive(const Entry& entry) { - const string line = formatter->format(entry); - *stream << line << std::endl; - } - - StdErrSink::StdErrSink(shared_ptr formatter) : - StreamSink(std::shared_ptr(&std::cerr, [](void*) {}), formatter) - {} - } + +StreamSink::StreamSink(shared_ptr stream, shared_ptr formatter) : + stream(stream), + formatter(formatter) {} + +void StreamSink::receive(const Entry& entry) { + const string line = formatter->format(entry); + *stream << line << std::endl; +} + +StdErrSink::StdErrSink(shared_ptr formatter) : + StreamSink(std::shared_ptr(&std::cerr, [](void*) {}), formatter) {} + +} // namespace logging diff --git a/rhubarb/src/logging/sinks.h b/rhubarb/src/logging/sinks.h index 642ef1d..62c82ee 100644 --- a/rhubarb/src/logging/sinks.h +++ b/rhubarb/src/logging/sinks.h @@ -1,33 +1,36 @@ #pragma once -#include "Sink.h" #include + #include "Formatter.h" +#include "Sink.h" namespace logging { - enum class Level; +enum class Level; - class LevelFilter : public Sink { - public: - LevelFilter(std::shared_ptr innerSink, Level minLevel); - void receive(const Entry& entry) override; - private: - std::shared_ptr innerSink; - Level minLevel; - }; +class LevelFilter : public Sink { +public: + LevelFilter(std::shared_ptr innerSink, Level minLevel); + void receive(const Entry& entry) override; - class StreamSink : public Sink { - public: - StreamSink(std::shared_ptr stream, std::shared_ptr formatter); - void receive(const Entry& entry) override; - private: - std::shared_ptr stream; - std::shared_ptr formatter; - }; +private: + std::shared_ptr innerSink; + Level minLevel; +}; - class StdErrSink : public StreamSink { - public: - explicit StdErrSink(std::shared_ptr formatter); - }; +class StreamSink : public Sink { +public: + StreamSink(std::shared_ptr stream, std::shared_ptr formatter); + void receive(const Entry& entry) override; -} +private: + std::shared_ptr stream; + std::shared_ptr formatter; +}; + +class StdErrSink : public StreamSink { +public: + explicit StdErrSink(std::shared_ptr formatter); +}; + +} // namespace logging diff --git a/rhubarb/src/recognition/PhoneticRecognizer.cpp b/rhubarb/src/recognition/PhoneticRecognizer.cpp index 16696f2..81a9b6c 100644 --- a/rhubarb/src/recognition/PhoneticRecognizer.cpp +++ b/rhubarb/src/recognition/PhoneticRecognizer.cpp @@ -1,52 +1,66 @@ #include "PhoneticRecognizer.h" -#include "time/Timeline.h" -#include "audio/AudioSegment.h" -#include "audio/SampleRateConverter.h" -#include "audio/processing.h" -#include "time/timedLogging.h" -using std::runtime_error; -using std::unique_ptr; -using std::string; +#include "audio/AudioSegment.h" +#include "audio/processing.h" +#include "audio/SampleRateConverter.h" +#include "time/timedLogging.h" +#include "time/Timeline.h" + using boost::optional; +using std::runtime_error; +using std::string; +using std::unique_ptr; static lambda_unique_ptr createDecoder(optional dialog) { UNUSED(dialog); lambda_unique_ptr config( cmd_ln_init( - nullptr, ps_args(), true, + nullptr, + ps_args(), + true, // Set acoustic model - "-hmm", (getSphinxModelDirectory() / "acoustic-model").u8string().c_str(), + "-hmm", + (getSphinxModelDirectory() / "acoustic-model").u8string().c_str(), // Set phonetic language model - "-allphone", (getSphinxModelDirectory() / "en-us-phone.lm.bin").u8string().c_str(), - "-allphone_ci", "yes", + "-allphone", + (getSphinxModelDirectory() / "en-us-phone.lm.bin").u8string().c_str(), + "-allphone_ci", + "yes", // Set language model probability weight. // Low values (<= 0.4) can lead to fluttering animation. // High values (>= 1.0) can lead to imprecise or freezing animation. - "-lw", "0.8", + "-lw", + "0.8", // Add noise against zero silence // (see http://cmusphinx.sourceforge.net/wiki/faq#qwhy_my_accuracy_is_poor) - "-dither", "yes", + "-dither", + "yes", // Disable VAD -- we're doing that ourselves - "-remove_silence", "no", + "-remove_silence", + "no", // Perform per-utterance cepstral mean normalization - "-cmn", "batch", + "-cmn", + "batch", // The following settings are recommended at // http://cmusphinx.sourceforge.net/wiki/phonemerecognition // Set beam width applied to every frame in Viterbi search - "-beam", "1e-20", + "-beam", + "1e-20", // Set beam width applied to phone transitions - "-pbeam", "1e-20", - nullptr), - [](cmd_ln_t* config) { cmd_ln_free_r(config); }); + "-pbeam", + "1e-20", + nullptr + ), + [](cmd_ln_t* config) { cmd_ln_free_r(config); } + ); if (!config) throw runtime_error("Error creating configuration."); - lambda_unique_ptr decoder( - ps_init(config.get()), - [](ps_decoder_t* recognizer) { ps_free(recognizer); }); + lambda_unique_ptr decoder(ps_init(config.get()), [](ps_decoder_t* recognizer) { + ps_free(recognizer); + }); if (!decoder) throw runtime_error("Error creating speech decoder."); return decoder; @@ -64,9 +78,8 @@ static Timeline utteranceToPhones( paddedTimeRange.grow(padding); paddedTimeRange.trim(audioClip.getTruncatedRange()); - const unique_ptr clipSegment = audioClip.clone() - | segment(paddedTimeRange) - | resample(sphinxSampleRate); + const unique_ptr clipSegment = + audioClip.clone() | segment(paddedTimeRange) | resample(sphinxSampleRate); const auto audioBuffer = copyTo16bitBuffer(*clipSegment); // Detect phones (returned as words) @@ -109,5 +122,7 @@ BoundedTimeline PhoneticRecognizer::recognizePhones( int maxThreadCount, ProgressSink& progressSink ) const { - return ::recognizePhones(inputAudioClip, dialog, &createDecoder, &utteranceToPhones, maxThreadCount, progressSink); + return ::recognizePhones( + inputAudioClip, dialog, &createDecoder, &utteranceToPhones, maxThreadCount, progressSink + ); } diff --git a/rhubarb/src/recognition/PhoneticRecognizer.h b/rhubarb/src/recognition/PhoneticRecognizer.h index 62f743e..2b19013 100644 --- a/rhubarb/src/recognition/PhoneticRecognizer.h +++ b/rhubarb/src/recognition/PhoneticRecognizer.h @@ -1,7 +1,7 @@ #pragma once -#include "Recognizer.h" #include "pocketSphinxTools.h" +#include "Recognizer.h" class PhoneticRecognizer : public Recognizer { public: diff --git a/rhubarb/src/recognition/PocketSphinxRecognizer.cpp b/rhubarb/src/recognition/PocketSphinxRecognizer.cpp index e6260a2..8f78dd2 100644 --- a/rhubarb/src/recognition/PocketSphinxRecognizer.cpp +++ b/rhubarb/src/recognition/PocketSphinxRecognizer.cpp @@ -1,30 +1,33 @@ #include "PocketSphinxRecognizer.h" -#include + #include + +#include + #include "audio/AudioSegment.h" -#include "audio/SampleRateConverter.h" -#include "languageModels.h" -#include "tokenization.h" -#include "g2p.h" -#include "time/ContinuousTimeline.h" #include "audio/processing.h" +#include "audio/SampleRateConverter.h" +#include "g2p.h" +#include "languageModels.h" +#include "time/ContinuousTimeline.h" #include "time/timedLogging.h" +#include "tokenization.h" extern "C" { #include } -using std::runtime_error; -using std::invalid_argument; -using std::unique_ptr; -using std::string; -using std::vector; -using std::map; -using std::filesystem::path; -using std::regex; -using std::regex_replace; using boost::optional; using std::array; +using std::invalid_argument; +using std::map; +using std::regex; +using std::regex_replace; +using std::runtime_error; +using std::string; +using std::unique_ptr; +using std::vector; +using std::filesystem::path; bool dictionaryContains(dict_t& dictionary, const string& word) { return dict_wordid(&dictionary, word.c_str()) != BAD_S3WID; @@ -50,7 +53,9 @@ void addMissingDictionaryWords(const vector& words, ps_decoder_t& decode } for (auto it = missingPronunciations.begin(); it != missingPronunciations.end(); ++it) { const bool isLast = it == --missingPronunciations.end(); - logging::infoFormat("Unknown word '{}'. Guessing pronunciation '{}'.", it->first, it->second); + logging::infoFormat( + "Unknown word '{}'. Guessing pronunciation '{}'.", it->first, it->second + ); ps_add_word(&decoder, it->first.c_str(), it->second.c_str(), isLast); } } @@ -59,23 +64,24 @@ lambda_unique_ptr createDefaultLanguageModel(ps_decoder_t& decode path modelPath = getSphinxModelDirectory() / "en-us.lm.bin"; lambda_unique_ptr result( ngram_model_read(decoder.config, modelPath.u8string().c_str(), NGRAM_AUTO, decoder.lmath), - [](ngram_model_t* lm) { ngram_model_free(lm); }); + [](ngram_model_t* lm) { ngram_model_free(lm); } + ); if (!result) { - throw runtime_error(fmt::format("Error reading language model from {}.", modelPath.u8string())); + throw runtime_error( + fmt::format("Error reading language model from {}.", modelPath.u8string()) + ); } return result; } lambda_unique_ptr createDialogLanguageModel( - ps_decoder_t& decoder, - const string& dialog + ps_decoder_t& decoder, const string& dialog ) { // Split dialog into normalized words - vector words = tokenizeText( - dialog, - [&](const string& word) { return dictionaryContains(*decoder.dict, word); } - ); + vector words = tokenizeText(dialog, [&](const string& word) { + return dictionaryContains(*decoder.dict, word); + }); // Add dialog-specific words to the dictionary addMissingDictionaryWords(words, decoder); @@ -87,18 +93,16 @@ lambda_unique_ptr createDialogLanguageModel( } lambda_unique_ptr createBiasedLanguageModel( - ps_decoder_t& decoder, - const string& dialog + ps_decoder_t& decoder, const string& dialog ) { auto defaultLanguageModel = createDefaultLanguageModel(decoder); auto dialogLanguageModel = createDialogLanguageModel(decoder, dialog); constexpr int modelCount = 2; - array languageModels { - defaultLanguageModel.get(), - dialogLanguageModel.get() + array languageModels{ + defaultLanguageModel.get(), dialogLanguageModel.get() }; - array modelNames { "defaultLM", "dialogLM" }; - array modelWeights { 0.1f, 0.9f }; + array modelNames{"defaultLM", "dialogLM"}; + array modelWeights{0.1f, 0.9f}; lambda_unique_ptr result( ngram_model_set_init( nullptr, @@ -107,7 +111,8 @@ lambda_unique_ptr createBiasedLanguageModel( modelWeights.data(), modelCount ), - [](ngram_model_t* lm) { ngram_model_free(lm); }); + [](ngram_model_t* lm) { ngram_model_free(lm); } + ); if (!result) { throw runtime_error("Error creating biased language model."); } @@ -118,31 +123,40 @@ lambda_unique_ptr createBiasedLanguageModel( static lambda_unique_ptr createDecoder(optional dialog) { lambda_unique_ptr config( cmd_ln_init( - nullptr, ps_args(), true, + nullptr, + ps_args(), + true, // Set acoustic model - "-hmm", (getSphinxModelDirectory() / "acoustic-model").u8string().c_str(), + "-hmm", + (getSphinxModelDirectory() / "acoustic-model").u8string().c_str(), // Set pronunciation dictionary - "-dict", (getSphinxModelDirectory() / "cmudict-en-us.dict").u8string().c_str(), + "-dict", + (getSphinxModelDirectory() / "cmudict-en-us.dict").u8string().c_str(), // Add noise against zero silence // (see http://cmusphinx.sourceforge.net/wiki/faq#qwhy_my_accuracy_is_poor) - "-dither", "yes", + "-dither", + "yes", // Disable VAD -- we're doing that ourselves - "-remove_silence", "no", + "-remove_silence", + "no", // Perform per-utterance cepstral mean normalization - "-cmn", "batch", - nullptr), - [](cmd_ln_t* config) { cmd_ln_free_r(config); }); + "-cmn", + "batch", + nullptr + ), + [](cmd_ln_t* config) { cmd_ln_free_r(config); } + ); if (!config) throw runtime_error("Error creating configuration."); - lambda_unique_ptr decoder( - ps_init(config.get()), - [](ps_decoder_t* recognizer) { ps_free(recognizer); }); + lambda_unique_ptr decoder(ps_init(config.get()), [](ps_decoder_t* recognizer) { + ps_free(recognizer); + }); if (!decoder) throw runtime_error("Error creating speech decoder."); // Set language model - lambda_unique_ptr languageModel(dialog - ? createBiasedLanguageModel(*decoder, *dialog) - : createDefaultLanguageModel(*decoder)); + lambda_unique_ptr languageModel( + dialog ? createBiasedLanguageModel(*decoder, *dialog) : createDefaultLanguageModel(*decoder) + ); ps_set_lm(decoder.get(), "lm", languageModel.get()); ps_set_search(decoder.get(), "lm"); @@ -150,16 +164,15 @@ static lambda_unique_ptr createDecoder(optional dialo } optional> getPhoneAlignment( - const vector& wordIds, - const vector& audioBuffer, - ps_decoder_t& decoder) -{ + const vector& wordIds, const vector& audioBuffer, ps_decoder_t& decoder +) { if (wordIds.empty()) return boost::none; // Create alignment list lambda_unique_ptr alignment( ps_alignment_init(decoder.d2p), - [](ps_alignment_t* alignment) { ps_alignment_free(alignment); }); + [](ps_alignment_t* alignment) { ps_alignment_free(alignment); } + ); if (!alignment) throw runtime_error("Error creating alignment."); for (s3wid_t wordId : wordIds) { // Add word. Initial value for duration is ignored. @@ -172,7 +185,8 @@ optional> getPhoneAlignment( acmod_t* acousticModel = decoder.acmod; lambda_unique_ptr search( state_align_search_init("state_align", decoder.config, acousticModel, alignment.get()), - [](ps_search_t* search) { ps_search_free(search); }); + [](ps_search_t* search) { ps_search_free(search); } + ); if (!search) throw runtime_error("Error creating search."); // Start recognition @@ -190,7 +204,8 @@ optional> getPhoneAlignment( const int16* nextSample = audioBuffer.data(); size_t remainingSamples = audioBuffer.size(); const bool fullUtterance = true; - while (acmod_process_raw(acousticModel, &nextSample, &remainingSamples, fullUtterance) > 0) { + while (acmod_process_raw(acousticModel, &nextSample, &remainingSamples, fullUtterance) > 0 + ) { while (acousticModel->n_feat_frame > 0) { ps_search_step(search.get(), acousticModel->output_frame); acmod_advance(acousticModel); @@ -205,11 +220,8 @@ optional> getPhoneAlignment( // Extract phones with timestamps char** phoneNames = decoder.dict->mdef->ciname; Timeline result; - for ( - ps_alignment_iter_t* it = ps_alignment_phones(alignment.get()); - it; - it = ps_alignment_iter_next(it) - ) { + for (ps_alignment_iter_t* it = ps_alignment_phones(alignment.get()); it; + it = ps_alignment_iter_next(it)) { // Get phone ps_alignment_entry_t* phoneEntry = ps_alignment_iter_get(it); const s3cipid_t phoneId = phoneEntry->id.pid.cipid; @@ -231,16 +243,16 @@ optional> getPhoneAlignment( return result; } -// Some words have multiple pronunciations, one of which results in better animation than the others. -// This function returns the optimal pronunciation for a select set of these words. +// Some words have multiple pronunciations, one of which results in better animation than the +// others. This function returns the optimal pronunciation for a select set of these words. string fixPronunciation(const string& word) { - const static map replacements { - { "into(2)", "into" }, - { "to(2)", "to" }, - { "to(3)", "to" }, - { "today(2)", "today" }, - { "tomorrow(2)", "tomorrow" }, - { "tonight(2)", "tonight" } + const static map replacements{ + {"into(2)", "into"}, + {"to(2)", "to"}, + {"to(3)", "to"}, + {"today(2)", "today"}, + {"tomorrow(2)", "tomorrow"}, + {"tonight(2)", "tonight"} }; const auto pair = replacements.find(word); @@ -265,9 +277,8 @@ static Timeline utteranceToPhones( paddedTimeRange.grow(padding); paddedTimeRange.trim(audioClip.getTruncatedRange()); - const unique_ptr clipSegment = audioClip.clone() - | segment(paddedTimeRange) - | resample(sphinxSampleRate); + const unique_ptr clipSegment = + audioClip.clone() | segment(paddedTimeRange) | resample(sphinxSampleRate); const auto audioBuffer = copyTo16bitBuffer(*clipSegment); // Get words @@ -307,8 +318,9 @@ static Timeline utteranceToPhones( #if BOOST_VERSION < 105600 // Support legacy syntax #define value_or get_value_or #endif - Timeline utterancePhones = getPhoneAlignment(wordIds, audioBuffer, decoder) - .value_or(ContinuousTimeline(clipSegment->getTruncatedRange(), Phone::Noise)); + Timeline utterancePhones = + getPhoneAlignment(wordIds, audioBuffer, decoder) + .value_or(ContinuousTimeline(clipSegment->getTruncatedRange(), Phone::Noise)); alignmentProgressSink.reportProgress(1.0); utterancePhones.shift(paddedTimeRange.getStart()); @@ -338,5 +350,6 @@ BoundedTimeline PocketSphinxRecognizer::recognizePhones( ProgressSink& progressSink ) const { return ::recognizePhones( - inputAudioClip, dialog, &createDecoder, &utteranceToPhones, maxThreadCount, progressSink); + inputAudioClip, dialog, &createDecoder, &utteranceToPhones, maxThreadCount, progressSink + ); } diff --git a/rhubarb/src/recognition/PocketSphinxRecognizer.h b/rhubarb/src/recognition/PocketSphinxRecognizer.h index 20dc82f..f25b6da 100644 --- a/rhubarb/src/recognition/PocketSphinxRecognizer.h +++ b/rhubarb/src/recognition/PocketSphinxRecognizer.h @@ -1,7 +1,7 @@ #pragma once -#include "Recognizer.h" #include "pocketSphinxTools.h" +#include "Recognizer.h" class PocketSphinxRecognizer : public Recognizer { public: diff --git a/rhubarb/src/recognition/Recognizer.h b/rhubarb/src/recognition/Recognizer.h index c7ca710..6b1fce7 100644 --- a/rhubarb/src/recognition/Recognizer.h +++ b/rhubarb/src/recognition/Recognizer.h @@ -2,8 +2,8 @@ #include "audio/AudioClip.h" #include "core/Phone.h" -#include "tools/progress.h" #include "time/BoundedTimeline.h" +#include "tools/progress.h" class Recognizer { public: @@ -15,4 +15,4 @@ public: int maxThreadCount, ProgressSink& progressSink ) const = 0; -}; \ No newline at end of file +}; diff --git a/rhubarb/src/recognition/g2p.cpp b/rhubarb/src/recognition/g2p.cpp index 750340a..2702a7d 100644 --- a/rhubarb/src/recognition/g2p.cpp +++ b/rhubarb/src/recognition/g2p.cpp @@ -1,23 +1,25 @@ #include -#include -#include "tools/stringTools.h" -#include "logging/logging.h" -using std::vector; -using std::wstring; -using std::regex; -using std::wregex; +#include + +#include "logging/logging.h" +#include "tools/stringTools.h" + using std::invalid_argument; using std::pair; +using std::regex; +using std::vector; +using std::wregex; +using std::wstring; const vector>& getReplacementRules() { - static vector> rules { - #include "g2pRules.cpp" + static vector> rules{ +#include "g2pRules.cpp" // Turn bigrams into unigrams for easier conversion - { wregex(L"ôw"), L"Ω" }, - { wregex(L"öy"), L"ω" }, - { wregex(L"@r"), L"ɝ" } + {wregex(L"ôw"), L"Ω"}, + {wregex(L"öy"), L"ω"}, + {wregex(L"@r"), L"ɝ"} }; return rules; } @@ -64,8 +66,7 @@ Phone charToPhone(wchar_t c) { case L'r': return Phone::R; case L'l': return Phone::L; case L'h': return Phone::HH; - default: - return Phone::Noise; + default: return Phone::Noise; } } diff --git a/rhubarb/src/recognition/g2p.h b/rhubarb/src/recognition/g2p.h index d2f5d17..cb50d7d 100644 --- a/rhubarb/src/recognition/g2p.h +++ b/rhubarb/src/recognition/g2p.h @@ -1,6 +1,7 @@ #pragma once #include + #include "core/Phone.h" std::vector wordToPhones(const std::string& word); diff --git a/rhubarb/src/recognition/g2pRules.cpp b/rhubarb/src/recognition/g2pRules.cpp index 7c7a8a4..7a41ebc 100644 --- a/rhubarb/src/recognition/g2pRules.cpp +++ b/rhubarb/src/recognition/g2pRules.cpp @@ -4,212 +4,253 @@ // Rules // // get rid of some digraphs -{ wregex(L"ch"), L"ç" }, -{ wregex(L"sh"), L"$$" }, -{ wregex(L"ph"), L"f" }, -{ wregex(L"th"), L"+" }, -{ wregex(L"qu"), L"kw" }, -// and other spelling-level changes -{ wregex(L"w(r)"), L"$1" }, -{ wregex(L"w(ho)"), L"$1" }, -{ wregex(L"(w)h"), L"$1" }, -{ wregex(L"(^r)h"), L"$1" }, -{ wregex(L"(x)h"), L"$1" }, -{ wregex(L"([aeiouäëïöüâêîôûùò@])h($)"), L"$1$2" }, -{ wregex(L"(^e)x([aeiouäëïöüâêîôûùò@])"), L"$1gz$2" }, -{ wregex(L"x"), L"ks" }, -{ wregex(L"'"), L"" }, -// gh is particularly variable -{ wregex(L"gh([aeiouäëïöüâêîôûùò@])"), L"g$1" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])a(gh)"), L"$1ä$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])e(gh)"), L"$1ë$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])i(gh)"), L"$1ï$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])o(gh)"), L"$1ö$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])u(gh)"), L"$1ü$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])â(gh)"), L"$1ä$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])ê(gh)"), L"$1ë$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])î(gh)"), L"$1ï$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])ô(gh)"), L"$1ö$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])û(gh)"), L"$1ü$2" }, -{ wregex(L"ough(t)"), L"ò$1" }, -{ wregex(L"augh(t)"), L"ò$1" }, -{ wregex(L"ough"), L"ö" }, -{ wregex(L"gh"), L"" }, -// unpronounceable combinations -{ wregex(L"(^)g(n)"), L"$1$2" }, -{ wregex(L"(^)k(n)"), L"$1$2" }, -{ wregex(L"(^)m(n)"), L"$1$2" }, -{ wregex(L"(^)p(t)"), L"$1$2" }, -{ wregex(L"(^)p(s)"), L"$1$2" }, -{ wregex(L"(^)t(m)"), L"$1$2" }, -// medial y = i -{ wregex(L"(^[bcdfghjklmnpqrstvwxyzç+$ñ])y($)"), L"$1ï$2" }, -{ wregex(L"(^[bcdfghjklmnpqrstvwxyzç+$ñ]{2})y($)"), L"$1ï$2" }, -{ wregex(L"(^[bcdfghjklmnpqrstvwxyzç+$ñ]{3})y($)"), L"$1ï$2" }, -{ wregex(L"ey"), L"ë" }, -{ wregex(L"ay"), L"ä" }, -{ wregex(L"oy"), L"öy" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])y([bcdfghjklmnpqrstvwxyzç+$ñ])"), L"$1i$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])y($)"), L"$1i$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])y(e$)"), L"$1i$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ]{2})ie($)"), L"$1ï$2" }, -{ wregex(L"(^[bcdfghjklmnpqrstvwxyzç+$ñ])ie($)"), L"$1ï$2" }, -// sSl can simplify -{ wregex(L"(s)t(l[aeiouäëïöüâêîôûùò@]$)"), L"$1$2" }, -// affrication of t + front vowel -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@])ci([aeiouäëïöüâêîôûùò@])"), L"$1$$$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@])ti([aeiouäëïöüâêîôûùò@])"), L"$1$$$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@])tu([aeiouäëïöüâêîôûùò@])"), L"$1çu$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@])tu([rl][aeiouäëïöüâêîôûùò@])"), L"$1çu$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])si(o)"), L"$1$$$2" }, -{ wregex(L"([aeiouäëïöüâêîôûùò@])si(o)"), L"$1j$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])s(ur)"), L"$1$$$2" }, -{ wregex(L"([aeiouäëïöüâêîôûùò@])s(ur)"), L"$1j$2" }, -{ wregex(L"(k)s(u[aeiouäëïöüâêîôûùò@])"), L"$1$$$2" }, -{ wregex(L"(k)s(u[rl])"), L"$1$$$2" }, -// intervocalic s -{ wregex(L"([eiou])s([aeiouäëïöüâêîôûùò@])"), L"$1z$2" }, -// al to ol (do this before respelling) -{ wregex(L"a(ls)"), L"ò$1" }, -{ wregex(L"a(lr)"), L"ò$1" }, -{ wregex(L"a(l{2}$)"), L"ò$1" }, -{ wregex(L"a(lm(?:[aeiouäëïöüâêîôûùò@])?$)"), L"ò$1" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])a(l[td+])"), L"$1ò$2" }, -{ wregex(L"(^)a(l[td+])"), L"$1ò$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@])al(k)"), L"$1ò$2" }, -// soft c and g -{ wregex(L"c([eiêîy])"), L"s$1" }, -{ wregex(L"c"), L"k" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@])ge(a)"), L"$1j$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@])ge(o)"), L"$1j$2" }, -{ wregex(L"g([eiêîy])"), L"j$1" }, -// init/final guF was there just to harden the g -{ wregex(L"(^)gu([eiêîy])"), L"$1g$2" }, -{ wregex(L"gu(e$)"), L"g$1" }, -// untangle reverse-written final liquids -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])re($)"), L"$1@r$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])le($)"), L"$1@l$2" }, -// vowels are long medially -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])a([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), L"$1ä$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])e([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), L"$1ë$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])i([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), L"$1ï$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])o([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), L"$1ö$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])u([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), L"$1ü$2" }, -{ wregex(L"(^)a([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), L"$1ä$2" }, { wregex(L"(^)e([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), L"$1ë$2" }, { wregex(L"(^)i([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), L"$1ï$2" }, { wregex(L"(^)o([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), L"$1ö$2" }, { wregex(L"(^)u([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), L"$1ü$2" }, -// and short before 2 consonants or a final one -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])a([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1â$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])e([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1ê$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])i([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1î$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])o([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1ô$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])u([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1û$2" }, -{ wregex(L"(^)a([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1â$2" }, { wregex(L"(^)e([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1ê$2" }, { wregex(L"(^)i([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1î$2" }, { wregex(L"(^)o([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1ô$2" }, { wregex(L"(^)u([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1û$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])a([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1â$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])e([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1ê$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])i([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1î$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])o([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1ô$2" }, { wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])u([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1û$2" }, -{ wregex(L"(^)a([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1â$2" }, { wregex(L"(^)e([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1ê$2" }, { wregex(L"(^)i([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1î$2" }, { wregex(L"(^)o([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1ô$2" }, { wregex(L"(^)u([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1û$2" }, -// special but general rules -{ wregex(L"î(nd$)"), L"ï$1" }, -{ wregex(L"ô(s{2}$)"), L"ò$1" }, -{ wregex(L"ô(g$)"), L"ò$1" }, -{ wregex(L"ô(f[bcdfghjklmnpqrstvwxyzç+$ñ])"), L"ò$1" }, -{ wregex(L"ô(l[td+])"), L"ö$1" }, -{ wregex(L"(w)â(\\$)"), L"$1ò$2" }, -{ wregex(L"(w)â((?:t)?ç)"), L"$1ò$2" }, -{ wregex(L"(w)â([tdns+])"), L"$1ô$2" }, -// soft gn -{ wregex(L"îg([mnñ]$)"), L"ï$1" }, -{ wregex(L"îg([mnñ][bcdfghjklmnpqrstvwxyzç+$ñ])"), L"ï$1" }, -{ wregex(L"(ei)g(n)"), L"$1$2" }, -// handle ous before removing -e -{ wregex(L"ou(s$)"), L"@$1" }, -{ wregex(L"ou(s[bcdfghjklmnpqrstvwxyzç+$ñ])"), L"@$1" }, -// remove silent -e -{ wregex(L"([aeiouäëïöüâêîôûùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)e($)"), L"$1$2" }, -// common suffixes that hide a silent e -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@]{3})ë(mênt$)"), L"$1$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@]{3})ë(nês{2}$)"), L"$1$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@]{3})ë(li$)"), L"$1$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@]{3})ë(fûl$)"), L"$1$2" }, -// another common suffix -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@]{3})ï(nês{2}$)"), L"$1ë$2" }, -// shorten (1-char) weak penults after a long -// note: this error breaks almost as many words as it fixes... -{ wregex(L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ä([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)"), L"$1â$2" }, { wregex(L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ë([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)"), L"$1ê$2" }, { wregex(L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ï([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)"), L"$1î$2" }, { wregex(L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ö([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)"), L"$1ô$2" }, { wregex(L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ü([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)"), L"$1û$2" }, { wregex(L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ä([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)"), L"$1â$2" }, { wregex(L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ë([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)"), L"$1ê$2" }, { wregex(L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ï([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)"), L"$1î$2" }, { wregex(L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ö([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)"), L"$1ô$2" }, { wregex(L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ü([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)"), L"$1û$2" }, { wregex(L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ä([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)"), L"$1â$2" }, { wregex(L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ë([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)"), L"$1ê$2" }, { wregex(L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ï([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)"), L"$1î$2" }, { wregex(L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ö([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)"), L"$1ô$2" }, { wregex(L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ü([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)"), L"$1û$2" }, -// double vowels -{ wregex(L"eau"), L"ö" }, -{ wregex(L"ai"), L"ä" }, -{ wregex(L"au"), L"ò" }, -{ wregex(L"âw"), L"ò" }, -{ wregex(L"e{2}"), L"ë" }, -{ wregex(L"ea"), L"ë" }, -{ wregex(L"(s)ei"), L"$1ë" }, -{ wregex(L"ei"), L"ä" }, -{ wregex(L"eo"), L"ë@" }, -{ wregex(L"êw"), L"ü" }, -{ wregex(L"eu"), L"ü" }, -{ wregex(L"ie"), L"ë" }, -{ wregex(L"(i)[aeiouäëïöüâêîôûùò@]"), L"$1@" }, -{ wregex(L"(^[bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)i"), L"$1ï" }, -{ wregex(L"i(@)"), L"ë$1" }, -{ wregex(L"oa"), L"ö" }, -{ wregex(L"oe($)"), L"ö$1" }, -{ wregex(L"o{2}(k)"), L"ù$1" }, -{ wregex(L"o{2}"), L"u" }, -{ wregex(L"oul(d$)"), L"ù$1" }, -{ wregex(L"ou"), L"ôw" }, -{ wregex(L"oi"), L"öy" }, -{ wregex(L"ua"), L"ü@" }, -{ wregex(L"ue"), L"u" }, -{ wregex(L"ui"), L"u" }, -{ wregex(L"ôw($)"), L"ö$1" }, -// those pesky final syllabics -{ wregex(L"([aeiouäëïöüâêîôûùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[aeiouäëïöüâêîôûùò@])?)[aeiouäëïöüâêîôûùò@](l$)"), L"$1@$2" }, -{ wregex(L"([aeiouäëïöüâêîôûùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ê(n$)"), L"$1@$2" }, -{ wregex(L"([aeiouäëïöüâêîôûùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)î(n$)"), L"$1@$2" }, -{ wregex(L"([aeiouäëïöüâêîôûùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)â(n$)"), L"$1@$2" }, -{ wregex(L"([aeiouäëïöüâêîôûùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ô(n$)"), L"$1@$2" }, -// suffix simplifications -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@]{3})[aâä](b@l$)"), L"$1@$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@]l)ë(@n$)"), L"$1y$2" }, -{ wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@]n)ë(@n$)"), L"$1y$2" }, -// unpronounceable finals -{ wregex(L"(m)b($)"), L"$1$2" }, -{ wregex(L"(m)n($)"), L"$1$2" }, -// color the final vowels -{ wregex(L"a($)"), L"@$1" }, -{ wregex(L"e($)"), L"ë$1" }, -{ wregex(L"i($)"), L"ë$1" }, -{ wregex(L"o($)"), L"ö$1" }, -// vowels before r V=aeiouäëïöüâêîôûùò@ -{ wregex(L"ôw(r[bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@])"), L"ö$1" }, -{ wregex(L"ô(r)"), L"ö$1" }, -{ wregex(L"ò(r)"), L"ö$1" }, -{ wregex(L"(w)â(r[bcdfghjklmnpqrstvwxyzç+$ñ])"), L"$1ö$2" }, -{ wregex(L"(w)â(r$)"), L"$1ö$2" }, -{ wregex(L"ê(r{2})"), L"ä$1" }, -{ wregex(L"ë(r[iîï][bcdfghjklmnpqrstvwxyzç+$ñ])"), L"ä$1" }, -{ wregex(L"â(r{2})"), L"ä$1" }, -{ wregex(L"â(r[bcdfghjklmnpqrstvwxyzç+$ñ])"), L"ô$1" }, -{ wregex(L"â(r$)"), L"ô$1" }, -{ wregex(L"â(r)"), L"ä$1" }, -{ wregex(L"ê(r)"), L"@$1" }, -{ wregex(L"î(r)"), L"@$1" }, -{ wregex(L"û(r)"), L"@$1" }, -{ wregex(L"ù(r)"), L"@$1" }, -// handle ng -{ wregex(L"ng([fs$+])"), L"ñ$1" }, -{ wregex(L"ng([bdg])"), L"ñ$1" }, -{ wregex(L"ng([ptk])"), L"ñ$1" }, -{ wregex(L"ng($)"), L"ñ$1" }, -{ wregex(L"n(g)"), L"ñ$1" }, -{ wregex(L"n(k)"), L"ñ$1" }, -{ wregex(L"ô(ñ)"), L"ò$1" }, -{ wregex(L"â(ñ)"), L"ä$1" }, -// really a morphophonological rule, but it's cute -{ wregex(L"([bdg])s($)"), L"$1z$2" }, -{ wregex(L"s(m$)"), L"z$1" }, -// double consonants -{ wregex(L"s(s)"), L"$1" }, -{ wregex(L"s(\\$)"), L"$1" }, -{ wregex(L"t(t)"), L"$1" }, -{ wregex(L"t(ç)"), L"$1" }, -{ wregex(L"p(p)"), L"$1" }, -{ wregex(L"k(k)"), L"$1" }, -{ wregex(L"b(b)"), L"$1" }, -{ wregex(L"d(d)"), L"$1" }, -{ wregex(L"d(j)"), L"$1" }, -{ wregex(L"g(g)"), L"$1" }, -{ wregex(L"n(n)"), L"$1" }, -{ wregex(L"m(m)"), L"$1" }, -{ wregex(L"r(r)"), L"$1" }, -{ wregex(L"l(l)"), L"$1" }, -{ wregex(L"f(f)"), L"$1" }, -{ wregex(L"z(z)"), L"$1" }, -// There are a number of cases not covered by these rules. -// Let's add some reasonable fallback rules. -{ wregex(L"a"), L"â" }, -{ wregex(L"e"), L"@" }, -{ wregex(L"i"), L"ë" }, -{ wregex(L"o"), L"ö" }, -{ wregex(L"q"), L"k" }, +{wregex(L"ch"), L"ç"}, {wregex(L"sh"), L"$$"}, {wregex(L"ph"), L"f"}, {wregex(L"th"), L"+"}, + {wregex(L"qu"), L"kw"}, + // and other spelling-level changes + {wregex(L"w(r)"), L"$1"}, {wregex(L"w(ho)"), L"$1"}, {wregex(L"(w)h"), L"$1"}, + {wregex(L"(^r)h"), L"$1"}, {wregex(L"(x)h"), L"$1"}, + {wregex(L"([aeiouäëïöüâêîôûùò@])h($)"), L"$1$2"}, + {wregex(L"(^e)x([aeiouäëïöüâêîôûùò@])"), L"$1gz$2"}, {wregex(L"x"), L"ks"}, {wregex(L"'"), L""}, + // gh is particularly variable + {wregex(L"gh([aeiouäëïöüâêîôûùò@])"), L"g$1"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])a(gh)"), L"$1ä$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])e(gh)"), L"$1ë$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])i(gh)"), L"$1ï$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])o(gh)"), L"$1ö$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])u(gh)"), L"$1ü$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])â(gh)"), L"$1ä$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])ê(gh)"), L"$1ë$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])î(gh)"), L"$1ï$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])ô(gh)"), L"$1ö$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])û(gh)"), L"$1ü$2"}, {wregex(L"ough(t)"), L"ò$1"}, + {wregex(L"augh(t)"), L"ò$1"}, {wregex(L"ough"), L"ö"}, {wregex(L"gh"), L""}, + // unpronounceable combinations + {wregex(L"(^)g(n)"), L"$1$2"}, {wregex(L"(^)k(n)"), L"$1$2"}, {wregex(L"(^)m(n)"), L"$1$2"}, + {wregex(L"(^)p(t)"), L"$1$2"}, {wregex(L"(^)p(s)"), L"$1$2"}, {wregex(L"(^)t(m)"), L"$1$2"}, + // medial y = i + {wregex(L"(^[bcdfghjklmnpqrstvwxyzç+$ñ])y($)"), L"$1ï$2"}, + {wregex(L"(^[bcdfghjklmnpqrstvwxyzç+$ñ]{2})y($)"), L"$1ï$2"}, + {wregex(L"(^[bcdfghjklmnpqrstvwxyzç+$ñ]{3})y($)"), L"$1ï$2"}, {wregex(L"ey"), L"ë"}, + {wregex(L"ay"), L"ä"}, {wregex(L"oy"), L"öy"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])y([bcdfghjklmnpqrstvwxyzç+$ñ])"), L"$1i$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])y($)"), L"$1i$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])y(e$)"), L"$1i$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ]{2})ie($)"), L"$1ï$2"}, + {wregex(L"(^[bcdfghjklmnpqrstvwxyzç+$ñ])ie($)"), L"$1ï$2"}, + // sSl can simplify + {wregex(L"(s)t(l[aeiouäëïöüâêîôûùò@]$)"), L"$1$2"}, + // affrication of t + front vowel + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@])ci([aeiouäëïöüâêîôûùò@])"), L"$1$$$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@])ti([aeiouäëïöüâêîôûùò@])"), L"$1$$$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@])tu([aeiouäëïöüâêîôûùò@])"), L"$1çu$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@])tu([rl][aeiouäëïöüâêîôûùò@])"), + L"$1çu$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])si(o)"), L"$1$$$2"}, + {wregex(L"([aeiouäëïöüâêîôûùò@])si(o)"), L"$1j$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])s(ur)"), L"$1$$$2"}, + {wregex(L"([aeiouäëïöüâêîôûùò@])s(ur)"), L"$1j$2"}, + {wregex(L"(k)s(u[aeiouäëïöüâêîôûùò@])"), L"$1$$$2"}, {wregex(L"(k)s(u[rl])"), L"$1$$$2"}, + // intervocalic s + {wregex(L"([eiou])s([aeiouäëïöüâêîôûùò@])"), L"$1z$2"}, + // al to ol (do this before respelling) + {wregex(L"a(ls)"), L"ò$1"}, {wregex(L"a(lr)"), L"ò$1"}, {wregex(L"a(l{2}$)"), L"ò$1"}, + {wregex(L"a(lm(?:[aeiouäëïöüâêîôûùò@])?$)"), L"ò$1"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])a(l[td+])"), L"$1ò$2"}, + {wregex(L"(^)a(l[td+])"), L"$1ò$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@])al(k)"), L"$1ò$2"}, + // soft c and g + {wregex(L"c([eiêîy])"), L"s$1"}, {wregex(L"c"), L"k"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@])ge(a)"), L"$1j$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@])ge(o)"), L"$1j$2"}, + {wregex(L"g([eiêîy])"), L"j$1"}, + // init/final guF was there just to harden the g + {wregex(L"(^)gu([eiêîy])"), L"$1g$2"}, {wregex(L"gu(e$)"), L"g$1"}, + // untangle reverse-written final liquids + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])re($)"), L"$1@r$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])le($)"), L"$1@l$2"}, + // vowels are long medially + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])a([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), + L"$1ä$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])e([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), + L"$1ë$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])i([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), + L"$1ï$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])o([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), + L"$1ö$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])u([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), + L"$1ü$2"}, + {wregex(L"(^)a([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), L"$1ä$2"}, + {wregex(L"(^)e([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), L"$1ë$2"}, + {wregex(L"(^)i([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), L"$1ï$2"}, + {wregex(L"(^)o([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), L"$1ö$2"}, + {wregex(L"(^)u([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@])"), L"$1ü$2"}, + // and short before 2 consonants or a final one + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])a([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1â$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])e([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1ê$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])i([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1î$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])o([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1ô$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])u([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1û$2"}, + {wregex(L"(^)a([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1â$2"}, + {wregex(L"(^)e([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1ê$2"}, + {wregex(L"(^)i([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1î$2"}, + {wregex(L"(^)o([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1ô$2"}, + {wregex(L"(^)u([bcdfghjklmnpqrstvwxyzç+$ñ]{2})"), L"$1û$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])a([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1â$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])e([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1ê$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])i([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1î$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])o([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1ô$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñ])u([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1û$2"}, + {wregex(L"(^)a([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1â$2"}, + {wregex(L"(^)e([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1ê$2"}, + {wregex(L"(^)i([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1î$2"}, + {wregex(L"(^)o([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1ô$2"}, + {wregex(L"(^)u([bcdfghjklmnpqrstvwxyzç+$ñ]$)"), L"$1û$2"}, + // special but general rules + {wregex(L"î(nd$)"), L"ï$1"}, {wregex(L"ô(s{2}$)"), L"ò$1"}, {wregex(L"ô(g$)"), L"ò$1"}, + {wregex(L"ô(f[bcdfghjklmnpqrstvwxyzç+$ñ])"), L"ò$1"}, {wregex(L"ô(l[td+])"), L"ö$1"}, + {wregex(L"(w)â(\\$)"), L"$1ò$2"}, {wregex(L"(w)â((?:t)?ç)"), L"$1ò$2"}, + {wregex(L"(w)â([tdns+])"), L"$1ô$2"}, + // soft gn + {wregex(L"îg([mnñ]$)"), L"ï$1"}, {wregex(L"îg([mnñ][bcdfghjklmnpqrstvwxyzç+$ñ])"), L"ï$1"}, + {wregex(L"(ei)g(n)"), L"$1$2"}, + // handle ous before removing -e + {wregex(L"ou(s$)"), L"@$1"}, {wregex(L"ou(s[bcdfghjklmnpqrstvwxyzç+$ñ])"), L"@$1"}, + // remove silent -e + {wregex( + L"([aeiouäëïöüâêîôûùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)e($)" + ), + L"$1$2"}, + // common suffixes that hide a silent e + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@]{3})ë(mênt$)"), L"$1$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@]{3})ë(nês{2}$)"), L"$1$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@]{3})ë(li$)"), L"$1$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@]{3})ë(fûl$)"), L"$1$2"}, + // another common suffix + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@]{3})ï(nês{2}$)"), L"$1ë$2"}, + // shorten (1-char) weak penults after a long + // note: this error breaks almost as many words as it fixes... + {wregex( + L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ä([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)" + ), + L"$1â$2"}, + {wregex( + L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ë([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)" + ), + L"$1ê$2"}, + {wregex( + L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ï([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)" + ), + L"$1î$2"}, + {wregex( + L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ö([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)" + ), + L"$1ô$2"}, + {wregex( + L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ü([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)" + ), + L"$1û$2"}, + {wregex( + L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ä([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)" + ), + L"$1â$2"}, + {wregex( + L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ë([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)" + ), + L"$1ê$2"}, + {wregex( + L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ï([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)" + ), + L"$1î$2"}, + {wregex( + L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ö([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)" + ), + L"$1ô$2"}, + {wregex( + L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ü([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)" + ), + L"$1û$2"}, + {wregex( + L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ä([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)" + ), + L"$1â$2"}, + {wregex( + L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ë([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)" + ), + L"$1ê$2"}, + {wregex( + L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ï([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)" + ), + L"$1î$2"}, + {wregex( + L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ö([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)" + ), + L"$1ô$2"}, + {wregex( + L"([äëïöüäëïöüäëïöüùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?(?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ü([bcdfghjklmnpqrstvwxyzç+$ñ][aeiouäëïöüâêîôûùò@]$)" + ), + L"$1û$2"}, + // double vowels + {wregex(L"eau"), L"ö"}, {wregex(L"ai"), L"ä"}, {wregex(L"au"), L"ò"}, {wregex(L"âw"), L"ò"}, + {wregex(L"e{2}"), L"ë"}, {wregex(L"ea"), L"ë"}, {wregex(L"(s)ei"), L"$1ë"}, + {wregex(L"ei"), L"ä"}, {wregex(L"eo"), L"ë@"}, {wregex(L"êw"), L"ü"}, {wregex(L"eu"), L"ü"}, + {wregex(L"ie"), L"ë"}, {wregex(L"(i)[aeiouäëïöüâêîôûùò@]"), L"$1@"}, + {wregex(L"(^[bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)i"), L"$1ï"}, + {wregex(L"i(@)"), L"ë$1"}, {wregex(L"oa"), L"ö"}, {wregex(L"oe($)"), L"ö$1"}, + {wregex(L"o{2}(k)"), L"ù$1"}, {wregex(L"o{2}"), L"u"}, {wregex(L"oul(d$)"), L"ù$1"}, + {wregex(L"ou"), L"ôw"}, {wregex(L"oi"), L"öy"}, {wregex(L"ua"), L"ü@"}, {wregex(L"ue"), L"u"}, + {wregex(L"ui"), L"u"}, {wregex(L"ôw($)"), L"ö$1"}, + // those pesky final syllabics + {wregex( + L"([aeiouäëïöüâêîôûùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[aeiouäëïöüâêîôûùò@])?)[aeiouäëïöüâêîôûùò@](l$)" + ), + L"$1@$2"}, + {wregex( + L"([aeiouäëïöüâêîôûùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ê(n$)" + ), + L"$1@$2"}, + {wregex( + L"([aeiouäëïöüâêîôûùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)î(n$)" + ), + L"$1@$2"}, + {wregex( + L"([aeiouäëïöüâêîôûùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)â(n$)" + ), + L"$1@$2"}, + {wregex( + L"([aeiouäëïöüâêîôûùò@][bcdfghjklmnpqrstvwxyzç+$ñ](?:[bcdfghjklmnpqrstvwxyzç+$ñ])?)ô(n$)" + ), + L"$1@$2"}, + // suffix simplifications + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@]{3})[aâä](b@l$)"), L"$1@$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@]l)ë(@n$)"), L"$1y$2"}, + {wregex(L"([bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@]n)ë(@n$)"), L"$1y$2"}, + // unpronounceable finals + {wregex(L"(m)b($)"), L"$1$2"}, {wregex(L"(m)n($)"), L"$1$2"}, + // color the final vowels + {wregex(L"a($)"), L"@$1"}, {wregex(L"e($)"), L"ë$1"}, {wregex(L"i($)"), L"ë$1"}, + {wregex(L"o($)"), L"ö$1"}, + // vowels before r V=aeiouäëïöüâêîôûùò@ + {wregex(L"ôw(r[bcdfghjklmnpqrstvwxyzç+$ñaeiouäëïöüâêîôûùò@])"), L"ö$1"}, + {wregex(L"ô(r)"), L"ö$1"}, {wregex(L"ò(r)"), L"ö$1"}, + {wregex(L"(w)â(r[bcdfghjklmnpqrstvwxyzç+$ñ])"), L"$1ö$2"}, {wregex(L"(w)â(r$)"), L"$1ö$2"}, + {wregex(L"ê(r{2})"), L"ä$1"}, {wregex(L"ë(r[iîï][bcdfghjklmnpqrstvwxyzç+$ñ])"), L"ä$1"}, + {wregex(L"â(r{2})"), L"ä$1"}, {wregex(L"â(r[bcdfghjklmnpqrstvwxyzç+$ñ])"), L"ô$1"}, + {wregex(L"â(r$)"), L"ô$1"}, {wregex(L"â(r)"), L"ä$1"}, {wregex(L"ê(r)"), L"@$1"}, + {wregex(L"î(r)"), L"@$1"}, {wregex(L"û(r)"), L"@$1"}, {wregex(L"ù(r)"), L"@$1"}, + // handle ng + {wregex(L"ng([fs$+])"), L"ñ$1"}, {wregex(L"ng([bdg])"), L"ñ$1"}, {wregex(L"ng([ptk])"), L"ñ$1"}, + {wregex(L"ng($)"), L"ñ$1"}, {wregex(L"n(g)"), L"ñ$1"}, {wregex(L"n(k)"), L"ñ$1"}, + {wregex(L"ô(ñ)"), L"ò$1"}, {wregex(L"â(ñ)"), L"ä$1"}, + // really a morphophonological rule, but it's cute + {wregex(L"([bdg])s($)"), L"$1z$2"}, {wregex(L"s(m$)"), L"z$1"}, + // double consonants + {wregex(L"s(s)"), L"$1"}, {wregex(L"s(\\$)"), L"$1"}, {wregex(L"t(t)"), L"$1"}, + {wregex(L"t(ç)"), L"$1"}, {wregex(L"p(p)"), L"$1"}, {wregex(L"k(k)"), L"$1"}, + {wregex(L"b(b)"), L"$1"}, {wregex(L"d(d)"), L"$1"}, {wregex(L"d(j)"), L"$1"}, + {wregex(L"g(g)"), L"$1"}, {wregex(L"n(n)"), L"$1"}, {wregex(L"m(m)"), L"$1"}, + {wregex(L"r(r)"), L"$1"}, {wregex(L"l(l)"), L"$1"}, {wregex(L"f(f)"), L"$1"}, + {wregex(L"z(z)"), L"$1"}, + // There are a number of cases not covered by these rules. + // Let's add some reasonable fallback rules. + {wregex(L"a"), L"â"}, {wregex(L"e"), L"@"}, {wregex(L"i"), L"ë"}, {wregex(L"o"), L"ö"}, + {wregex(L"q"), L"k"}, diff --git a/rhubarb/src/recognition/languageModels.cpp b/rhubarb/src/recognition/languageModels.cpp index 7691e77..427371d 100644 --- a/rhubarb/src/recognition/languageModels.cpp +++ b/rhubarb/src/recognition/languageModels.cpp @@ -1,22 +1,25 @@ #include "languageModels.h" -#include -#include -#include -#include -#include -#include "tools/platformTools.h" -#include -#include "core/appInfo.h" -#include + #include -using std::string; -using std::vector; -using std::regex; -using std::map; -using std::tuple; -using std::get; +#include +#include +#include +#include +#include +#include +#include + +#include "core/appInfo.h" +#include "tools/platformTools.h" + using std::endl; +using std::get; +using std::map; +using std::regex; +using std::string; +using std::tuple; +using std::vector; using std::filesystem::path; using Unigram = string; @@ -50,9 +53,7 @@ map getTrigramCounts(const vector& words) { } map getUnigramProbabilities( - const vector& words, - const map& unigramCounts, - const double deflator + const vector& words, const map& unigramCounts, const double deflator ) { map unigramProbabilities; for (const auto& pair : unigramCounts) { @@ -97,8 +98,8 @@ map getUnigramBackoffWeights( const map& unigramCounts, const map& unigramProbabilities, const map& bigramCounts, - const double discountMass) -{ + const double discountMass +) { map unigramBackoffWeights; for (const Unigram& unigram : unigramCounts | boost::adaptors::map_keys) { double denominator = 1; @@ -116,8 +117,8 @@ map getBigramBackoffWeights( const map& bigramCounts, const map& bigramProbabilities, const map& trigramCounts, - const double discountMass) -{ + const double discountMass +) { map bigramBackoffWeights; for (const Bigram& bigram : bigramCounts | boost::adaptors::map_keys) { double denominator = 1; @@ -163,24 +164,22 @@ void createLanguageModelFile(const vector& words, const path& filePath) file.precision(4); file << "\\1-grams:" << endl; for (const Unigram& unigram : unigramCounts | boost::adaptors::map_keys) { - file << log10(unigramProbabilities.at(unigram)) - << " " << unigram - << " " << log10(unigramBackoffWeights.at(unigram)) << endl; + file << log10(unigramProbabilities.at(unigram)) << " " << unigram << " " + << log10(unigramBackoffWeights.at(unigram)) << endl; } file << endl; file << "\\2-grams:" << endl; for (const Bigram& bigram : bigramCounts | boost::adaptors::map_keys) { - file << log10(bigramProbabilities.at(bigram)) - << " " << get<0>(bigram) << " " << get<1>(bigram) - << " " << log10(bigramBackoffWeights.at(bigram)) << endl; + file << log10(bigramProbabilities.at(bigram)) << " " << get<0>(bigram) << " " + << get<1>(bigram) << " " << log10(bigramBackoffWeights.at(bigram)) << endl; } file << endl; file << "\\3-grams:" << endl; for (const Trigram& trigram : trigramCounts | boost::adaptors::map_keys) { - file << log10(trigramProbabilities.at(trigram)) - << " " << get<0>(trigram) << " " << get<1>(trigram) << " " << get<2>(trigram) << endl; + file << log10(trigramProbabilities.at(trigram)) << " " << get<0>(trigram) << " " + << get<1>(trigram) << " " << get<2>(trigram) << endl; } file << endl; @@ -188,14 +187,16 @@ void createLanguageModelFile(const vector& words, const path& filePath) } lambda_unique_ptr createLanguageModel( - const vector& words, - ps_decoder_t& decoder + const vector& words, ps_decoder_t& decoder ) { path tempFilePath = getTempFilePath(); createLanguageModelFile(words, tempFilePath); auto deleteTempFile = gsl::finally([&]() { std::filesystem::remove(tempFilePath); }); return lambda_unique_ptr( - ngram_model_read(decoder.config, tempFilePath.u8string().c_str(), NGRAM_ARPA, decoder.lmath), - [](ngram_model_t* lm) { ngram_model_free(lm); }); + ngram_model_read( + decoder.config, tempFilePath.u8string().c_str(), NGRAM_ARPA, decoder.lmath + ), + [](ngram_model_t* lm) { ngram_model_free(lm); } + ); } diff --git a/rhubarb/src/recognition/languageModels.h b/rhubarb/src/recognition/languageModels.h index 41cf035..66148fb 100644 --- a/rhubarb/src/recognition/languageModels.h +++ b/rhubarb/src/recognition/languageModels.h @@ -1,14 +1,14 @@ #pragma once #include + #include "tools/tools.h" extern "C" { -#include #include +#include } lambda_unique_ptr createLanguageModel( - const std::vector& words, - ps_decoder_t& decoder + const std::vector& words, ps_decoder_t& decoder ); diff --git a/rhubarb/src/recognition/pocketSphinxTools.cpp b/rhubarb/src/recognition/pocketSphinxTools.cpp index 0ac49d1..9d9469a 100644 --- a/rhubarb/src/recognition/pocketSphinxTools.cpp +++ b/rhubarb/src/recognition/pocketSphinxTools.cpp @@ -1,43 +1,39 @@ #include "pocketSphinxTools.h" -#include "tools/platformTools.h" #include + #include "audio/DcOffset.h" #include "audio/voiceActivityDetection.h" -#include "tools/parallel.h" -#include "tools/ObjectPool.h" #include "time/timedLogging.h" +#include "tools/ObjectPool.h" +#include "tools/parallel.h" +#include "tools/platformTools.h" extern "C" { -#include -#include #include +#include +#include } -using std::runtime_error; -using std::invalid_argument; -using std::unique_ptr; -using std::string; -using std::vector; -using std::filesystem::path; -using std::regex; using boost::optional; +using std::invalid_argument; +using std::regex; +using std::runtime_error; +using std::string; +using std::unique_ptr; +using std::vector; using std::chrono::duration_cast; - +using std::filesystem::path; + logging::Level convertSphinxErrorLevel(err_lvl_t errorLevel) { switch (errorLevel) { case ERR_DEBUG: case ERR_INFO: - case ERR_INFOCONT: - return logging::Level::Trace; - case ERR_WARN: - return logging::Level::Warn; - case ERR_ERROR: - return logging::Level::Error; - case ERR_FATAL: - return logging::Level::Fatal; - default: - throw invalid_argument("Unknown log level."); + case ERR_INFOCONT: return logging::Level::Trace; + case ERR_WARN: return logging::Level::Warn; + case ERR_ERROR: return logging::Level::Error; + case ERR_FATAL: return logging::Level::Fatal; + default: throw invalid_argument("Unknown log level."); } } @@ -110,19 +106,18 @@ BoundedTimeline recognizePhones( redirectPocketSphinxOutput(); // Prepare pool of decoders - ObjectPool> decoderPool( - [&] { return createDecoder(dialog); }); + ObjectPool> decoderPool([&] { + return createDecoder(dialog); + }); BoundedTimeline phones(audioClip->getTruncatedRange()); std::mutex resultMutex; - const auto processUtterance = [&](Timed timedUtterance, ProgressSink& utteranceProgressSink) { + const auto processUtterance = [&](Timed timedUtterance, + ProgressSink& utteranceProgressSink) { // Detect phones for utterance const auto decoder = decoderPool.acquire(); Timeline utterancePhones = utteranceToPhones( - *audioClip, - timedUtterance.getTimeRange(), - *decoder, - utteranceProgressSink + *audioClip, timedUtterance.getTimeRange(), *decoder, utteranceProgressSink ); // Copy phones to result timeline @@ -139,15 +134,18 @@ BoundedTimeline recognizePhones( // Perform speech recognition try { // Determine how many parallel threads to use - int threadCount = std::min({ - maxThreadCount, - // Don't use more threads than there are utterances to be processed - static_cast(utterances.size()), - // Don't waste time creating additional threads (and decoders!) if the recording is short - static_cast( - duration_cast(audioClip->getTruncatedRange().getDuration()).count() / 5 - ) - }); + int threadCount = std::min( + {maxThreadCount, + // Don't use more threads than there are utterances to be processed + static_cast(utterances.size()), + // Don't waste time creating additional threads (and decoders!) if the recording is + // short + static_cast( + duration_cast(audioClip->getTruncatedRange().getDuration()) + .count() + / 5 + )} + ); if (threadCount < 1) { threadCount = 1; } @@ -162,7 +160,9 @@ BoundedTimeline recognizePhones( ); logging::debug("Speech recognition -- end"); } catch (...) { - std::throw_with_nested(runtime_error("Error performing speech recognition via PocketSphinx tools.")); + std::throw_with_nested( + runtime_error("Error performing speech recognition via PocketSphinx tools.") + ); } return phones; @@ -206,8 +206,9 @@ BoundedTimeline recognizeWords(const vector& audioBuffer, ps_de // Process entire audio clip const bool noRecognition = false; const bool fullUtterance = true; - const int searchedFrameCount = - ps_process_raw(&decoder, audioBuffer.data(), audioBuffer.size(), noRecognition, fullUtterance); + const int searchedFrameCount = ps_process_raw( + &decoder, audioBuffer.data(), audioBuffer.size(), noRecognition, fullUtterance + ); if (searchedFrameCount < 0) { throw runtime_error("Error analyzing raw audio data for word recognition."); } @@ -227,7 +228,8 @@ BoundedTimeline recognizeWords(const vector& audioBuffer, ps_de // Not every utterance does contain speech, however. In this case, we exit early to prevent // the log output. // We *don't* to that in phonetic mode because here, the same code would omit valid phones. - const bool noWordsRecognized = reinterpret_cast(decoder.search)->bpidx == 0; + const bool noWordsRecognized = + reinterpret_cast(decoder.search)->bpidx == 0; if (noWordsRecognized) { return result; } diff --git a/rhubarb/src/recognition/pocketSphinxTools.h b/rhubarb/src/recognition/pocketSphinxTools.h index f5c8bc6..ed8ec1e 100644 --- a/rhubarb/src/recognition/pocketSphinxTools.h +++ b/rhubarb/src/recognition/pocketSphinxTools.h @@ -1,25 +1,26 @@ #pragma once -#include "time/BoundedTimeline.h" -#include "core/Phone.h" -#include "audio/AudioClip.h" -#include "tools/progress.h" #include +#include "audio/AudioClip.h" +#include "core/Phone.h" +#include "time/BoundedTimeline.h" +#include "tools/progress.h" + extern "C" { #include } -typedef std::function( - boost::optional dialog -)> decoderFactory; +typedef std::function(boost::optional dialog)> + decoderFactory; typedef std::function( const AudioClip& audioClip, TimeRange utteranceTimeRange, ps_decoder_t& decoder, ProgressSink& utteranceProgressSink -)> utteranceToPhonesFunction; +)> + utteranceToPhonesFunction; BoundedTimeline recognizePhones( const AudioClip& inputAudioClip, @@ -37,6 +38,5 @@ const std::filesystem::path& getSphinxModelDirectory(); JoiningTimeline getNoiseSounds(TimeRange utteranceTimeRange, const Timeline& phones); BoundedTimeline recognizeWords( - const std::vector& audioBuffer, - ps_decoder_t& decoder + const std::vector& audioBuffer, ps_decoder_t& decoder ); diff --git a/rhubarb/src/recognition/tokenization.cpp b/rhubarb/src/recognition/tokenization.cpp index ed24230..e075dbc 100644 --- a/rhubarb/src/recognition/tokenization.cpp +++ b/rhubarb/src/recognition/tokenization.cpp @@ -1,22 +1,24 @@ #include "tokenization.h" -#include "tools/tools.h" -#include "tools/stringTools.h" -#include + #include +#include + +#include "tools/stringTools.h" +#include "tools/tools.h" extern "C" { #include -#include #include +#include } +using boost::optional; +using std::function; +using std::pair; +using std::regex; using std::runtime_error; using std::string; using std::vector; -using std::regex; -using std::pair; -using boost::optional; -using std::function; lambda_unique_ptr createDummyVoice() { lambda_unique_ptr voice(new_voice(), [](cst_voice* voice) { delete_voice(voice); }); @@ -28,9 +30,9 @@ lambda_unique_ptr createDummyVoice() { } static const cst_synth_module synth_method_normalize[] = { - { "tokenizer_func", default_tokenization }, // split text into tokens - { "textanalysis_func", default_textanalysis }, // transform tokens into words - { nullptr, nullptr } + {"tokenizer_func", default_tokenization}, // split text into tokens + {"textanalysis_func", default_textanalysis}, // transform tokens into words + {nullptr, nullptr} }; vector tokenizeViaFlite(const string& text) { @@ -38,10 +40,9 @@ vector tokenizeViaFlite(const string& text) { const string asciiText = utf8ToAscii(text); // Create utterance object with text - lambda_unique_ptr utterance( - new_utterance(), - [](cst_utterance* utterance) { delete_utterance(utterance); } - ); + lambda_unique_ptr utterance(new_utterance(), [](cst_utterance* utterance) { + delete_utterance(utterance); + }); utt_set_input_text(utterance.get(), asciiText.c_str()); lambda_unique_ptr voice = createDummyVoice(); utt_init(utterance.get(), voice.get()); @@ -52,11 +53,8 @@ vector tokenizeViaFlite(const string& text) { } vector result; - for ( - cst_item* item = relation_head(utt_relation(utterance.get(), "Word")); - item; - item = item_next(item) - ) { + for (cst_item* item = relation_head(utt_relation(utterance.get(), "Word")); item; + item = item_next(item)) { const char* word = item_feat_string(item, "name"); result.emplace_back(word); } @@ -64,11 +62,11 @@ vector tokenizeViaFlite(const string& text) { } optional findSimilarDictionaryWord( - const string& word, - const function& dictionaryContains + const string& word, const function& dictionaryContains ) { - for (bool addPeriod : { false, true }) { - for (int apostropheIndex = -1; apostropheIndex <= static_cast(word.size()); ++apostropheIndex) { + for (bool addPeriod : {false, true}) { + for (int apostropheIndex = -1; apostropheIndex <= static_cast(word.size()); + ++apostropheIndex) { string modified = word; if (apostropheIndex != -1) { modified.insert(apostropheIndex, "'"); @@ -87,8 +85,7 @@ optional findSimilarDictionaryWord( } vector tokenizeText( - const string& text, - const function& dictionaryContains + const string& text, const function& dictionaryContains ) { vector words = tokenizeViaFlite(text); @@ -101,13 +98,13 @@ vector tokenizeText( } // Turn some symbols into words, remove the rest - const static vector> replacements { - { regex("&"), "and" }, - { regex("\\*"), "times" }, - { regex("\\+"), "plus" }, - { regex("="), "equals" }, - { regex("@"), "at" }, - { regex("[^a-z']"), "" } + const static vector> replacements{ + {regex("&"), "and"}, + {regex("\\*"), "times"}, + {regex("\\+"), "plus"}, + {regex("="), "equals"}, + {regex("@"), "at"}, + {regex("[^a-z']"), ""} }; for (auto& word : words) { for (const auto& replacement : replacements) { diff --git a/rhubarb/src/recognition/tokenization.h b/rhubarb/src/recognition/tokenization.h index 904de99..b21dc12 100644 --- a/rhubarb/src/recognition/tokenization.h +++ b/rhubarb/src/recognition/tokenization.h @@ -1,10 +1,9 @@ #pragma once -#include #include #include +#include std::vector tokenizeText( - const std::string& text, - const std::function& dictionaryContains + const std::string& text, const std::function& dictionaryContains ); diff --git a/rhubarb/src/rhubarb/ExportFormat.cpp b/rhubarb/src/rhubarb/ExportFormat.cpp index 896320f..a37030e 100644 --- a/rhubarb/src/rhubarb/ExportFormat.cpp +++ b/rhubarb/src/rhubarb/ExportFormat.cpp @@ -12,11 +12,11 @@ string ExportFormatConverter::getTypeName() { } EnumConverter::member_data ExportFormatConverter::getMemberData() { - return member_data { - { ExportFormat::Dat, "dat" }, - { ExportFormat::Tsv, "tsv" }, - { ExportFormat::Xml, "xml" }, - { ExportFormat::Json, "json" } + return member_data{ + {ExportFormat::Dat, "dat"}, + {ExportFormat::Tsv, "tsv"}, + {ExportFormat::Xml, "xml"}, + {ExportFormat::Json, "json"} }; } diff --git a/rhubarb/src/rhubarb/ExportFormat.h b/rhubarb/src/rhubarb/ExportFormat.h index fbcb91f..87d79b7 100644 --- a/rhubarb/src/rhubarb/ExportFormat.h +++ b/rhubarb/src/rhubarb/ExportFormat.h @@ -2,16 +2,12 @@ #include "tools/EnumConverter.h" -enum class ExportFormat { - Dat, - Tsv, - Xml, - Json -}; +enum class ExportFormat { Dat, Tsv, Xml, Json }; class ExportFormatConverter : public EnumConverter { public: static ExportFormatConverter& get(); + protected: std::string getTypeName() override; member_data getMemberData() override; diff --git a/rhubarb/src/rhubarb/RecognizerType.cpp b/rhubarb/src/rhubarb/RecognizerType.cpp index 6107b45..e52dc13 100644 --- a/rhubarb/src/rhubarb/RecognizerType.cpp +++ b/rhubarb/src/rhubarb/RecognizerType.cpp @@ -12,9 +12,8 @@ string RecognizerTypeConverter::getTypeName() { } EnumConverter::member_data RecognizerTypeConverter::getMemberData() { - return member_data { - { RecognizerType::PocketSphinx, "pocketSphinx" }, - { RecognizerType::Phonetic, "phonetic" } + return member_data{ + {RecognizerType::PocketSphinx, "pocketSphinx"}, {RecognizerType::Phonetic, "phonetic"} }; } diff --git a/rhubarb/src/rhubarb/RecognizerType.h b/rhubarb/src/rhubarb/RecognizerType.h index 9990de9..b7601a6 100644 --- a/rhubarb/src/rhubarb/RecognizerType.h +++ b/rhubarb/src/rhubarb/RecognizerType.h @@ -2,14 +2,12 @@ #include "tools/EnumConverter.h" -enum class RecognizerType { - PocketSphinx, - Phonetic -}; +enum class RecognizerType { PocketSphinx, Phonetic }; class RecognizerTypeConverter : public EnumConverter { public: static RecognizerTypeConverter& get(); + protected: std::string getTypeName() override; member_data getMemberData() override; diff --git a/rhubarb/src/rhubarb/main.cpp b/rhubarb/src/rhubarb/main.cpp index 16cb2c1..c045553 100644 --- a/rhubarb/src/rhubarb/main.cpp +++ b/rhubarb/src/rhubarb/main.cpp @@ -1,67 +1,68 @@ -#include #include -#include -#include "core/appInfo.h" -#include "tools/NiceCmdLineOutput.h" -#include "logging/logging.h" -#include "logging/sinks.h" -#include "logging/formatters.h" #include -#include "exporters/Exporter.h" -#include "time/ContinuousTimeline.h" -#include "tools/stringTools.h" +#include + #include +#include #include -#include "tools/parallel.h" -#include "tools/exceptions.h" -#include "tools/textFiles.h" -#include "lib/rhubarbLib.h" -#include "ExportFormat.h" +#include + +#include "animation/targetShapeSet.h" +#include "core/appInfo.h" #include "exporters/DatExporter.h" +#include "exporters/Exporter.h" +#include "exporters/JsonExporter.h" #include "exporters/TsvExporter.h" #include "exporters/XmlExporter.h" -#include "exporters/JsonExporter.h" -#include "animation/targetShapeSet.h" -#include -#include "tools/platformTools.h" -#include "sinks.h" -#include "semanticEntries.h" -#include "RecognizerType.h" -#include "recognition/PocketSphinxRecognizer.h" +#include "ExportFormat.h" +#include "lib/rhubarbLib.h" +#include "logging/formatters.h" +#include "logging/logging.h" +#include "logging/sinks.h" #include "recognition/PhoneticRecognizer.h" +#include "recognition/PocketSphinxRecognizer.h" +#include "RecognizerType.h" +#include "semanticEntries.h" +#include "sinks.h" +#include "time/ContinuousTimeline.h" +#include "tools/exceptions.h" +#include "tools/NiceCmdLineOutput.h" +#include "tools/parallel.h" +#include "tools/platformTools.h" +#include "tools/stringTools.h" +#include "tools/textFiles.h" +using boost::optional; +using boost::adaptors::transformed; using std::exception; -using std::string; -using std::string; -using std::vector; -using std::unique_ptr; +using std::make_shared; using std::make_unique; using std::shared_ptr; -using std::make_shared; +using std::string; +using std::unique_ptr; +using std::vector; using std::filesystem::path; using std::filesystem::u8path; -using boost::adaptors::transformed; -using boost::optional; namespace tclap = TCLAP; // Tell TCLAP how to handle our types namespace TCLAP { - template<> - struct ArgTraits { - typedef ValueLike ValueCategory; - }; +template <> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; - template<> - struct ArgTraits { - typedef ValueLike ValueCategory; - }; +template <> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; - template<> - struct ArgTraits { - typedef ValueLike ValueCategory; - }; -} +template <> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; +} // namespace TCLAP shared_ptr createFileSink(const path& path, logging::Level minLevel) { auto file = make_shared(); @@ -74,12 +75,9 @@ shared_ptr createFileSink(const path& path, logging::Level minLev unique_ptr createRecognizer(RecognizerType recognizerType) { switch (recognizerType) { - case RecognizerType::PocketSphinx: - return make_unique(); - case RecognizerType::Phonetic: - return make_unique(); - default: - throw std::runtime_error("Unknown recognizer."); + case RecognizerType::PocketSphinx: return make_unique(); + case RecognizerType::Phonetic: return make_unique(); + default: throw std::runtime_error("Unknown recognizer."); } } @@ -92,14 +90,10 @@ unique_ptr createExporter( switch (exportFormat) { case ExportFormat::Dat: return make_unique(targetShapeSet, datFrameRate, datUsePrestonBlair); - case ExportFormat::Tsv: - return make_unique(); - case ExportFormat::Xml: - return make_unique(); - case ExportFormat::Json: - return make_unique(); - default: - throw std::runtime_error("Unknown export format."); + case ExportFormat::Tsv: return make_unique(); + case ExportFormat::Xml: return make_unique(); + case ExportFormat::Json: return make_unique(); + default: throw std::runtime_error("Unknown export format."); } } @@ -134,78 +128,118 @@ int main(int platformArgc, char* platformArgv[]) { cmd.setOutput(new NiceCmdLineOutput()); tclap::ValueArg outputFileName( - "o", "output", "The output file path.", - false, string(), "string", cmd + "o", "output", "The output file path.", false, string(), "string", cmd ); auto logLevels = vector(logging::LevelConverter::get().getValues()); tclap::ValuesConstraint logLevelConstraint(logLevels); tclap::ValueArg logLevel( - "", "logLevel", "The minimum log level that will be written to the log file", - false, logging::Level::Debug, &logLevelConstraint, cmd + "", + "logLevel", + "The minimum log level that will be written to the log file", + false, + logging::Level::Debug, + &logLevelConstraint, + cmd ); tclap::ValueArg logFileName( - "", "logFile", "The log file path.", - false, string(), "string", cmd + "", "logFile", "The log file path.", false, string(), "string", cmd ); tclap::ValueArg consoleLevel( - "", "consoleLevel", "The minimum log level that will be printed on the console (stderr)", - false, defaultMinStderrLevel, &logLevelConstraint, cmd + "", + "consoleLevel", + "The minimum log level that will be printed on the console (stderr)", + false, + defaultMinStderrLevel, + &logLevelConstraint, + cmd ); tclap::SwitchArg machineReadableMode( - "", "machineReadable", "Formats all output to stderr in a structured JSON format.", - cmd, false + "", + "machineReadable", + "Formats all output to stderr in a structured JSON format.", + cmd, + false ); tclap::SwitchArg quietMode( - "q", "quiet", "Suppresses all output to stderr except for warnings and error messages.", - cmd, false + "q", + "quiet", + "Suppresses all output to stderr except for warnings and error messages.", + cmd, + false ); tclap::ValueArg maxThreadCount( - "", "threads", "The maximum number of worker threads to use.", - false, getProcessorCoreCount(), "number", cmd + "", + "threads", + "The maximum number of worker threads to use.", + false, + getProcessorCoreCount(), + "number", + cmd ); tclap::ValueArg extendedShapes( - "", "extendedShapes", "All extended, optional shapes to use.", - false, "GHX", "string", cmd + "", "extendedShapes", "All extended, optional shapes to use.", false, "GHX", "string", cmd ); tclap::ValueArg dialogFile( - "d", "dialogFile", "A file containing the text of the dialog.", - false, string(), "string", cmd + "d", + "dialogFile", + "A file containing the text of the dialog.", + false, + string(), + "string", + cmd ); tclap::SwitchArg datUsePrestonBlair( - "", "datUsePrestonBlair", "Only for dat exporter: uses the Preston Blair mouth shape names.", - cmd, false + "", + "datUsePrestonBlair", + "Only for dat exporter: uses the Preston Blair mouth shape names.", + cmd, + false ); tclap::ValueArg datFrameRate( - "", "datFrameRate", "Only for dat exporter: the desired frame rate.", - false, 24.0, "number", cmd + "", + "datFrameRate", + "Only for dat exporter: the desired frame rate.", + false, + 24.0, + "number", + cmd ); auto exportFormats = vector(ExportFormatConverter::get().getValues()); tclap::ValuesConstraint exportFormatConstraint(exportFormats); tclap::ValueArg exportFormat( - "f", "exportFormat", "The export format.", - false, ExportFormat::Tsv, &exportFormatConstraint, cmd + "f", + "exportFormat", + "The export format.", + false, + ExportFormat::Tsv, + &exportFormatConstraint, + cmd ); auto recognizerTypes = vector(RecognizerTypeConverter::get().getValues()); tclap::ValuesConstraint recognizerConstraint(recognizerTypes); tclap::ValueArg recognizerType( - "r", "recognizer", "The dialog recognizer.", - false, RecognizerType::PocketSphinx, &recognizerConstraint, cmd + "r", + "recognizer", + "The dialog recognizer.", + false, + RecognizerType::PocketSphinx, + &recognizerConstraint, + cmd ); tclap::UnlabeledValueArg inputFileName( - "inputFile", "The input file. Must be a sound file in WAVE format.", - true, "", "string", cmd + "inputFile", "The input file. Must be a sound file in WAVE format.", true, "", "string", cmd ); try { @@ -247,8 +281,10 @@ int main(int platformArgc, char* platformArgv[]) { ); logging::log(StartEntry(inputFilePath)); - logging::debugFormat("Command line: {}", - join(args | transformed([](string arg) { return fmt::format("\"{}\"", arg); }), " ")); + logging::debugFormat( + "Command line: {}", + join(args | transformed([](string arg) { return fmt::format("\"{}\"", arg); }), " ") + ); try { // On progress change: Create log message @@ -260,13 +296,13 @@ int main(int platformArgc, char* platformArgv[]) { logging::info("Starting animation."); JoiningContinuousTimeline animation = animateWaveFile( inputFilePath, - dialogFile.isSet() - ? readUtf8File(u8path(dialogFile.getValue())) - : boost::optional(), + dialogFile.isSet() ? readUtf8File(u8path(dialogFile.getValue())) + : boost::optional(), *createRecognizer(recognizerType.getValue()), targetShapeSet, maxThreadCount.getValue(), - progressSink); + progressSink + ); logging::info("Done animating."); // Export animation @@ -282,9 +318,9 @@ int main(int platformArgc, char* platformArgv[]) { logging::log(SuccessEntry()); } catch (...) { - std::throw_with_nested( - std::runtime_error(fmt::format("Error processing file {}.", inputFilePath.u8string())) - ); + std::throw_with_nested(std::runtime_error( + fmt::format("Error processing file {}.", inputFilePath.u8string()) + )); } return 0; diff --git a/rhubarb/src/rhubarb/semanticEntries.cpp b/rhubarb/src/rhubarb/semanticEntries.cpp index 19982c4..ccaee6d 100644 --- a/rhubarb/src/rhubarb/semanticEntries.cpp +++ b/rhubarb/src/rhubarb/semanticEntries.cpp @@ -4,13 +4,13 @@ using logging::Level; using std::string; SemanticEntry::SemanticEntry(Level level, const string& message) : - Entry(level, message) -{} + Entry(level, message) {} StartEntry::StartEntry(const std::filesystem::path& inputFilePath) : - SemanticEntry(Level::Info, fmt::format("Application startup. Input file: {}.", inputFilePath.u8string())), - inputFilePath(inputFilePath) -{} + SemanticEntry( + Level::Info, fmt::format("Application startup. Input file: {}.", inputFilePath.u8string()) + ), + inputFilePath(inputFilePath) {} std::filesystem::path StartEntry::getInputFilePath() const { return inputFilePath; @@ -18,21 +18,18 @@ std::filesystem::path StartEntry::getInputFilePath() const { ProgressEntry::ProgressEntry(double progress) : SemanticEntry(Level::Trace, fmt::format("Progress: {}%", static_cast(progress * 100))), - progress(progress) -{} + progress(progress) {} double ProgressEntry::getProgress() const { return progress; } SuccessEntry::SuccessEntry() : - SemanticEntry(Level::Info, "Application terminating normally.") -{} + SemanticEntry(Level::Info, "Application terminating normally.") {} FailureEntry::FailureEntry(const string& reason) : SemanticEntry(Level::Fatal, fmt::format("Application terminating with error: {}", reason)), - reason(reason) -{} + reason(reason) {} string FailureEntry::getReason() const { return reason; diff --git a/rhubarb/src/rhubarb/semanticEntries.h b/rhubarb/src/rhubarb/semanticEntries.h index b92b09f..996a127 100644 --- a/rhubarb/src/rhubarb/semanticEntries.h +++ b/rhubarb/src/rhubarb/semanticEntries.h @@ -1,7 +1,8 @@ #pragma once -#include "logging/Entry.h" #include +#include "logging/Entry.h" + // Marker class for semantic entries class SemanticEntry : public logging::Entry { public: @@ -12,6 +13,7 @@ class StartEntry : public SemanticEntry { public: StartEntry(const std::filesystem::path& inputFilePath); std::filesystem::path getInputFilePath() const; + private: std::filesystem::path inputFilePath; }; @@ -20,6 +22,7 @@ class ProgressEntry : public SemanticEntry { public: ProgressEntry(double progress); double getProgress() const; + private: double progress; }; @@ -33,6 +36,7 @@ class FailureEntry : public SemanticEntry { public: FailureEntry(const std::string& reason); std::string getReason() const; + private: std::string reason; }; diff --git a/rhubarb/src/rhubarb/sinks.cpp b/rhubarb/src/rhubarb/sinks.cpp index 1c12fb2..4807b2f 100644 --- a/rhubarb/src/rhubarb/sinks.cpp +++ b/rhubarb/src/rhubarb/sinks.cpp @@ -1,31 +1,32 @@ #include "sinks.h" -#include "logging/sinks.h" -#include "logging/formatters.h" -#include "semanticEntries.h" -#include "tools/stringTools.h" -#include "core/appInfo.h" + #include -using std::string; -using std::make_shared; -using logging::Level; -using logging::StdErrSink; -using logging::SimpleConsoleFormatter; +#include "core/appInfo.h" +#include "logging/formatters.h" +#include "logging/sinks.h" +#include "semanticEntries.h" +#include "tools/stringTools.h" + using boost::optional; +using logging::Level; +using logging::SimpleConsoleFormatter; +using logging::StdErrSink; +using std::make_shared; +using std::string; NiceStderrSink::NiceStderrSink(Level minLevel) : minLevel(minLevel), progress(0.0), - innerSink(make_shared(make_shared())) -{} + innerSink(make_shared(make_shared())) {} void NiceStderrSink::receive(const logging::Entry& entry) { // For selected semantic entries, print a user-friendly message instead of // the technical log message. if (const auto* startEntry = dynamic_cast(&entry)) { - std::cerr - << fmt::format("Generating lip sync data for {}.", startEntry->getInputFilePath().u8string()) - << std::endl; + std::cerr << fmt::format( + "Generating lip sync data for {}.", startEntry->getInputFilePath().u8string() + ) << std::endl; startProgressIndication(); } else if (const auto* progressEntry = dynamic_cast(&entry)) { assert(progressBar); @@ -62,8 +63,7 @@ void NiceStderrSink::resumeProgressIndication() { QuietStderrSink::QuietStderrSink(Level minLevel) : minLevel(minLevel), - innerSink(make_shared(make_shared())) -{} + innerSink(make_shared(make_shared())) {} void QuietStderrSink::receive(const logging::Entry& entry) { // Set inputFilePath as soon as we get it @@ -75,7 +75,9 @@ void QuietStderrSink::receive(const logging::Entry& entry) { if (quietSoFar) { // This is the first message we print. Give a bit of context. const string intro = inputFilePath - ? fmt::format("{} {} processing file {}:", appName, appVersion, inputFilePath->u8string()) + ? fmt::format( + "{} {} processing file {}:", appName, appVersion, inputFilePath->u8string() + ) : fmt::format("{} {}:", appName, appVersion); std::cerr << intro << std::endl; quietSoFar = false; @@ -85,8 +87,7 @@ void QuietStderrSink::receive(const logging::Entry& entry) { } MachineReadableStderrSink::MachineReadableStderrSink(Level minLevel) : - minLevel(minLevel) -{} + minLevel(minLevel) {} string formatLogProperty(const logging::Entry& entry) { return fmt::format( @@ -102,9 +103,7 @@ void MachineReadableStderrSink::receive(const logging::Entry& entry) { if (const auto* startEntry = dynamic_cast(&entry)) { const string file = escapeJsonString(startEntry->getInputFilePath().u8string()); line = fmt::format( - R"({{ "type": "start", "file": "{}", {} }})", - file, - formatLogProperty(entry) + R"({{ "type": "start", "file": "{}", {} }})", file, formatLogProperty(entry) ); } else if (const auto* progressEntry = dynamic_cast(&entry)) { const int progressPercent = static_cast(progressEntry->getProgress() * 100); @@ -121,9 +120,7 @@ void MachineReadableStderrSink::receive(const logging::Entry& entry) { } else if (const auto* failureEntry = dynamic_cast(&entry)) { const string reason = escapeJsonString(failureEntry->getReason()); line = fmt::format( - R"({{ "type": "failure", "reason": "{}", {} }})", - reason, - formatLogProperty(entry) + R"({{ "type": "failure", "reason": "{}", {} }})", reason, formatLogProperty(entry) ); } else { throw std::runtime_error("Unsupported type of semantic entry."); @@ -136,7 +133,8 @@ void MachineReadableStderrSink::receive(const logging::Entry& entry) { if (line) { std::cerr << *line << std::endl; - // Make sure the stream is flushed so that applications listening to it get the line immediately + // Make sure the stream is flushed so that applications listening to it get the line + // immediately fflush(stderr); } } diff --git a/rhubarb/src/rhubarb/sinks.h b/rhubarb/src/rhubarb/sinks.h index 5f395bc..c1d582c 100644 --- a/rhubarb/src/rhubarb/sinks.h +++ b/rhubarb/src/rhubarb/sinks.h @@ -1,16 +1,19 @@ #pragma once +#include + #include "logging/Entry.h" #include "logging/Sink.h" #include "tools/ProgressBar.h" -#include // Prints nicely formatted progress to stderr. -// Non-semantic entries are only printed if their log level at least matches the specified minimum level. +// Non-semantic entries are only printed if their log level at least matches the specified minimum +// level. class NiceStderrSink : public logging::Sink { public: NiceStderrSink(logging::Level minLevel); void receive(const logging::Entry& entry) override; + private: void startProgressIndication(); void interruptProgressIndication(); @@ -28,6 +31,7 @@ class QuietStderrSink : public logging::Sink { public: QuietStderrSink(logging::Level minLevel); void receive(const logging::Entry& entry) override; + private: logging::Level minLevel; bool quietSoFar = true; @@ -36,11 +40,13 @@ private: }; // Prints machine-readable progress to stderr. -// Non-semantic entries are only printed if their log level at least matches the specified minimum level. +// Non-semantic entries are only printed if their log level at least matches the specified minimum +// level. class MachineReadableStderrSink : public logging::Sink { public: MachineReadableStderrSink(logging::Level minLevel); void receive(const logging::Entry& entry) override; + private: logging::Level minLevel; int lastProgressPercent = -1; diff --git a/rhubarb/src/time/BoundedTimeline.h b/rhubarb/src/time/BoundedTimeline.h index 71b4c78..9ab74ea 100644 --- a/rhubarb/src/time/BoundedTimeline.h +++ b/rhubarb/src/time/BoundedTimeline.h @@ -2,7 +2,7 @@ #include "Timeline.h" -template +template class BoundedTimeline : public Timeline { using typename Timeline::time_type; using Timeline::equals; @@ -12,31 +12,26 @@ public: using Timeline::end; BoundedTimeline() : - range(TimeRange::zero()) - {} + range(TimeRange::zero()) {} explicit BoundedTimeline(TimeRange range) : - range(range) - {} + range(range) {} - template + template BoundedTimeline(TimeRange range, InputIterator first, InputIterator last) : - range(range) - { + range(range) { for (auto it = first; it != last; ++it) { // Virtual function call in constructor. Derived constructors shouldn't call this one! BoundedTimeline::set(*it); } } - template + template BoundedTimeline(TimeRange range, collection_type collection) : - BoundedTimeline(range, collection.begin(), collection.end()) - {} + BoundedTimeline(range, collection.begin(), collection.end()) {} BoundedTimeline(TimeRange range, std::initializer_list> initializerList) : - BoundedTimeline(range, initializerList.begin(), initializerList.end()) - {} + BoundedTimeline(range, initializerList.begin(), initializerList.end()) {} TimeRange getRange() const override { return range; @@ -53,8 +48,7 @@ public: // Clip the value's range to bounds TimeRange& valueRange = timedValue.getTimeRange(); valueRange.resize( - max(range.getStart(), valueRange.getStart()), - min(range.getEnd(), valueRange.getEnd()) + max(range.getStart(), valueRange.getStart()), min(range.getEnd(), valueRange.getEnd()) ); return Timeline::set(timedValue); @@ -77,5 +71,5 @@ private: TimeRange range; }; -template +template using JoiningBoundedTimeline = BoundedTimeline; diff --git a/rhubarb/src/time/ContinuousTimeline.h b/rhubarb/src/time/ContinuousTimeline.h index 0155739..1af6bc0 100644 --- a/rhubarb/src/time/ContinuousTimeline.h +++ b/rhubarb/src/time/ContinuousTimeline.h @@ -2,40 +2,33 @@ #include "BoundedTimeline.h" -template +template class ContinuousTimeline : public BoundedTimeline { - public: ContinuousTimeline(TimeRange range, T defaultValue) : BoundedTimeline(range), - defaultValue(defaultValue) - { + defaultValue(defaultValue) { // Virtual function call in constructor. Derived constructors shouldn't call this one! ContinuousTimeline::clear(range); } - template + template ContinuousTimeline(TimeRange range, T defaultValue, InputIterator first, InputIterator last) : - ContinuousTimeline(range, defaultValue) - { + ContinuousTimeline(range, defaultValue) { // Virtual function calls in constructor. Derived constructors shouldn't call this one! for (auto it = first; it != last; ++it) { ContinuousTimeline::set(*it); } } - template + template ContinuousTimeline(TimeRange range, T defaultValue, collection_type collection) : - ContinuousTimeline(range, defaultValue, collection.begin(), collection.end()) - {} + ContinuousTimeline(range, defaultValue, collection.begin(), collection.end()) {} ContinuousTimeline( - TimeRange range, - T defaultValue, - std::initializer_list> initializerList + TimeRange range, T defaultValue, std::initializer_list> initializerList ) : - ContinuousTimeline(range, defaultValue, initializerList.begin(), initializerList.end()) - {} + ContinuousTimeline(range, defaultValue, initializerList.begin(), initializerList.end()) {} using BoundedTimeline::clear; @@ -47,5 +40,5 @@ private: T defaultValue; }; -template +template using JoiningContinuousTimeline = ContinuousTimeline; diff --git a/rhubarb/src/time/TimeRange.cpp b/rhubarb/src/time/TimeRange.cpp index 3a612b8..628563d 100644 --- a/rhubarb/src/time/TimeRange.cpp +++ b/rhubarb/src/time/TimeRange.cpp @@ -1,8 +1,10 @@ #include "TimeRange.h" -#include -#include + #include +#include +#include + using time_type = TimeRange::time_type; TimeRange TimeRange::zero() { @@ -12,18 +14,14 @@ TimeRange TimeRange::zero() { TimeRange::TimeRange() : start(0_cs), - end(0_cs) -{} + end(0_cs) {} TimeRange::TimeRange(time_type start, time_type end) : start(start), - end(end) -{ + end(end) { if (start > end) { throw std::invalid_argument(fmt::format( - "Time range start must not be less than end. Start: {0}, end: {1}", - start, - end + "Time range start must not be less than end. Start: {0}, end: {1}", start, end )); } } @@ -97,11 +95,11 @@ void TimeRange::trim(const TimeRange& limits) { } void TimeRange::trimLeft(time_type value) { - trim({ value, end }); + trim({value, end}); } void TimeRange::trimRight(time_type value) { - trim({ start, value }); + trim({start, value}); } bool TimeRange::operator==(const TimeRange& rhs) const { diff --git a/rhubarb/src/time/TimeRange.h b/rhubarb/src/time/TimeRange.h index 4072b2f..28bc742 100644 --- a/rhubarb/src/time/TimeRange.h +++ b/rhubarb/src/time/TimeRange.h @@ -37,6 +37,7 @@ public: bool operator==(const TimeRange& rhs) const; bool operator!=(const TimeRange& rhs) const; + private: time_type start, end; }; diff --git a/rhubarb/src/time/Timed.h b/rhubarb/src/time/Timed.h index 881dfff..de0258f 100644 --- a/rhubarb/src/time/Timed.h +++ b/rhubarb/src/time/Timed.h @@ -1,19 +1,18 @@ #pragma once -#include "time/TimeRange.h" #include -template +#include "time/TimeRange.h" + +template class Timed { public: Timed(TimeRange::time_type start, TimeRange::time_type end, const TValue& value) : - Timed(TimeRange(start, end), value) - {} + Timed(TimeRange(start, end), value) {} Timed(const TimeRange& timeRange, const TValue& value) : timeRange(timeRange), - value(value) - {} + value(value) {} Timed(const Timed&) = default; Timed(Timed&&) = default; @@ -70,26 +69,20 @@ private: TValue value; }; -template +template std::ostream& operator<<(std::ostream& stream, const Timed& timedValue) { - return stream - << "Timed(" - << timedValue.getStart() << ", " - << timedValue.getEnd() << ", " - << timedValue.getValue() - << ")"; + return stream << "Timed(" << timedValue.getStart() << ", " << timedValue.getEnd() << ", " + << timedValue.getValue() << ")"; } -template<> +template <> class Timed { public: Timed(TimeRange::time_type start, TimeRange::time_type end) : - Timed(TimeRange(start, end)) - {} + Timed(TimeRange(start, end)) {} Timed(const TimeRange& timeRange) : - timeRange(timeRange) - {} + timeRange(timeRange) {} Timed(const Timed&) = default; Timed(Timed&&) = default; @@ -133,11 +126,8 @@ private: TimeRange timeRange; }; -template<> +template <> inline std::ostream& operator<<(std::ostream& stream, const Timed& timedValue) { - return stream - << "Timed(" - << timedValue.getTimeRange().getStart() << ", " - << timedValue.getTimeRange().getEnd() - << ")"; + return stream << "Timed(" << timedValue.getTimeRange().getStart() << ", " + << timedValue.getTimeRange().getEnd() << ")"; } diff --git a/rhubarb/src/time/Timeline.h b/rhubarb/src/time/Timeline.h index 1e455ad..ea7f1c7 100644 --- a/rhubarb/src/time/Timeline.h +++ b/rhubarb/src/time/Timeline.h @@ -1,32 +1,28 @@ #pragma once -#include "Timed.h" -#include #include +#include #include + +#include "Timed.h" #include "tools/tools.h" -enum class FindMode { - SampleLeft, - SampleRight, - SearchLeft, - SearchRight -}; +enum class FindMode { SampleLeft, SampleRight, SearchLeft, SearchRight }; namespace internal { - template - bool valueEquals(const Timed& lhs, const Timed& rhs) { - return lhs.getValue() == rhs.getValue(); - } - - template<> - inline bool valueEquals(const Timed& lhs, const Timed& rhs) { - UNUSED(lhs); - UNUSED(rhs); - return true; - } +template +bool valueEquals(const Timed& lhs, const Timed& rhs) { + return lhs.getValue() == rhs.getValue(); } -template +template <> +inline bool valueEquals(const Timed& lhs, const Timed& rhs) { + UNUSED(lhs); + UNUSED(rhs); + return true; +} +} // namespace internal + +template class Timeline { public: using time_type = TimeRange::time_type; @@ -84,8 +80,7 @@ public: ReferenceWrapper(Timeline& timeline, time_type time) : timeline(timeline), - time(time) - {} + time(time) {} Timeline& timeline; time_type time; @@ -93,7 +88,7 @@ public: Timeline() = default; - template + template Timeline(InputIterator first, InputIterator last) { for (auto it = first; it != last; ++it) { // Virtual function call in constructor. Derived constructors don't call this one. @@ -101,14 +96,12 @@ public: } } - template + template explicit Timeline(collection_type collection) : - Timeline(collection.begin(), collection.end()) - {} + Timeline(collection.begin(), collection.end()) {} explicit Timeline(std::initializer_list> initializerList) : - Timeline(initializerList.begin(), initializerList.end()) - {} + Timeline(initializerList.begin(), initializerList.end()) {} virtual ~Timeline() = default; @@ -121,9 +114,8 @@ public: } virtual TimeRange getRange() const { - return empty() - ? TimeRange(time_type::zero(), time_type::zero()) - : TimeRange(begin()->getStart(), rbegin()->getEnd()); + return empty() ? TimeRange(time_type::zero(), time_type::zero()) + : TimeRange(begin()->getStart(), rbegin()->getEnd()); } iterator begin() const { @@ -144,26 +136,22 @@ public: iterator find(time_type time, FindMode findMode = FindMode::SampleRight) const { switch (findMode) { - case FindMode::SampleLeft: - { + case FindMode::SampleLeft: { iterator left = find(time, FindMode::SearchLeft); return left != end() && left->getEnd() >= time ? left : end(); } - case FindMode::SampleRight: - { + case FindMode::SampleRight: { iterator right = find(time, FindMode::SearchRight); return right != end() && right->getStart() <= time ? right : end(); } - case FindMode::SearchLeft: - { + case FindMode::SearchLeft: { // Get first element starting >= time iterator it = elements.lower_bound(time); // Go one element back return it != begin() ? --it : end(); } - case FindMode::SearchRight: - { + case FindMode::SearchRight: { // Get first element starting > time iterator it = elements.upper_bound(time); @@ -175,8 +163,7 @@ public: } return it; } - default: - throw std::invalid_argument("Unexpected find mode."); + default: throw std::invalid_argument("Unexpected find mode."); } } @@ -229,15 +216,14 @@ public: return elements.insert(timedValue).first; } - template + template iterator set( - const TimeRange& timeRange, - const std::enable_if_t::value, T>& value + const TimeRange& timeRange, const std::enable_if_t::value, T>& value ) { return set(Timed(timeRange, value)); } - template + template iterator set( time_type start, time_type end, @@ -246,9 +232,8 @@ public: return set(Timed(start, end, value)); } - template - std::enable_if_t::value, iterator> - set(time_type start, time_type end) { + template + std::enable_if_t::value, iterator> set(time_type start, time_type end) { return set(Timed(start, end)); } @@ -262,22 +247,20 @@ public: } // Combines adjacent equal elements into one - template> + template > void joinAdjacent() { Timeline copy(*this); for (auto it = copy.begin(); it != copy.end(); ++it) { const auto rangeBegin = it; auto rangeEnd = std::next(rangeBegin); - while (rangeEnd != copy.end() - && rangeEnd->getStart() == rangeBegin->getEnd() - && ::internal::valueEquals(*rangeEnd, *rangeBegin) - ) { + while (rangeEnd != copy.end() && rangeEnd->getStart() == rangeBegin->getEnd() + && ::internal::valueEquals(*rangeEnd, *rangeBegin)) { ++rangeEnd; } if (rangeEnd != std::next(rangeBegin)) { Timed combined = *rangeBegin; - combined.setTimeRange({ rangeBegin->getStart(), rangeEnd->getEnd() }); + combined.setTimeRange({rangeBegin->getStart(), rangeEnd->getEnd()}); set(combined); it = rangeEnd; } @@ -318,7 +301,7 @@ private: iterator elementBefore = find(splitTime - time_type(1)); iterator elementAfter = find(splitTime); if (elementBefore != elementAfter || elementBefore == end()) return; - + Timed first = *elementBefore; Timed second = *elementBefore; elements.erase(elementBefore); @@ -331,10 +314,10 @@ private: set_type elements; }; -template +template using JoiningTimeline = Timeline; -template +template std::ostream& operator<<(std::ostream& stream, const Timeline& timeline) { stream << "Timeline{"; bool isFirst = true; diff --git a/rhubarb/src/time/centiseconds.cpp b/rhubarb/src/time/centiseconds.cpp index c31ffdd..e046407 100644 --- a/rhubarb/src/time/centiseconds.cpp +++ b/rhubarb/src/time/centiseconds.cpp @@ -1,11 +1,12 @@ +#include "centiseconds.h" + #include #include -#include "centiseconds.h" namespace std { - std::ostream& operator <<(std::ostream& stream, const centiseconds cs) { - return stream << cs.count() << "cs"; - } +std::ostream& operator<<(std::ostream& stream, const centiseconds cs) { + return stream << cs.count() << "cs"; +} -} \ No newline at end of file +} // namespace std diff --git a/rhubarb/src/time/centiseconds.h b/rhubarb/src/time/centiseconds.h index c589cd0..8becb3d 100644 --- a/rhubarb/src/time/centiseconds.h +++ b/rhubarb/src/time/centiseconds.h @@ -6,16 +6,19 @@ using centiseconds = std::chrono::duration; // Needs to be in the same namespace as std::chrono::duration, or googletest won't pick it up. -// See https://github.com/google/googletest/blob/master/docs/advanced.md#user-content-teaching-googletest-how-to-print-your-values +// See +// https://github.com/google/googletest/blob/master/docs/advanced.md#user-content-teaching-googletest-how-to-print-your-values namespace std { - std::ostream& operator <<(std::ostream&, centiseconds cs); - +std::ostream& operator<<(std::ostream&, centiseconds cs); + } #pragma warning(push) -#pragma warning(disable: 4455) -inline constexpr centiseconds operator "" _cs(unsigned long long cs) { +#pragma warning(disable : 4455) + +inline constexpr centiseconds operator"" _cs(unsigned long long cs) { return centiseconds(cs); } + #pragma warning(pop) diff --git a/rhubarb/src/time/timedLogging.h b/rhubarb/src/time/timedLogging.h index 319e558..e178846 100644 --- a/rhubarb/src/time/timedLogging.h +++ b/rhubarb/src/time/timedLogging.h @@ -1,11 +1,11 @@ #pragma once #include "centiseconds.h" -#include "TimeRange.h" -#include "Timed.h" #include "logging/logging.h" +#include "Timed.h" +#include "TimeRange.h" -template +template void logTimedEvent(const std::string& eventName, const Timed timedValue) { logging::debugFormat( "##{0}[{1}-{2}]: {3}", @@ -16,17 +16,14 @@ void logTimedEvent(const std::string& eventName, const Timed timedValue) ); } -template +template void logTimedEvent(const std::string& eventName, const TimeRange& timeRange, const TValue& value) { logTimedEvent(eventName, Timed(timeRange, value)); } -template +template void logTimedEvent( - const std::string& eventName, - centiseconds start, - centiseconds end, - const TValue& value + const std::string& eventName, centiseconds start, centiseconds end, const TValue& value ) { logTimedEvent(eventName, Timed(start, end, value)); -} \ No newline at end of file +} diff --git a/rhubarb/src/tools/EnumConverter.h b/rhubarb/src/tools/EnumConverter.h index d5f4947..1e9f32c 100644 --- a/rhubarb/src/tools/EnumConverter.h +++ b/rhubarb/src/tools/EnumConverter.h @@ -1,27 +1,25 @@ #pragma once -#include -#include -#include -#include -#include #include -template +#include +#include +#include +#include +#include + +template class EnumConverter { public: EnumConverter() : - initialized(false) - {} + initialized(false) {} virtual ~EnumConverter() = default; virtual boost::optional tryToString(TEnum value) { initialize(); auto it = valueToNameMap.find(value); - return it != valueToNameMap.end() - ? it->second - : boost::optional(); + return it != valueToNameMap.end() ? it->second : boost::optional(); } std::string toString(TEnum value) { @@ -40,9 +38,7 @@ public: virtual boost::optional tryParse(const std::string& s) { initialize(); auto it = lowerCaseNameToValueMap.find(boost::algorithm::to_lower_copy(s)); - return it != lowerCaseNameToValueMap.end() - ? it->second - : boost::optional(); + return it != lowerCaseNameToValueMap.end() ? it->second : boost::optional(); } TEnum parse(const std::string& s) { diff --git a/rhubarb/src/tools/Lazy.h b/rhubarb/src/tools/Lazy.h index 1ffbf87..18c607a 100644 --- a/rhubarb/src/tools/Lazy.h +++ b/rhubarb/src/tools/Lazy.h @@ -1,12 +1,12 @@ #pragma once +#include #include #include -#include // Class template for lazy initialization. // Copies use reference semantics. -template +template class Lazy { // Shared state between copies struct State { @@ -56,10 +56,9 @@ public: private: void init() const { - std::call_once( - state->initialized, - [&] { state->value = std::make_unique(state->createValue()); } - ); + std::call_once(state->initialized, [&] { + state->value = std::make_unique(state->createValue()); + }); } std::shared_ptr state = std::make_shared(); diff --git a/rhubarb/src/tools/NiceCmdLineOutput.cpp b/rhubarb/src/tools/NiceCmdLineOutput.cpp index 0057cad..fcb8278 100644 --- a/rhubarb/src/tools/NiceCmdLineOutput.cpp +++ b/rhubarb/src/tools/NiceCmdLineOutput.cpp @@ -1,13 +1,14 @@ -#include "regex" #include "NiceCmdLineOutput.h" + #include "platformTools.h" +#include "regex" #include "TablePrinter.h" +using std::cout; +using std::endl; using std::string; using std::vector; using TCLAP::CmdLineInterface; -using std::cout; -using std::endl; string getBinaryName() { return getBinPath().filename().u8string(); @@ -36,10 +37,9 @@ void NiceCmdLineOutput::failure(CmdLineInterface& cli, TCLAP::ArgException& e) { std::cerr << "Short usage:" << endl; printShortUsage(cli, std::cerr); - std::cerr - << endl - << "For complete usage and help, type `" << getBinaryName() << " --help`" << endl - << endl; + std::cerr << endl + << "For complete usage and help, type `" << getBinaryName() << " --help`" << endl + << endl; } else { usage(cli); } @@ -53,8 +53,9 @@ void NiceCmdLineOutput::printShortUsage(CmdLineInterface& cli, std::ostream& out const vector> xorArgGroups = xorHandler.getXorList(); for (const vector& xorArgGroup : xorArgGroups) { shortUsage += " {"; - - for (auto arg : xorArgGroup) shortUsage += arg->shortID() + "|"; + + for (auto arg : xorArgGroup) + shortUsage += arg->shortID() + "|"; shortUsage.pop_back(); shortUsage += '}'; @@ -64,7 +65,7 @@ void NiceCmdLineOutput::printShortUsage(CmdLineInterface& cli, std::ostream& out std::list argList = cli.getArgList(); for (auto arg : argList) { if (xorHandler.contains(arg)) continue; - + shortUsage += " " + arg->shortID(); } @@ -72,7 +73,7 @@ void NiceCmdLineOutput::printShortUsage(CmdLineInterface& cli, std::ostream& out } void NiceCmdLineOutput::printLongUsage(CmdLineInterface& cli, std::ostream& outStream) const { - TablePrinter tablePrinter(&outStream, { 20, 56 }); + TablePrinter tablePrinter(&outStream, {20, 56}); // Print XOR arguments TCLAP::XorHandler xorHandler = cli.getXorHandler(); @@ -83,7 +84,7 @@ void NiceCmdLineOutput::printLongUsage(CmdLineInterface& cli, std::ostream& outS outStream << "-- or --" << endl; } - tablePrinter.printRow({ arg->longID(), arg->getDescription() }); + tablePrinter.printRow({arg->longID(), arg->getDescription()}); } outStream << endl; } @@ -93,6 +94,6 @@ void NiceCmdLineOutput::printLongUsage(CmdLineInterface& cli, std::ostream& outS for (auto arg : argList) { if (xorHandler.contains(arg)) continue; - tablePrinter.printRow({ arg->longID(), arg->getDescription() }); + tablePrinter.printRow({arg->longID(), arg->getDescription()}); } -} \ No newline at end of file +} diff --git a/rhubarb/src/tools/NiceCmdLineOutput.h b/rhubarb/src/tools/NiceCmdLineOutput.h index 28a4210..95c2d34 100644 --- a/rhubarb/src/tools/NiceCmdLineOutput.h +++ b/rhubarb/src/tools/NiceCmdLineOutput.h @@ -7,6 +7,7 @@ public: void usage(TCLAP::CmdLineInterface& cli) override; void version(TCLAP::CmdLineInterface& cli) override; void failure(TCLAP::CmdLineInterface& cli, TCLAP::ArgException& e) override; + private: // Writes a brief usage message with short args. void printShortUsage(TCLAP::CmdLineInterface& cli, std::ostream& outStream) const; diff --git a/rhubarb/src/tools/ObjectPool.h b/rhubarb/src/tools/ObjectPool.h index 40b72cc..17d16a6 100644 --- a/rhubarb/src/tools/ObjectPool.h +++ b/rhubarb/src/tools/ObjectPool.h @@ -1,17 +1,16 @@ #pragma once -#include #include -#include +#include #include +#include -template> +template > class ObjectPool { public: using wrapper_type = lambda_unique_ptr; ObjectPool(std::function createObject) : - createObject(createObject) - {} + createObject(createObject) {} wrapper_type acquire() { std::lock_guard lock(poolMutex); diff --git a/rhubarb/src/tools/ProgressBar.cpp b/rhubarb/src/tools/ProgressBar.cpp index 0a97496..8b2a371 100644 --- a/rhubarb/src/tools/ProgressBar.cpp +++ b/rhubarb/src/tools/ProgressBar.cpp @@ -1,29 +1,26 @@ #include "ProgressBar.h" -#include -#include -#include #include + +#include #include +#include #include +#include #include using std::string; double sanitizeProgress(double progress) { // Make sure value is in [0..1] range - return std::isnan(progress) - ? 0.0 - : boost::algorithm::clamp(progress, 0.0, 1.0); + return std::isnan(progress) ? 0.0 : boost::algorithm::clamp(progress, 0.0, 1.0); } ProgressBar::ProgressBar(double progress) : - ProgressBar(std::cerr, progress) -{} + ProgressBar(std::cerr, progress) {} ProgressBar::ProgressBar(std::ostream& stream, double progress) : - stream(stream) -{ + stream(stream) { currentProgress = sanitizeProgress(progress); updateLoopFuture = std::async(std::launch::async, &ProgressBar::updateLoop, this); } @@ -59,11 +56,12 @@ void ProgressBar::update(bool showSpinner) { const int progressBlockCount = static_cast(currentProgress * blockCount); const double epsilon = 0.0001; const int percent = static_cast(currentProgress * 100 + epsilon); - const string spinner = showSpinner - ? string(1, animation[animationIndex++ % animation.size()]) - : ""; - const string text = fmt::format("[{0}{1}] {2:3}% {3}", - string(progressBlockCount, '#'), string(blockCount - progressBlockCount, '-'), + const string spinner = + showSpinner ? string(1, animation[animationIndex++ % animation.size()]) : ""; + const string text = fmt::format( + "[{0}{1}] {2:3}% {3}", + string(progressBlockCount, '#'), + string(blockCount - progressBlockCount, '-'), percent, spinner ); @@ -74,7 +72,8 @@ void ProgressBar::updateText(const string& text) { // Get length of common portion size_t commonPrefixLength = 0; const size_t commonLength = std::min(currentText.size(), text.size()); - while (commonPrefixLength < commonLength && text[commonPrefixLength] == currentText[commonPrefixLength]) { + while (commonPrefixLength < commonLength + && text[commonPrefixLength] == currentText[commonPrefixLength]) { commonPrefixLength++; } diff --git a/rhubarb/src/tools/ProgressBar.h b/rhubarb/src/tools/ProgressBar.h index fc8335b..b2b66ba 100644 --- a/rhubarb/src/tools/ProgressBar.h +++ b/rhubarb/src/tools/ProgressBar.h @@ -3,6 +3,7 @@ #include #include #include + #include "progress.h" class ProgressBar : public ProgressSink { @@ -26,8 +27,8 @@ private: void updateText(const std::string& text); std::future updateLoopFuture; - std::atomic currentProgress { 0 }; - std::atomic done { false }; + std::atomic currentProgress{0}; + std::atomic done{false}; std::ostream& stream; std::string currentText; diff --git a/rhubarb/src/tools/TablePrinter.cpp b/rhubarb/src/tools/TablePrinter.cpp index 3da8798..d1eef70 100644 --- a/rhubarb/src/tools/TablePrinter.cpp +++ b/rhubarb/src/tools/TablePrinter.cpp @@ -1,23 +1,26 @@ #include "TablePrinter.h" + #include -#include #include +#include + #include "stringTools.h" -using std::ostream; using std::initializer_list; using std::invalid_argument; -using std::vector; +using std::ostream; using std::string; +using std::vector; TablePrinter::TablePrinter(ostream* stream, initializer_list columnWidths, int columnSpacing) : stream(stream), columnWidths(columnWidths.begin(), columnWidths.end()), - columnSpacing(columnSpacing) -{ + columnSpacing(columnSpacing) { if (stream == nullptr) throw invalid_argument("stream is null."); if (columnWidths.size() < 1) throw invalid_argument("No columns defined."); - if (std::any_of(columnWidths.begin(), columnWidths.end(), [](int width) { return width <= 1; })) { + if (std::any_of(columnWidths.begin(), columnWidths.end(), [](int width) { + return width <= 1; + })) { throw invalid_argument("All columns must have a width of at least 1."); } if (columnSpacing < 0) throw invalid_argument("columnSpacing must not be negative."); @@ -25,7 +28,9 @@ TablePrinter::TablePrinter(ostream* stream, initializer_list columnWidths, void TablePrinter::printRow(initializer_list columns) const { if (columns.size() != columnWidths.size()) { - throw invalid_argument("Number of specified strings does not match number of defined columns."); + throw invalid_argument( + "Number of specified strings does not match number of defined columns." + ); } // Some cells may span multiple lines. diff --git a/rhubarb/src/tools/TablePrinter.h b/rhubarb/src/tools/TablePrinter.h index 3551901..b6d9035 100644 --- a/rhubarb/src/tools/TablePrinter.h +++ b/rhubarb/src/tools/TablePrinter.h @@ -7,11 +7,10 @@ class TablePrinter { public: TablePrinter( - std::ostream* stream, - std::initializer_list columnWidths, - int columnSpacing = 2 + std::ostream* stream, std::initializer_list columnWidths, int columnSpacing = 2 ); void printRow(std::initializer_list columns) const; + private: std::ostream* const stream; const std::vector columnWidths; diff --git a/rhubarb/src/tools/array.h b/rhubarb/src/tools/array.h index 9eb66ad..69daa93 100644 --- a/rhubarb/src/tools/array.h +++ b/rhubarb/src/tools/array.h @@ -5,41 +5,48 @@ // This file contains an implementation of std::experimental::make_array. namespace details { - template - struct negation : std::integral_constant {}; +template +struct negation : std::integral_constant {}; - template struct is_ref_wrapper : std::false_type {}; +template +struct is_ref_wrapper : std::false_type {}; - template struct is_ref_wrapper> : std::true_type {}; +template +struct is_ref_wrapper> : std::true_type {}; - template - using not_ref_wrapper = negation>>; +template +using not_ref_wrapper = negation>>; - template struct conjunction : std::true_type { }; +template +struct conjunction : std::true_type {}; - template struct conjunction : B1 { }; +template +struct conjunction : B1 {}; - template - struct conjunction - : std::conditional_t, B1> {}; +template +struct conjunction : std::conditional_t, B1> {}; - template - constexpr bool conjunction_v = conjunction::value; +template +constexpr bool conjunction_v = conjunction::value; - template struct return_type_helper { using type = D; }; +template +struct return_type_helper { + using type = D; +}; - template - struct return_type_helper : std::common_type { - static_assert(conjunction_v...>, - "Types cannot contain reference_wrappers when D is void"); - }; +template +struct return_type_helper : std::common_type { + static_assert( + conjunction_v...>, + "Types cannot contain reference_wrappers when D is void" + ); +}; - template - using return_type = std::array::type, - sizeof...(Types)>; -} +template +using return_type = std::array::type, sizeof...(Types)>; +} // namespace details -template +template constexpr details::return_type make_array(Types&&... t) { - return { std::forward(t)... }; -} \ No newline at end of file + return {std::forward(t)...}; +} diff --git a/rhubarb/src/tools/exceptions.cpp b/rhubarb/src/tools/exceptions.cpp index 1ece9bd..26690d5 100644 --- a/rhubarb/src/tools/exceptions.cpp +++ b/rhubarb/src/tools/exceptions.cpp @@ -1,9 +1,9 @@ -#include - #include "exceptions.h" -using std::string; +#include + using std::exception; +using std::string; string getMessage(const exception& e) { string result(e.what()); @@ -11,7 +11,8 @@ string getMessage(const exception& e) { rethrow_if_nested(e); } catch (const exception& innerException) { result += "\n" + getMessage(innerException); - } catch (...) {} + } catch (...) { + } return result; } diff --git a/rhubarb/src/tools/exceptions.h b/rhubarb/src/tools/exceptions.h index 176c5f6..8401456 100644 --- a/rhubarb/src/tools/exceptions.h +++ b/rhubarb/src/tools/exceptions.h @@ -2,4 +2,4 @@ #include -std::string getMessage(const std::exception& e); \ No newline at end of file +std::string getMessage(const std::exception& e); diff --git a/rhubarb/src/tools/fileTools.h b/rhubarb/src/tools/fileTools.h index d709c6a..6fe0c3a 100644 --- a/rhubarb/src/tools/fileTools.h +++ b/rhubarb/src/tools/fileTools.h @@ -1,8 +1,9 @@ #pragma once -#include "platformTools.h" -#include #include +#include + +#include "platformTools.h" std::ifstream openFile(std::filesystem::path filePath); -void throwIfNotReadable(std::filesystem::path filePath); \ No newline at end of file +void throwIfNotReadable(std::filesystem::path filePath); diff --git a/rhubarb/src/tools/nextCombination.h b/rhubarb/src/tools/nextCombination.h index 9f443aa..a13eb42 100644 --- a/rhubarb/src/tools/nextCombination.h +++ b/rhubarb/src/tools/nextCombination.h @@ -7,12 +7,12 @@ // // Designed after next_permutation in STL // Inspired by Mark Nelson's article http://www.dogma.net/markn/articles/Permutations/ -// +// // Start with a sorted container with thee iterators -- first, k, last -// After each iteration, the first k elements of the container will be +// After each iteration, the first k elements of the container will be // a combination. When there are no more combinations, the container // will return to the original sorted order. -template +template inline bool next_combination(const Iterator first, Iterator k, const Iterator last) { // Handle degenerate cases if (first == last || std::next(first) == last || first == k || k == last) { diff --git a/rhubarb/src/tools/pairs.h b/rhubarb/src/tools/pairs.h index e883392..f978523 100644 --- a/rhubarb/src/tools/pairs.h +++ b/rhubarb/src/tools/pairs.h @@ -1,7 +1,7 @@ #pragma once #include -template +template std::vector> getPairs( const TCollection& collection ) { diff --git a/rhubarb/src/tools/parallel.h b/rhubarb/src/tools/parallel.h index b85d2f9..f2c6495 100644 --- a/rhubarb/src/tools/parallel.h +++ b/rhubarb/src/tools/parallel.h @@ -1,16 +1,18 @@ #pragma once +#include + #include #include -#include "progress.h" -#include -template +#include "progress.h" + +template void runParallel( std::function processElement, TCollection& collection, - int maxThreadCount) -{ + int maxThreadCount +) { if (maxThreadCount < 1) { throw std::invalid_argument(fmt::format("maxThreadCount cannot be {}.", maxThreadCount)); } @@ -77,10 +79,9 @@ void runParallel( } } } - } -template +template void runParallel( const std::string& description, std::function processElement, @@ -88,8 +89,8 @@ void runParallel( int maxThreadCount, ProgressSink& progressSink, std::function getElementProgressWeight = - [](typename TCollection::reference) { return 1.0; }) -{ + [](typename TCollection::reference) { return 1.0; } +) { // Create a collection of wrapper functions that take care of progress handling ProgressMerger progressMerger(progressSink); std::vector> functions; diff --git a/rhubarb/src/tools/platformTools.cpp b/rhubarb/src/tools/platformTools.cpp index 832c14a..4cc67ef 100644 --- a/rhubarb/src/tools/platformTools.cpp +++ b/rhubarb/src/tools/platformTools.cpp @@ -1,23 +1,26 @@ -#include +#include "platformTools.h" + #include +#include +#include +#include + #include #include #include -#include "platformTools.h" -#include -#include -#include -#include "tools.h" #include +#include #include +#include "tools.h" + #ifdef _WIN32 - #include +#include #endif -using std::filesystem::path; using std::string; using std::vector; +using std::filesystem::path; path getBinPath() { static const path binPath = [] { @@ -33,7 +36,8 @@ path getBinPath() { // Actually, it does. // In case there are situations where it doesn't, we allocate one character more. std::vector buffer(pathLength + 1); - if (wai_getExecutablePath(buffer.data(), static_cast(buffer.size()), nullptr) == -1) { + if (wai_getExecutablePath(buffer.data(), static_cast(buffer.size()), nullptr) + == -1) { throw std::runtime_error("Error reading path."); } buffer[pathLength] = 0; @@ -43,7 +47,8 @@ path getBinPath() { path result(std::filesystem::canonical(pathString).make_preferred()); return result; } catch (...) { - std::throw_with_nested(std::runtime_error("Could not determine path of bin directory.")); + std::throw_with_nested(std::runtime_error("Could not determine path of bin directory.") + ); } }(); return binPath; @@ -61,7 +66,7 @@ path getTempFilePath() { } std::tm getLocalTime(const time_t& time) { - tm timeInfo {}; + tm timeInfo{}; #if (__unix || __linux || __APPLE__) localtime_r(&time, &timeInfo); #else @@ -119,8 +124,8 @@ vector argsToUtf8(int argc, char* argv[]) { class ConsoleBuffer : public std::stringbuf { public: - explicit ConsoleBuffer(FILE* file) - : file(file) {} + explicit ConsoleBuffer(FILE* file) : + file(file) {} int sync() override { fputs(str().c_str(), file); @@ -139,7 +144,8 @@ void useUtf8ForConsole() { SetConsoleOutputCP(CP_UTF8); // Prevent default stream buffer from chopping up UTF-8 byte sequences. - // See https://stackoverflow.com/questions/45575863/how-to-print-utf-8-strings-to-stdcout-on-windows + // See + // https://stackoverflow.com/questions/45575863/how-to-print-utf-8-strings-to-stdcout-on-windows std::cout.rdbuf(new ConsoleBuffer(stdout)); std::cerr.rdbuf(new ConsoleBuffer(stderr)); #endif diff --git a/rhubarb/src/tools/platformTools.h b/rhubarb/src/tools/platformTools.h index e93b056..dbd95a8 100644 --- a/rhubarb/src/tools/platformTools.h +++ b/rhubarb/src/tools/platformTools.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include #include diff --git a/rhubarb/src/tools/progress.cpp b/rhubarb/src/tools/progress.cpp index 999edcb..0411c1d 100644 --- a/rhubarb/src/tools/progress.cpp +++ b/rhubarb/src/tools/progress.cpp @@ -1,21 +1,20 @@ #include "progress.h" #include + #include "logging/logging.h" using std::string; ProgressForwarder::ProgressForwarder(std::function callback) : - callback(callback) -{} + callback(callback) {} void ProgressForwarder::reportProgress(double value) { callback(value); } ProgressMerger::ProgressMerger(ProgressSink& sink) : - sink(sink) -{} + sink(sink) {} ProgressMerger::~ProgressMerger() { for (const auto& source : sources) { @@ -35,17 +34,15 @@ ProgressSink& ProgressMerger::addSource(const std::string& description, double w totalWeight += weight; const int sourceIndex = static_cast(sources.size()); - sources.push_back({ - description, - weight, - std::make_unique( - [sourceIndex, this](double progress) { - sources[sourceIndex].progress = progress; - report(); - } - ), - 0.0 - }); + sources.push_back( + {description, + weight, + std::make_unique([sourceIndex, this](double progress) { + sources[sourceIndex].progress = progress; + report(); + }), + 0.0} + ); return *sources[sourceIndex].forwarder; } diff --git a/rhubarb/src/tools/progress.h b/rhubarb/src/tools/progress.h index 6763047..8b2d24a 100644 --- a/rhubarb/src/tools/progress.h +++ b/rhubarb/src/tools/progress.h @@ -1,14 +1,15 @@ #pragma once #include -#include -#include #include +#include #include +#include class ProgressSink { public: virtual ~ProgressSink() {} + virtual void reportProgress(double value) = 0; }; @@ -21,6 +22,7 @@ class ProgressForwarder : public ProgressSink { public: ProgressForwarder(std::function callback); void reportProgress(double value) override; + private: std::function callback; }; @@ -39,6 +41,7 @@ public: ProgressMerger(ProgressSink& sink); ~ProgressMerger(); ProgressSink& addSource(const std::string& description, double weight); + private: void report(); diff --git a/rhubarb/src/tools/stringTools.cpp b/rhubarb/src/tools/stringTools.cpp index 04d6642..f2616ff 100644 --- a/rhubarb/src/tools/stringTools.cpp +++ b/rhubarb/src/tools/stringTools.cpp @@ -1,16 +1,18 @@ #include "stringTools.h" -#include + +#include #include #include -#include -#include -using std::string; -using std::wstring; -using std::u32string; -using std::vector; +#include +#include + using std::regex; using std::regex_replace; +using std::string; +using std::u32string; +using std::vector; +using std::wstring; vector splitIntoLines(const string& s) { vector lines; @@ -29,16 +31,18 @@ vector splitIntoLines(const string& s) { } ++p; } - + return lines; } vector wrapSingleLineString(const string& s, int lineLength, int hangingIndent) { if (lineLength <= 0) throw std::invalid_argument("lineLength must be > 0."); if (hangingIndent < 0) throw std::invalid_argument("hangingIndent must be >= 0."); - if (hangingIndent >= lineLength) throw std::invalid_argument("hangingIndent must be < lineLength."); + if (hangingIndent >= lineLength) + throw std::invalid_argument("hangingIndent must be < lineLength."); if (s.find('\t') != std::string::npos) throw std::invalid_argument("s must not contain tabs."); - if (s.find('\n') != std::string::npos) throw std::invalid_argument("s must not contain line breaks."); + if (s.find('\n') != std::string::npos) + throw std::invalid_argument("s must not contain line breaks."); vector lines; auto p = &s[0]; @@ -67,7 +71,8 @@ vector wrapSingleLineString(const string& s, int lineLength, int hanging // Resume after the last line, skipping spaces p = lineEnd; - while (p != end && *p == ' ') ++p; + while (p != end && *p == ' ') + ++p; lineBegin = lineEnd = p; } @@ -102,23 +107,21 @@ wstring latin1ToWide(const string& s) { string utf8ToAscii(const string& s) { // Normalize string, simplifying it as much as possible const NormalizationOptions options = NormalizationOptions::CompatibilityMode - | NormalizationOptions::Decompose - | NormalizationOptions::SimplifyLineBreaks - | NormalizationOptions::SimplifyWhiteSpace - | NormalizationOptions::StripCharacterMarkings + | NormalizationOptions::Decompose | NormalizationOptions::SimplifyLineBreaks + | NormalizationOptions::SimplifyWhiteSpace | NormalizationOptions::StripCharacterMarkings | NormalizationOptions::StripIgnorableCharacters; string simplified = normalizeUnicode(s, options); // Replace common Unicode characters with ASCII equivalents - static const vector> replacements { - { regex("«|»|“|”|„|‟"), "\"" }, - { regex("‘|’|‚|‛|‹|›"), "'" }, - { regex("‐|‑|‒|⁃|⁻|₋|−|➖|–|—|―|﹘|﹣|-"), "-" }, - { regex("…|⋯"), "..." }, - { regex("•"), "*" }, - { regex("†|+"), "+" }, - { regex("⁄|∕|⧸|/|/"), "/" }, - { regex("×"), "x" }, + static const vector> replacements{ + {regex("«|»|“|”|„|‟"), "\""}, + {regex("‘|’|‚|‛|‹|›"), "'"}, + {regex("‐|‑|‒|⁃|⁻|₋|−|➖|–|—|―|﹘|﹣|-"), "-"}, + {regex("…|⋯"), "..."}, + {regex("•"), "*"}, + {regex("†|+"), "+"}, + {regex("⁄|∕|⧸|/|/"), "/"}, + {regex("×"), "x"}, }; for (const auto& replacement : replacements) { simplified = regex_replace(simplified, replacement.first, replacement.second); @@ -142,7 +145,8 @@ string normalizeUnicode(const string& s, NormalizationOptions options) { reinterpret_cast(s.data()), s.length(), reinterpret_cast(&result), - static_cast(options)); + static_cast(options) + ); if (charCount < 0) { const utf8proc_ssize_t errorCode = charCount; @@ -159,23 +163,22 @@ string normalizeUnicode(const string& s, NormalizationOptions options) { } string escapeJsonString(const string& s) { - // JavaScript uses UTF-16 internally. As a result, character escaping in JSON strings is UTF-16-based. - // Convert string to UTF-16 + // JavaScript uses UTF-16 internally. As a result, character escaping in JSON strings is + // UTF-16-based. Convert string to UTF-16 std::u16string utf16String; utf8::utf8to16(s.begin(), s.end(), std::back_inserter(utf16String)); string result; for (char16_t c : utf16String) { switch (c) { - case '"': result += "\\\""; break; - case '\\': result += "\\\\"; break; - case '\b': result += "\\b"; break; - case '\f': result += "\\f"; break; - case '\n': result += "\\n"; break; - case '\r': result += "\\r"; break; - case '\t': result += "\\t"; break; - default: - { + case '"': result += "\\\""; break; + case '\\': result += "\\\\"; break; + case '\b': result += "\\b"; break; + case '\f': result += "\\f"; break; + case '\n': result += "\\n"; break; + case '\r': result += "\\r"; break; + case '\t': result += "\\t"; break; + default: { const bool needsEscaping = c < '\x20' || c >= 0x80; if (needsEscaping) { result += fmt::format("\\u{0:04x}", c); diff --git a/rhubarb/src/tools/stringTools.h b/rhubarb/src/tools/stringTools.h index b52bee3..e09ca50 100644 --- a/rhubarb/src/tools/stringTools.h +++ b/rhubarb/src/tools/stringTools.h @@ -1,15 +1,14 @@ #pragma once -#include -#include #include +#include +#include + std::vector splitIntoLines(const std::string& s); std::vector wrapSingleLineString( - const std::string& s, - int lineLength, - int hangingIndent = 0 + const std::string& s, int lineLength, int hangingIndent = 0 ); std::vector wrapString(const std::string& s, int lineLength, int hangingIndent = 0); @@ -31,14 +30,13 @@ enum class NormalizationOptions : int { StripCharacterMarkings = UTF8PROC_STRIPMARK }; -constexpr NormalizationOptions -operator|(NormalizationOptions a, NormalizationOptions b) { +constexpr NormalizationOptions operator|(NormalizationOptions a, NormalizationOptions b) { return static_cast(static_cast(a) | static_cast(b)); } std::string normalizeUnicode(const std::string& s, NormalizationOptions options); -template +template std::string join(T range, const std::string separator) { std::string result; bool isFirst = true; @@ -50,4 +48,4 @@ std::string join(T range, const std::string separator) { return result; } -std::string escapeJsonString(const std::string& s); \ No newline at end of file +std::string escapeJsonString(const std::string& s); diff --git a/rhubarb/src/tools/textFiles.cpp b/rhubarb/src/tools/textFiles.cpp index 9086076..91294a6 100644 --- a/rhubarb/src/tools/textFiles.cpp +++ b/rhubarb/src/tools/textFiles.cpp @@ -1,6 +1,9 @@ #include "textFiles.h" + #include + #include + #include "stringTools.h" using std::string; @@ -21,7 +24,8 @@ string readUtf8File(path filePath) { return text; } catch (...) { - std::throw_with_nested(std::runtime_error(fmt::format("Error reading file {0}.", filePath.u8string()))); + std::throw_with_nested( + std::runtime_error(fmt::format("Error reading file {0}.", filePath.u8string())) + ); } } - diff --git a/rhubarb/src/tools/textFiles.h b/rhubarb/src/tools/textFiles.h index 4f1d5cd..37597c0 100644 --- a/rhubarb/src/tools/textFiles.h +++ b/rhubarb/src/tools/textFiles.h @@ -2,4 +2,4 @@ #include -std::string readUtf8File(std::filesystem::path filePath); \ No newline at end of file +std::string readUtf8File(std::filesystem::path filePath); diff --git a/rhubarb/src/tools/tools.cpp b/rhubarb/src/tools/tools.cpp index dd3f0dd..b95b40a 100644 --- a/rhubarb/src/tools/tools.cpp +++ b/rhubarb/src/tools/tools.cpp @@ -1,9 +1,12 @@ #include "tools.h" -#include "platformTools.h" + #include + #include #include +#include "platformTools.h" + using std::string; using std::chrono::duration; diff --git a/rhubarb/src/tools/tools.h b/rhubarb/src/tools/tools.h index 8417fa4..f3e25b6 100644 --- a/rhubarb/src/tools/tools.h +++ b/rhubarb/src/tools/tools.h @@ -1,25 +1,26 @@ #pragma once -#include -#include #include #include +#include +#include #include #define UNUSED(x) ((void)(x)) -template +template using lambda_unique_ptr = std::unique_ptr>; std::string formatDuration(std::chrono::duration seconds); std::string formatTime(time_t time, const std::string& format); -template +template void for_each_adjacent( iterator_type begin, iterator_type end, - std::function>&)> f + std::function< + void(const std::deque>&)> f ) { // Get the first n values iterator_type it = begin; @@ -39,32 +40,36 @@ void for_each_adjacent( } } -template +template void for_each_adjacent( iterator_type begin, iterator_type end, - std::function f + std::function< + void(const typename iterator_type::reference a, const typename iterator_type::reference b)> + f ) { for_each_adjacent<2>( begin, end, - [&](const std::deque>& args) { - f(args[0], args[1]); - } + [&](const std::deque>& args + ) { f(args[0], args[1]); } ); } -template +template void for_each_adjacent( iterator_type begin, iterator_type end, - std::function f + std::function f ) { for_each_adjacent<3>( begin, end, - [&](const std::deque>& args) { - f(args[0], args[1], args[2]); - } + [&](const std::deque>& args + ) { f(args[0], args[1], args[2]); } ); } diff --git a/rhubarb/src/tools/tupleHash.h b/rhubarb/src/tools/tupleHash.h index 3b68fc9..e33a6bc 100644 --- a/rhubarb/src/tools/tupleHash.h +++ b/rhubarb/src/tools/tupleHash.h @@ -3,38 +3,38 @@ #include namespace std { - - namespace { - template - void hash_combine(size_t& seed, const T& value) { - seed ^= std::hash()(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2); - } +namespace { - // Recursive template code derived from Matthieu M. - template::value - 1> - struct HashValueImpl { - static void apply(size_t& seed, const Tuple& tuple) { - HashValueImpl::apply(seed, tuple); - hash_combine(seed, std::get(tuple)); - } - }; +template +void hash_combine(size_t& seed, const T& value) { + seed ^= std::hash()(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} - template - struct HashValueImpl { - static void apply(size_t& seed, const Tuple& tuple) { - hash_combine(seed, std::get<0>(tuple)); - } - }; +// Recursive template code derived from Matthieu M. +template ::value - 1> +struct HashValueImpl { + static void apply(size_t& seed, const Tuple& tuple) { + HashValueImpl::apply(seed, tuple); + hash_combine(seed, std::get(tuple)); } +}; - template - struct hash> { - size_t operator()(const tuple& tt) const { - size_t seed = 0; - HashValueImpl>::apply(seed, tt); - return seed; - } - }; +template +struct HashValueImpl { + static void apply(size_t& seed, const Tuple& tuple) { + hash_combine(seed, std::get<0>(tuple)); + } +}; +} // namespace -} \ No newline at end of file +template +struct hash> { + size_t operator()(const tuple& tt) const { + size_t seed = 0; + HashValueImpl>::apply(seed, tt); + return seed; + } +}; + +} // namespace std diff --git a/rhubarb/tests/BoundedTimelineTests.cpp b/rhubarb/tests/BoundedTimelineTests.cpp index 0e28ec8..a270006 100644 --- a/rhubarb/tests/BoundedTimelineTests.cpp +++ b/rhubarb/tests/BoundedTimelineTests.cpp @@ -1,35 +1,23 @@ #include + #include "time/BoundedTimeline.h" using namespace testing; -using std::vector; using boost::optional; using std::initializer_list; +using std::vector; TEST(BoundedTimeline, constructors_initializeState) { const TimeRange range(-5_cs, 55_cs); auto args = { - Timed(-10_cs, 30_cs, 1), - Timed(10_cs, 40_cs, 2), - Timed(50_cs, 60_cs, 3) + Timed(-10_cs, 30_cs, 1), Timed(10_cs, 40_cs, 2), Timed(50_cs, 60_cs, 3) }; auto expected = { - Timed(-5_cs, 10_cs, 1), - Timed(10_cs, 40_cs, 2), - Timed(50_cs, 55_cs, 3) + Timed(-5_cs, 10_cs, 1), Timed(10_cs, 40_cs, 2), Timed(50_cs, 55_cs, 3) }; - EXPECT_THAT( - BoundedTimeline(range, args.begin(), args.end()), - ElementsAreArray(expected) - ); - EXPECT_THAT( - BoundedTimeline(range, vector>(args)), - ElementsAreArray(expected) - ); - EXPECT_THAT( - BoundedTimeline(range, args), - ElementsAreArray(expected) - ); + EXPECT_THAT(BoundedTimeline(range, args.begin(), args.end()), ElementsAreArray(expected)); + EXPECT_THAT(BoundedTimeline(range, vector>(args)), ElementsAreArray(expected)); + EXPECT_THAT(BoundedTimeline(range, args), ElementsAreArray(expected)); } TEST(BoundedTimeline, empty) { @@ -37,7 +25,7 @@ TEST(BoundedTimeline, empty) { EXPECT_TRUE(empty.empty()); EXPECT_THAT(empty, IsEmpty()); - BoundedTimeline nonEmpty(TimeRange(0_cs, 10_cs), { Timed(1_cs, 2_cs, 1) }); + BoundedTimeline nonEmpty(TimeRange(0_cs, 10_cs), {Timed(1_cs, 2_cs, 1)}); EXPECT_FALSE(nonEmpty.empty()); EXPECT_THAT(nonEmpty, Not(IsEmpty())); } @@ -47,7 +35,7 @@ TEST(BoundedTimeline, getRange) { BoundedTimeline empty(range); EXPECT_EQ(range, empty.getRange()); - BoundedTimeline nonEmpty(range, { Timed(1_cs, 2_cs, 1) }); + BoundedTimeline nonEmpty(range, {Timed(1_cs, 2_cs, 1)}); EXPECT_EQ(range, nonEmpty.getRange()); } @@ -84,12 +72,10 @@ TEST(BoundedTimeline, setAndClear) { TEST(BoundedTimeline, shift) { BoundedTimeline timeline( - TimeRange(0_cs, 10_cs), - { { 1_cs, 2_cs, 1 }, { 2_cs, 5_cs, 2 }, { 7_cs, 9_cs, 3 } } + TimeRange(0_cs, 10_cs), {{1_cs, 2_cs, 1}, {2_cs, 5_cs, 2}, {7_cs, 9_cs, 3}} ); BoundedTimeline expected( - TimeRange(2_cs, 12_cs), - { { 3_cs, 4_cs, 1 }, { 4_cs, 7_cs, 2 }, { 9_cs, 11_cs, 3 } } + TimeRange(2_cs, 12_cs), {{3_cs, 4_cs, 1}, {4_cs, 7_cs, 2}, {9_cs, 11_cs, 3}} ); timeline.shift(2_cs); EXPECT_EQ(expected, timeline); @@ -98,8 +84,8 @@ TEST(BoundedTimeline, shift) { TEST(BoundedTimeline, equality) { vector> timelines = { BoundedTimeline(TimeRange(0_cs, 10_cs)), - BoundedTimeline(TimeRange(0_cs, 10_cs), { { 1_cs, 2_cs, 1 } }), - BoundedTimeline(TimeRange(1_cs, 10_cs), { { 1_cs, 2_cs, 1 } }) + BoundedTimeline(TimeRange(0_cs, 10_cs), {{1_cs, 2_cs, 1}}), + BoundedTimeline(TimeRange(1_cs, 10_cs), {{1_cs, 2_cs, 1}}) }; for (size_t i = 0; i < timelines.size(); ++i) { @@ -108,8 +94,7 @@ TEST(BoundedTimeline, equality) { EXPECT_EQ(timelines[i], BoundedTimeline(timelines[j])) << "i: " << i << ", j: " << j; } else { - EXPECT_NE(timelines[i], timelines[j]) - << "i: " << i << ", j: " << j; + EXPECT_NE(timelines[i], timelines[j]) << "i: " << i << ", j: " << j; } } } diff --git a/rhubarb/tests/ContinuousTimelineTests.cpp b/rhubarb/tests/ContinuousTimelineTests.cpp index ed9ff0e..6c76c27 100644 --- a/rhubarb/tests/ContinuousTimelineTests.cpp +++ b/rhubarb/tests/ContinuousTimelineTests.cpp @@ -1,18 +1,17 @@ #include + #include "time/ContinuousTimeline.h" using namespace testing; -using std::vector; using boost::optional; using std::initializer_list; +using std::vector; TEST(ContinuousTimeline, constructors_initializeState) { const TimeRange range(-5_cs, 55_cs); const int defaultValue = -1; auto args = { - Timed(-10_cs, 30_cs, 1), - Timed(10_cs, 40_cs, 2), - Timed(50_cs, 60_cs, 3) + Timed(-10_cs, 30_cs, 1), Timed(10_cs, 40_cs, 2), Timed(50_cs, 60_cs, 3) }; auto expected = { Timed(-5_cs, 10_cs, 1), @@ -28,10 +27,7 @@ TEST(ContinuousTimeline, constructors_initializeState) { ContinuousTimeline(range, defaultValue, vector>(args)), ElementsAreArray(expected) ); - EXPECT_THAT( - ContinuousTimeline(range, defaultValue, args), - ElementsAreArray(expected) - ); + EXPECT_THAT(ContinuousTimeline(range, defaultValue, args), ElementsAreArray(expected)); } TEST(ContinuousTimeline, empty) { @@ -43,7 +39,7 @@ TEST(ContinuousTimeline, empty) { EXPECT_FALSE(nonEmpty1.empty()); EXPECT_THAT(nonEmpty1, Not(IsEmpty())); - ContinuousTimeline nonEmpty2(TimeRange(0_cs, 10_cs), -1, { Timed(1_cs, 2_cs, 1) }); + ContinuousTimeline nonEmpty2(TimeRange(0_cs, 10_cs), -1, {Timed(1_cs, 2_cs, 1)}); EXPECT_FALSE(nonEmpty2.empty()); EXPECT_THAT(nonEmpty2, Not(IsEmpty())); } @@ -83,14 +79,10 @@ TEST(ContinuousTimeline, setAndClear) { TEST(ContinuousTimeline, shift) { ContinuousTimeline timeline( - TimeRange(0_cs, 10_cs), - -1, - { { 1_cs, 2_cs, 1 }, { 2_cs, 5_cs, 2 }, { 7_cs, 9_cs, 3 } } + TimeRange(0_cs, 10_cs), -1, {{1_cs, 2_cs, 1}, {2_cs, 5_cs, 2}, {7_cs, 9_cs, 3}} ); ContinuousTimeline expected( - TimeRange(2_cs, 12_cs), - -1, - { { 3_cs, 4_cs, 1 }, { 4_cs, 7_cs, 2 }, { 9_cs, 11_cs, 3 } } + TimeRange(2_cs, 12_cs), -1, {{3_cs, 4_cs, 1}, {4_cs, 7_cs, 2}, {9_cs, 11_cs, 3}} ); timeline.shift(2_cs); EXPECT_EQ(expected, timeline); @@ -100,8 +92,8 @@ TEST(ContinuousTimeline, equality) { vector> timelines = { ContinuousTimeline(TimeRange(0_cs, 10_cs), -1), ContinuousTimeline(TimeRange(0_cs, 10_cs), 1), - ContinuousTimeline(TimeRange(0_cs, 10_cs), -1, { { 1_cs, 2_cs, 1 } }), - ContinuousTimeline(TimeRange(1_cs, 10_cs), -1, { { 1_cs, 2_cs, 1 } }) + ContinuousTimeline(TimeRange(0_cs, 10_cs), -1, {{1_cs, 2_cs, 1}}), + ContinuousTimeline(TimeRange(1_cs, 10_cs), -1, {{1_cs, 2_cs, 1}}) }; for (size_t i = 0; i < timelines.size(); ++i) { diff --git a/rhubarb/tests/LazyTests.cpp b/rhubarb/tests/LazyTests.cpp index 387de4b..914b342 100644 --- a/rhubarb/tests/LazyTests.cpp +++ b/rhubarb/tests/LazyTests.cpp @@ -1,4 +1,5 @@ #include + #include "tools/Lazy.h" using namespace testing; @@ -6,7 +7,9 @@ using namespace testing; // Not copyable, no default constructor, movable struct Foo { const int value; - Foo(int value) : value(value) {} + + Foo(int value) : + value(value) {} Foo() = delete; Foo(const Foo&) = delete; @@ -18,7 +21,10 @@ struct Foo { TEST(Lazy, basicUsage) { bool lambdaCalled = false; - Lazy lazy([&lambdaCalled] { lambdaCalled = true; return Foo(42); }); + Lazy lazy([&lambdaCalled] { + lambdaCalled = true; + return Foo(42); + }); EXPECT_FALSE(lambdaCalled); EXPECT_FALSE(static_cast(lazy)); EXPECT_EQ(42, (*lazy).value); @@ -30,7 +36,10 @@ TEST(Lazy, basicUsage) { TEST(Lazy, constUsage) { bool lambdaCalled = false; - const Lazy lazy([&lambdaCalled] { lambdaCalled = true; return Foo(42); }); + const Lazy lazy([&lambdaCalled] { + lambdaCalled = true; + return Foo(42); + }); EXPECT_FALSE(lambdaCalled); EXPECT_FALSE(static_cast(lazy)); EXPECT_EQ(42, (*lazy).value); @@ -71,5 +80,6 @@ TEST(Lazy, demo) { auto c = lazy->member; // Check if initialized - if (lazy) { /* ... */ } -} \ No newline at end of file + if (lazy) { /* ... */ + } +} diff --git a/rhubarb/tests/TimelineTests.cpp b/rhubarb/tests/TimelineTests.cpp index 05e741f..b158de2 100644 --- a/rhubarb/tests/TimelineTests.cpp +++ b/rhubarb/tests/TimelineTests.cpp @@ -1,37 +1,26 @@ #include -#include "time/Timeline.h" -#include + #include +#include + +#include "time/Timeline.h" using namespace testing; -using std::vector; +using boost::none; using boost::optional; using std::initializer_list; -using boost::none; +using std::vector; TEST(Timeline, constructors_initializeState) { auto args = { - Timed(-10_cs, 30_cs, 1), - Timed(10_cs, 40_cs, 2), - Timed(50_cs, 60_cs, 3) + Timed(-10_cs, 30_cs, 1), Timed(10_cs, 40_cs, 2), Timed(50_cs, 60_cs, 3) }; auto expected = { - Timed(-10_cs, 10_cs, 1), - Timed(10_cs, 40_cs, 2), - Timed(50_cs, 60_cs, 3) + Timed(-10_cs, 10_cs, 1), Timed(10_cs, 40_cs, 2), Timed(50_cs, 60_cs, 3) }; - EXPECT_THAT( - Timeline(args.begin(), args.end()), - ElementsAreArray(expected) - ); - EXPECT_THAT( - Timeline(vector>(args)), - ElementsAreArray(expected) - ); - EXPECT_THAT( - Timeline(args), - ElementsAreArray(expected) - ); + EXPECT_THAT(Timeline(args.begin(), args.end()), ElementsAreArray(expected)); + EXPECT_THAT(Timeline(vector>(args)), ElementsAreArray(expected)); + EXPECT_THAT(Timeline(args), ElementsAreArray(expected)); } TEST(Timeline, empty) { @@ -39,15 +28,15 @@ TEST(Timeline, empty) { EXPECT_TRUE(empty0.empty()); EXPECT_THAT(empty0, IsEmpty()); - Timeline empty1 {}; + Timeline empty1{}; EXPECT_TRUE(empty1.empty()); EXPECT_THAT(empty1, IsEmpty()); - Timeline empty2 { Timed(1_cs, 1_cs, 1) }; + Timeline empty2{Timed(1_cs, 1_cs, 1)}; EXPECT_TRUE(empty2.empty()); EXPECT_THAT(empty2, IsEmpty()); - Timeline nonEmpty { Timed(1_cs, 2_cs, 1) }; + Timeline nonEmpty{Timed(1_cs, 2_cs, 1)}; EXPECT_FALSE(nonEmpty.empty()); EXPECT_THAT(nonEmpty, Not(IsEmpty())); } @@ -57,19 +46,19 @@ TEST(Timeline, size) { EXPECT_EQ(0, empty0.size()); EXPECT_THAT(empty0, SizeIs(0)); - Timeline empty1 {}; + Timeline empty1{}; EXPECT_EQ(0, empty1.size()); EXPECT_THAT(empty1, SizeIs(0)); - Timeline empty2 { Timed(1_cs, 1_cs, 1) }; + Timeline empty2{Timed(1_cs, 1_cs, 1)}; EXPECT_EQ(0, empty2.size()); EXPECT_THAT(empty2, SizeIs(0)); - Timeline size1 { Timed(1_cs, 10_cs, 1) }; + Timeline size1{Timed(1_cs, 10_cs, 1)}; EXPECT_EQ(1, size1.size()); EXPECT_THAT(size1, SizeIs(1)); - Timeline size2 { Timed(-10_cs, 10_cs, 1), Timed(10_cs, 11_cs, 5) }; + Timeline size2{Timed(-10_cs, 10_cs, 1), Timed(10_cs, 11_cs, 5)}; EXPECT_EQ(2, size2.size()); EXPECT_THAT(size2, SizeIs(2)); } @@ -78,22 +67,22 @@ TEST(Timeline, getRange) { Timeline empty0; EXPECT_EQ(TimeRange(0_cs, 0_cs), empty0.getRange()); - Timeline empty1 {}; + Timeline empty1{}; EXPECT_EQ(TimeRange(0_cs, 0_cs), empty1.getRange()); - Timeline empty2 { Timed(1_cs, 1_cs, 1) }; + Timeline empty2{Timed(1_cs, 1_cs, 1)}; EXPECT_EQ(TimeRange(0_cs, 0_cs), empty2.getRange()); - Timeline nonEmpty1 { Timed(1_cs, 10_cs, 1) }; + Timeline nonEmpty1{Timed(1_cs, 10_cs, 1)}; EXPECT_EQ(TimeRange(1_cs, 10_cs), nonEmpty1.getRange()); - Timeline nonEmpty2 { Timed(-10_cs, 5_cs, 1), Timed(10_cs, 11_cs, 5) }; + Timeline nonEmpty2{Timed(-10_cs, 5_cs, 1), Timed(10_cs, 11_cs, 5)}; EXPECT_EQ(TimeRange(-10_cs, 11_cs), nonEmpty2.getRange()); } TEST(Timeline, iterators) { - Timeline timeline { Timed(-5_cs, 0_cs, 10), Timed(5_cs, 15_cs, 9) }; - auto expected = { Timed(-5_cs, 0_cs, 10), Timed(5_cs, 15_cs, 9) }; + Timeline timeline{Timed(-5_cs, 0_cs, 10), Timed(5_cs, 15_cs, 9)}; + auto expected = {Timed(-5_cs, 0_cs, 10), Timed(5_cs, 15_cs, 9)}; EXPECT_THAT(timeline, ElementsAreArray(expected)); vector> reversedActual; @@ -113,14 +102,17 @@ void testFind( auto it = timeline.find(centiseconds(++i), findMode); if (expectedResult != nullptr) { EXPECT_NE(it, timeline.end()) - << "Timeline: " << timeline << "; findMode: " << static_cast(findMode) << "; i: " << i; + << "Timeline: " << timeline << "; findMode: " << static_cast(findMode) + << "; i: " << i; if (it != timeline.end()) { EXPECT_EQ(*expectedResult, *it) - << "Timeline: " << timeline << "; findMode: " << static_cast(findMode) << "; i: " << i; + << "Timeline: " << timeline << "; findMode: " << static_cast(findMode) + << "; i: " << i; } } else { EXPECT_EQ(timeline.end(), it) - << "Timeline: " << timeline << "; findMode: " << static_cast(findMode) << "; i: " << i; + << "Timeline: " << timeline << "; findMode: " << static_cast(findMode) + << "; i: " << i; } } } @@ -129,22 +121,35 @@ TEST(Timeline, find) { Timed a = Timed(1_cs, 2_cs, 1); Timed b = Timed(2_cs, 5_cs, 2); Timed c = Timed(7_cs, 9_cs, 3); - const Timeline timeline { a, b, c }; + const Timeline timeline{a, b, c}; - testFind(timeline, FindMode::SampleLeft, { nullptr, nullptr, &a, &b, &b, &b, nullptr, nullptr, &c, &c, nullptr }); - testFind(timeline, FindMode::SampleRight, { nullptr, &a, &b, &b, &b, nullptr, nullptr, &c, &c, nullptr, nullptr }); - testFind(timeline, FindMode::SearchLeft, { nullptr, nullptr, &a, &b, &b, &b, &b, &b, &c, &c, &c }); - testFind(timeline, FindMode::SearchRight, { &a, &a, &b, &b, &b, &c, &c, &c, &c, nullptr, nullptr }); + testFind( + timeline, + FindMode::SampleLeft, + {nullptr, nullptr, &a, &b, &b, &b, nullptr, nullptr, &c, &c, nullptr} + ); + testFind( + timeline, + FindMode::SampleRight, + {nullptr, &a, &b, &b, &b, nullptr, nullptr, &c, &c, nullptr, nullptr} + ); + testFind( + timeline, FindMode::SearchLeft, {nullptr, nullptr, &a, &b, &b, &b, &b, &b, &c, &c, &c} + ); + testFind( + timeline, FindMode::SearchRight, {&a, &a, &b, &b, &b, &c, &c, &c, &c, nullptr, nullptr} + ); } TEST(Timeline, get) { Timed a = Timed(1_cs, 2_cs, 1); Timed b = Timed(2_cs, 5_cs, 2); Timed c = Timed(7_cs, 9_cs, 3); - Timeline timeline { a, b, c }; + Timeline timeline{a, b, c}; - initializer_list*> expectedResults = - { nullptr, &a, &b, &b, &b, nullptr, nullptr, &c, &c, nullptr, nullptr }; + initializer_list*> expectedResults = { + nullptr, &a, &b, &b, &b, nullptr, nullptr, &c, &c, nullptr, nullptr + }; int i = -1; for (Timed* expectedResult : expectedResults) { optional&> value = timeline.get(centiseconds(++i)); @@ -160,39 +165,39 @@ TEST(Timeline, get) { } TEST(Timeline, clear) { - const Timeline original { { 1_cs, 2_cs, 1 }, { 2_cs, 5_cs, 2 }, { 7_cs, 9_cs, 3 } }; - + const Timeline original{{1_cs, 2_cs, 1}, {2_cs, 5_cs, 2}, {7_cs, 9_cs, 3}}; + { auto timeline = original; timeline.clear(-10_cs, 10_cs); EXPECT_THAT(timeline, IsEmpty()); } - + { auto timeline = original; timeline.clear(1_cs, 2_cs); - Timeline expected { { 2_cs, 5_cs, 2 }, { 7_cs, 9_cs, 3 } }; + Timeline expected{{2_cs, 5_cs, 2}, {7_cs, 9_cs, 3}}; EXPECT_EQ(expected, timeline); } - + { auto timeline = original; timeline.clear(3_cs, 4_cs); - Timeline expected { { 1_cs, 2_cs, 1 }, { 2_cs, 3_cs, 2 }, { 4_cs, 5_cs, 2 }, { 7_cs, 9_cs, 3 } }; + Timeline expected{{1_cs, 2_cs, 1}, {2_cs, 3_cs, 2}, {4_cs, 5_cs, 2}, {7_cs, 9_cs, 3}}; EXPECT_EQ(expected, timeline); } - + { auto timeline = original; timeline.clear(6_cs, 8_cs); - Timeline expected { { 1_cs, 2_cs, 1 }, { 2_cs, 5_cs, 2 }, { 8_cs, 9_cs, 3 } }; + Timeline expected{{1_cs, 2_cs, 1}, {2_cs, 5_cs, 2}, {8_cs, 9_cs, 3}}; EXPECT_EQ(expected, timeline); } - + { auto timeline = original; timeline.clear(8_cs, 10_cs); - Timeline expected { { 1_cs, 2_cs, 1 }, { 2_cs, 5_cs, 2 }, { 7_cs, 8_cs, 3 } }; + Timeline expected{{1_cs, 2_cs, 1}, {2_cs, 5_cs, 2}, {7_cs, 8_cs, 3}}; EXPECT_EQ(expected, timeline); } } @@ -244,10 +249,13 @@ void testSetter(const std::function&, Timeline&)>& se EXPECT_LT(0_cs, element.getDuration()); // Element should match expected values - for (centiseconds t = std::max(centiseconds::zero(), element.getStart()); t < element.getEnd(); ++t) { + for (centiseconds t = std::max(centiseconds::zero(), element.getStart()); + t < element.getEnd(); + ++t) { optional expectedValue = expectedValues[t.count()]; EXPECT_TRUE(expectedValue) - << "Index " << t.count() << " should not have a value, but is within element " << element << ". " + << "Index " << t.count() << " should not have a value, but is within element " + << element << ". " << "newElementIndex: " << newElementIndex; if (expectedValue) { EXPECT_EQ(*expectedValue, element.getValue()); @@ -258,9 +266,7 @@ void testSetter(const std::function&, Timeline&)>& se } TEST(Timeline, set) { - testSetter([](const Timed& element, Timeline& timeline) { - timeline.set(element); - }); + testSetter([](const Timed& element, Timeline& timeline) { timeline.set(element); }); testSetter([](const Timed& element, Timeline& timeline) { timeline.set(element.getTimeRange(), element.getValue()); }); @@ -270,8 +276,8 @@ TEST(Timeline, set) { } TEST(Timeline, indexer_get) { - Timeline timeline { { 1_cs, 2_cs, 1 }, { 2_cs, 4_cs, 2 }, { 6_cs, 9_cs, 3 } }; - vector> expectedValues { none, 1, 2, 2, none, none, 3, 3, 3 }; + Timeline timeline{{1_cs, 2_cs, 1}, {2_cs, 4_cs, 2}, {6_cs, 9_cs, 3}}; + vector> expectedValues{none, 1, 2, 2, none, none, 3, 3, 3}; for (centiseconds t = 0_cs; t < 9_cs; ++t) { { optional actual = timeline[t]; @@ -303,63 +309,63 @@ TEST(Timeline, indexer_set) { } TEST(Timeline, joinAdjacent) { - Timeline timeline { - { 1_cs, 2_cs, 1 }, - { 2_cs, 4_cs, 2 }, - { 3_cs, 6_cs, 2 }, - { 6_cs, 7_cs, 2 }, + Timeline timeline{ + {1_cs, 2_cs, 1}, + {2_cs, 4_cs, 2}, + {3_cs, 6_cs, 2}, + {6_cs, 7_cs, 2}, // Gap - { 8_cs, 10_cs, 2 }, - { 11_cs, 12_cs, 3 } + {8_cs, 10_cs, 2}, + {11_cs, 12_cs, 3} }; EXPECT_EQ(6, timeline.size()); timeline.joinAdjacent(); EXPECT_EQ(4, timeline.size()); - + Timed expectedJoined[] = { - { 1_cs, 2_cs, 1 }, - { 2_cs, 7_cs, 2 }, + {1_cs, 2_cs, 1}, + {2_cs, 7_cs, 2}, // Gap - { 8_cs, 10_cs, 2 }, - { 11_cs, 12_cs, 3 } + {8_cs, 10_cs, 2}, + {11_cs, 12_cs, 3} }; EXPECT_THAT(timeline, ElementsAreArray(expectedJoined)); } TEST(Timeline, autoJoin) { - JoiningTimeline timeline { - { 1_cs, 2_cs, 1 }, - { 2_cs, 4_cs, 2 }, - { 3_cs, 6_cs, 2 }, - { 6_cs, 7_cs, 2 }, + JoiningTimeline timeline{ + {1_cs, 2_cs, 1}, + {2_cs, 4_cs, 2}, + {3_cs, 6_cs, 2}, + {6_cs, 7_cs, 2}, // Gap - { 8_cs, 10_cs, 2 }, - { 11_cs, 12_cs, 3 } + {8_cs, 10_cs, 2}, + {11_cs, 12_cs, 3} }; Timed expectedJoined[] = { - { 1_cs, 2_cs, 1 }, - { 2_cs, 7_cs, 2 }, + {1_cs, 2_cs, 1}, + {2_cs, 7_cs, 2}, // Gap - { 8_cs, 10_cs, 2 }, - { 11_cs, 12_cs, 3 } + {8_cs, 10_cs, 2}, + {11_cs, 12_cs, 3} }; EXPECT_EQ(4, timeline.size()); EXPECT_THAT(timeline, ElementsAreArray(expectedJoined)); } TEST(Timeline, shift) { - Timeline timeline { { 1_cs, 2_cs, 1 }, { 2_cs, 5_cs, 2 }, { 7_cs, 9_cs, 3 } }; - Timeline expected { { 3_cs, 4_cs, 1 }, { 4_cs, 7_cs, 2 }, { 9_cs, 11_cs, 3 } }; + Timeline timeline{{1_cs, 2_cs, 1}, {2_cs, 5_cs, 2}, {7_cs, 9_cs, 3}}; + Timeline expected{{3_cs, 4_cs, 1}, {4_cs, 7_cs, 2}, {9_cs, 11_cs, 3}}; timeline.shift(2_cs); EXPECT_EQ(expected, timeline); } TEST(Timeline, equality) { vector> timelines = { - Timeline {}, - Timeline { { 1_cs, 2_cs, 0 } }, - Timeline { { 1_cs, 2_cs, 1 } }, - Timeline { { -10_cs, 0_cs, 0 } } + Timeline{}, + Timeline{{1_cs, 2_cs, 0}}, + Timeline{{1_cs, 2_cs, 1}}, + Timeline{{-10_cs, 0_cs, 0}} }; for (size_t i = 0; i < timelines.size(); ++i) { diff --git a/rhubarb/tests/WaveFileReaderTests.cpp b/rhubarb/tests/WaveFileReaderTests.cpp index bd0fd6a..5f506a4 100644 --- a/rhubarb/tests/WaveFileReaderTests.cpp +++ b/rhubarb/tests/WaveFileReaderTests.cpp @@ -1,11 +1,13 @@ #include + #include "audio/WaveFileReader.h" #include "tools/platformTools.h" using namespace testing; TEST(getWaveFormatInfo, float32FromAudacity) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-float32-audacity.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-float32-audacity.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -15,7 +17,8 @@ TEST(getWaveFormatInfo, float32FromAudacity) { } TEST(getWaveFormatInfo, float32FromAudition) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-float32-audition.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-float32-audition.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -25,7 +28,8 @@ TEST(getWaveFormatInfo, float32FromAudition) { } TEST(getWaveFormatInfo, float32FromFfmpeg) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-float32-ffmpeg.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-float32-ffmpeg.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -35,7 +39,9 @@ TEST(getWaveFormatInfo, float32FromFfmpeg) { } TEST(getWaveFormatInfo, float32FromSoundforge) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-float32-soundforge.wav"); + auto formatInfo = getWaveFormatInfo( + getBinDirectory() / "tests/resources/sine-triangle-float32-soundforge.wav" + ); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -45,7 +51,8 @@ TEST(getWaveFormatInfo, float32FromSoundforge) { } TEST(getWaveFormatInfo, float64FromFfmpeg) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-float64-ffmpeg.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-float64-ffmpeg.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -55,7 +62,8 @@ TEST(getWaveFormatInfo, float64FromFfmpeg) { } TEST(getWaveFormatInfo, int16FromAudacity) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int16-audacity.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int16-audacity.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -65,7 +73,8 @@ TEST(getWaveFormatInfo, int16FromAudacity) { } TEST(getWaveFormatInfo, int16FromAudition) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int16-audition.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int16-audition.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -75,7 +84,8 @@ TEST(getWaveFormatInfo, int16FromAudition) { } TEST(getWaveFormatInfo, int16FromFfmpeg) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int16-ffmpeg.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int16-ffmpeg.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -85,7 +95,8 @@ TEST(getWaveFormatInfo, int16FromFfmpeg) { } TEST(getWaveFormatInfo, int16FromSoundforge) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int16-soundforge.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int16-soundforge.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -95,7 +106,8 @@ TEST(getWaveFormatInfo, int16FromSoundforge) { } TEST(getWaveFormatInfo, int24FromAudacity) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int24-audacity.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int24-audacity.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -105,7 +117,8 @@ TEST(getWaveFormatInfo, int24FromAudacity) { } TEST(getWaveFormatInfo, int24FromAudition) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int24-audition.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int24-audition.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -115,7 +128,8 @@ TEST(getWaveFormatInfo, int24FromAudition) { } TEST(getWaveFormatInfo, int24FromFfmpeg) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int24-ffmpeg.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int24-ffmpeg.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -125,7 +139,8 @@ TEST(getWaveFormatInfo, int24FromFfmpeg) { } TEST(getWaveFormatInfo, int24FromSoundforge) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int24-soundforge.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int24-soundforge.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -135,7 +150,8 @@ TEST(getWaveFormatInfo, int24FromSoundforge) { } TEST(getWaveFormatInfo, int32FromFfmpeg) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int32-ffmpeg.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int32-ffmpeg.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -145,7 +161,8 @@ TEST(getWaveFormatInfo, int32FromFfmpeg) { } TEST(getWaveFormatInfo, int32FromSoundforge) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int32-soundforge.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int32-soundforge.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -155,7 +172,8 @@ TEST(getWaveFormatInfo, int32FromSoundforge) { } TEST(getWaveFormatInfo, uint8FromAudition) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-uint8-audition.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-uint8-audition.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -165,7 +183,8 @@ TEST(getWaveFormatInfo, uint8FromAudition) { } TEST(getWaveFormatInfo, uint8FromFfmpeg) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-uint8-ffmpeg.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-uint8-ffmpeg.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -175,7 +194,8 @@ TEST(getWaveFormatInfo, uint8FromFfmpeg) { } TEST(getWaveFormatInfo, uint8FromSoundforge) { - auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-uint8-soundforge.wav"); + auto formatInfo = + getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-uint8-soundforge.wav"); EXPECT_EQ(formatInfo.frameRate, 48000); EXPECT_EQ(formatInfo.frameCount, 480000); EXPECT_EQ(formatInfo.channelCount, 2); @@ -183,4 +203,3 @@ TEST(getWaveFormatInfo, uint8FromSoundforge) { EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 1); EXPECT_EQ(formatInfo.dataOffset, 44); } - diff --git a/rhubarb/tests/g2pTests.cpp b/rhubarb/tests/g2pTests.cpp index 8217fb9..02e259e 100644 --- a/rhubarb/tests/g2pTests.cpp +++ b/rhubarb/tests/g2pTests.cpp @@ -1,10 +1,11 @@ #include + #include "recognition/g2p.h" using namespace testing; -using std::vector; using std::pair; using std::string; +using std::vector; TEST(wordToPhones, basic) { EXPECT_THAT(wordToPhones(""), IsEmpty()); @@ -12,21 +13,22 @@ TEST(wordToPhones, basic) { EXPECT_ANY_THROW(wordToPhones("Invalid")); // The following phones are based on actual output, *not* ideal output. - vector>> words { - { "once", { Phone::AA, Phone::N, Phone::S } }, - { "upon", { Phone::UW, Phone::P, Phone::AH, Phone::N } }, - { "a", { Phone::AH } }, - { "midnight", { Phone::M, Phone::IH, Phone::D, Phone::N, Phone::AY, Phone::T } }, - { "dreary", { Phone::D, Phone::R, Phone::IY, Phone::R, Phone::IY } }, - { "while", { Phone::W, Phone::AY, Phone::L } }, - { "i", { Phone::IY } }, - { "pondered", { Phone::P, Phone::AA, Phone::N, Phone::D, Phone::IY, Phone::R, Phone::EH, Phone::D } }, - { "weak", { Phone::W, Phone::IY, Phone::K } }, - { "and", { Phone::AE, Phone::N, Phone::D } }, - { "weary", { Phone::W, Phone::IY, Phone::R, Phone::IY } } + vector>> words{ + {"once", {Phone::AA, Phone::N, Phone::S}}, + {"upon", {Phone::UW, Phone::P, Phone::AH, Phone::N}}, + {"a", {Phone::AH}}, + {"midnight", {Phone::M, Phone::IH, Phone::D, Phone::N, Phone::AY, Phone::T}}, + {"dreary", {Phone::D, Phone::R, Phone::IY, Phone::R, Phone::IY}}, + {"while", {Phone::W, Phone::AY, Phone::L}}, + {"i", {Phone::IY}}, + {"pondered", + {Phone::P, Phone::AA, Phone::N, Phone::D, Phone::IY, Phone::R, Phone::EH, Phone::D}}, + {"weak", {Phone::W, Phone::IY, Phone::K}}, + {"and", {Phone::AE, Phone::N, Phone::D}}, + {"weary", {Phone::W, Phone::IY, Phone::R, Phone::IY}} }; for (const auto& word : words) { EXPECT_THAT(wordToPhones(word.first), ElementsAreArray(word.second)) << "Original word: '" << word.first << "'"; } -} \ No newline at end of file +} diff --git a/rhubarb/tests/pairsTests.cpp b/rhubarb/tests/pairsTests.cpp index aabef95..9434cce 100644 --- a/rhubarb/tests/pairsTests.cpp +++ b/rhubarb/tests/pairsTests.cpp @@ -1,9 +1,10 @@ #include + #include "tools/pairs.h" using namespace testing; -using std::vector; using std::pair; +using std::vector; TEST(getPairs, emptyCollection) { EXPECT_THAT(getPairs(vector()), IsEmpty()); @@ -15,19 +16,18 @@ TEST(getPairs, oneElementCollection) { TEST(getPairs, validCollection) { { - const auto actual = getPairs(vector { 1, 2 }); - const vector> expected { { 1, 2 } }; + const auto actual = getPairs(vector{1, 2}); + const vector> expected{{1, 2}}; EXPECT_THAT(actual, ElementsAreArray(expected)); } { - const auto actual = getPairs(vector { 1, 2, 3 }); - const vector> expected { { 1, 2 }, { 2, 3 } }; + const auto actual = getPairs(vector{1, 2, 3}); + const vector> expected{{1, 2}, {2, 3}}; EXPECT_THAT(actual, ElementsAreArray(expected)); } { - const auto actual = getPairs(vector { 1, 2, 3, 4 }); - const vector> expected { { 1, 2 }, { 2, 3 }, { 3, 4 } }; + const auto actual = getPairs(vector{1, 2, 3, 4}); + const vector> expected{{1, 2}, {2, 3}, {3, 4}}; EXPECT_THAT(actual, ElementsAreArray(expected)); } } - diff --git a/rhubarb/tests/stringToolsTests.cpp b/rhubarb/tests/stringToolsTests.cpp index bf031e8..d2feb5a 100644 --- a/rhubarb/tests/stringToolsTests.cpp +++ b/rhubarb/tests/stringToolsTests.cpp @@ -1,4 +1,5 @@ #include + #include "tools/stringTools.h" using namespace testing; @@ -22,7 +23,15 @@ TEST(splitIntoLines, handlesEmptyElements) { TEST(wrapSingleLineString, basic) { const char* lipsum = "Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua."; - EXPECT_THAT(wrapSingleLineString(lipsum, 30), ElementsAre("Lorem ipsum dolor sit amet,", "consectetur adipisici elit,", "sed eiusmod tempor incidunt ut", "labore et dolore magna aliqua.")); + EXPECT_THAT( + wrapSingleLineString(lipsum, 30), + ElementsAre( + "Lorem ipsum dolor sit amet,", + "consectetur adipisici elit,", + "sed eiusmod tempor incidunt ut", + "labore et dolore magna aliqua." + ) + ); } TEST(wrapSingleLineString, preciseWrapPosition) { @@ -71,7 +80,10 @@ TEST(wrapSingleLineString, errorHandling) { // wrapString TEST(wrapString, basic) { - EXPECT_THAT(wrapString("\n\nLine no 3\n\nLine no 4\n", 8), ElementsAre("", "", "Line no", "3", "", "Line no", "4", "")); + EXPECT_THAT( + wrapString("\n\nLine no 3\n\nLine no 4\n", 8), + ElementsAre("", "", "Line no", "3", "", "Line no", "4", "") + ); } // latin1ToWide @@ -89,7 +101,8 @@ TEST(latin1ToWide, basic) { TEST(utf8ToAscii, string) { EXPECT_EQ( "A naive man called was having pina colada and creme brulee.", - utf8ToAscii("A naïve man called 晨 was having piña colada and crème brûlée.")); + utf8ToAscii("A naïve man called 晨 was having piña colada and crème brûlée.") + ); EXPECT_EQ(string(""), utf8ToAscii("")); EXPECT_EQ(string("- - - - - - - - - -"), utf8ToAscii("- ‐ ‑ ‒ – — ― ﹘ ﹣ -")); EXPECT_EQ(string("' ' ' ' \" \" \" \" \" \""), utf8ToAscii("‘ ’ ‚ ‛ “ ” „ ‟ « »")); diff --git a/rhubarb/tests/tokenizationTests.cpp b/rhubarb/tests/tokenizationTests.cpp index e416859..e00e7a5 100644 --- a/rhubarb/tests/tokenizationTests.cpp +++ b/rhubarb/tests/tokenizationTests.cpp @@ -1,14 +1,16 @@ #include -#include "recognition/tokenization.h" -#include -#include #include +#include +#include + +#include "recognition/tokenization.h" + using namespace testing; +using std::regex; using std::string; using std::u32string; using std::vector; -using std::regex; bool returnTrue(const string&) { return true; @@ -54,8 +56,9 @@ TEST(tokenizeText, apostrophes) { "'Tis said he'd wish'd for a 'bus 'cause he wouldn't walk.", [](const string& word) { return word == "wouldn't"; } ), - ElementsAreArray( - vector{ "tis", "said", "he'd", "wish'd", "for", "a", "bus", "cause", "he", "wouldn't", "walk" }) + ElementsAreArray(vector{ + "tis", "said", "he'd", "wish'd", "for", "a", "bus", "cause", "he", "wouldn't", "walk" + }) ); } @@ -69,7 +72,9 @@ TEST(tokenizeText, math) { TEST(tokenizeText, unicodeCharacters) { EXPECT_THAT( tokenizeText("A naïve man called 晨 had piña colada and crème brûlée.", returnTrue), - ElementsAre("a", "naive", "man", "called", "had", "pina", "colada", "and", "creme", "brulee") + ElementsAre( + "a", "naive", "man", "called", "had", "pina", "colada", "and", "creme", "brulee" + ) ); }