diff --git a/Unreal/Content/Project/BP/BP_Project_Manager.uasset b/Unreal/Content/Project/BP/BP_Project_Manager.uasset index ded928f..18b10a5 100644 --- a/Unreal/Content/Project/BP/BP_Project_Manager.uasset +++ b/Unreal/Content/Project/BP/BP_Project_Manager.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3d9818ca5096fc9771249a60633fb3727a185e98fa27801ed1bb4f4ac7193fb -size 2088719 +oid sha256:0ea04cd757c12f79b430fe965bfc8f14f1d1a62088af3f496471b6bcc79d141c +size 2047584 diff --git a/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/Private/AIBaseManager.cpp b/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/Private/AIBaseManager.cpp index 56a90f1..33e68a8 100644 --- a/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/Private/AIBaseManager.cpp +++ b/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/Private/AIBaseManager.cpp @@ -434,6 +434,11 @@ void UAIBaseManager::OnRequestTimeout() SetNewState(EAvatarCoreAIState::Error); } +UMCPBaseManager* UAIBaseManager::GetMCPManager() +{ + return MCPManager; +} + // MCP Event Handler Implementations void UAIBaseManager::OnMCPLogReceived(const FString& LogMessage) { diff --git a/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/Public/AIBaseManager.h b/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/Public/AIBaseManager.h index 6345ea1..1115724 100644 --- a/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/Public/AIBaseManager.h +++ b/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/Public/AIBaseManager.h @@ -17,6 +17,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAIError, FString, ErrorMessage, DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FMulticastDelegateRealtimeAPIAudioChunk, const TArray, PCMData, bool, IsFinal); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnAIDelayedAnswer); +class UMCPManager; UCLASS(Abstract, Blueprintable, BlueprintType) class AVATARCORE_AI_API UAIBaseManager : public UObject @@ -154,6 +155,10 @@ public: UFUNCTION() void OnRequestTimeout(); + // Add a command at runtime (handles AddToRoot) + UFUNCTION(BlueprintCallable, Category = "AvatarCoreAI|MCP") + UMCPBaseManager* GetMCPManager(); + // Add a command at runtime (handles AddToRoot) UFUNCTION(BlueprintCallable, Category = "AvatarCoreAI|MCP Commands") void AddUnrealCommand(UMCPUnrealCommand* Command); diff --git a/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/Public/RealtimeAPI/AvatarCoreAIRealtime.h b/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/Public/RealtimeAPI/AvatarCoreAIRealtime.h index e8ca00e..235135e 100644 --- a/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/Public/RealtimeAPI/AvatarCoreAIRealtime.h +++ b/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/Public/RealtimeAPI/AvatarCoreAIRealtime.h @@ -95,22 +95,22 @@ struct FOpenAIRateLimit GENERATED_USTRUCT_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite) - int32 requestsLimit; + int32 requestsLimit = 0; UPROPERTY(EditAnywhere, BlueprintReadWrite) - int32 requestsRemaining; + int32 requestsRemaining = 0; UPROPERTY(EditAnywhere, BlueprintReadWrite) - int32 requestsResetTimeRemaining; + int32 requestsResetTimeRemaining = 0; UPROPERTY(EditAnywhere, BlueprintReadWrite) - int32 tokensLimit; + int32 tokensLimit = 0; UPROPERTY(EditAnywhere, BlueprintReadWrite) - int32 tokensRemaining; + int32 tokensRemaining = 0; UPROPERTY(EditAnywhere, BlueprintReadWrite) - int32 tokensResetTimeRemaining; + int32 tokensResetTimeRemaining = 0; }; DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMulticastDelegateRealtimeAPIMessageReceived, FString, ReceivedMessage); diff --git a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/AddDocumentsToDatabase.py b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/AddDocumentsToDatabase.py index 653f965..d92354d 100644 --- a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/AddDocumentsToDatabase.py +++ b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/AddDocumentsToDatabase.py @@ -1,10 +1,20 @@ # setup_db.py import os +import re from document_vectordb import DocumentVectorDB +from colorama import Fore + +def _sanitize_table_name(name: str) -> str: + name = name.strip().lower() + name = re.sub(r'[^a-z0-9]+', '_', name) + name = name.strip('_') + if not name: + return "documents" + return name def setup_database(): - print("Initializing database...") + print(Fore.GREEN + "Initializing database...") db = DocumentVectorDB() db.create_table() @@ -15,30 +25,44 @@ def setup_database(): if not os.path.exists(document_add_folder): os.makedirs(document_add_folder) - print(f"Created {document_add_folder} directory. Please add your PDF/text files there.") + print(Fore.GREEN + f"Created {document_add_folder} directory. Please add your PDF/text files there.") return if not os.path.exists(document_added_folder): os.makedirs(document_added_folder) files_added = 0 - for filename in os.listdir(document_add_folder): - if filename.endswith(('.pdf', '.txt')): - file_path = os.path.join(document_add_folder, filename) - copy_file_path = os.path.join(document_added_folder, filename) - print(f"Adding {filename}...") - try: - db.add_document(file_path) - files_added += 1 - os.rename(file_path, copy_file_path) - except Exception as e: - print(f"Error adding {filename}: {e}") + for root, dirs, files in os.walk(document_add_folder): + rel_root = os.path.relpath(root, document_add_folder) + if rel_root == ".": + current_table = "documents" + else: + first_folder = rel_root.split(os.sep)[0] + current_table = _sanitize_table_name(first_folder) + for filename in files: + if filename.endswith(('.pdf', '.txt')): + file_path = os.path.join(root, filename) + if rel_root == ".": + target_root = document_added_folder + else: + target_root = os.path.join(document_added_folder, rel_root) + if not os.path.exists(target_root): + os.makedirs(target_root) + copy_file_path = os.path.join(target_root, filename) + print(Fore.GREEN +f"Adding {file_path} to table {current_table}...") + try: + db.add_document(file_path, table_name=current_table) + files_added += 1 + os.rename(file_path, copy_file_path) + except Exception as e: + print(Fore.RED + f"Error adding {file_path}: {e}") db.finalize_db() - print(f"Database setup complete! Added {files_added} documents.") + print(Fore.GREEN + f"Database setup complete! Added {files_added} documents.") # Show stats stats = db.get_stats() - print(f"Database stats: {stats}") + print(Fore.GREEN + f"Database stats: {stats}") + if __name__ == "__main__": setup_database() \ No newline at end of file diff --git a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/FastMCPServer.py b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/FastMCPServer.py index 1396131..2f5ae9a 100644 --- a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/FastMCPServer.py +++ b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/FastMCPServer.py @@ -2,6 +2,8 @@ from fastmcp import FastMCP from typing import List, Dict, Any from document_vectordb import DocumentVectorDB import json +import re + mcp = FastMCP("AvatarCoreMCP_1_0", stateless_http=True) @@ -10,10 +12,14 @@ db = DocumentVectorDB() db.create_table() @mcp.tool() -def search_information_in_database(query: str) -> List[Dict[str, Any]]: - """If you need more information search this database.""" +def search_information(query: str) -> List[Dict[str, Any]]: + """If you need more information about search this database.""" try: - results = db.search(query, 10, True, 48, 12) #Boolean for Cuda based ReRanking + query = re.sub('Green Hydrogen Hub Stuttgart', '', query) + query = re.sub('Green Hydrogen Hub', '', query) + query = re.sub('GHH', '', query) + print(query) + results = db.search(query, 3,"documents", True, 48, 12) #Boolean for Cuda based ReRanking return results except Exception as e: return [{"error": f"Search failed: {str(e)}"}] diff --git a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/TestSearchDatabase.py b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/TestSearchDatabase.py index f0ed02b..cbab6a8 100644 --- a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/TestSearchDatabase.py +++ b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/TestSearchDatabase.py @@ -2,11 +2,15 @@ from document_vectordb import DocumentVectorDB import traceback import time +table = "documents" + def DoSearch(): + global table print("-------------------------", flush=True) + print(f'Which table to search? (empty for {table})', flush=True) + table = input() or table print('What do you wanna search?', flush=True) query = input() - #query = "Usability 101" if query == "": exit(); @@ -14,7 +18,7 @@ def DoSearch(): try: start_time = time.time() print("Calling db.search...", flush=True) - results = db.search(query, limit=10, rerank=True, candidates=48, batch_size=12) + results = db.search(query, limit=3, table_name=table, rerank=True, candidates=48, batch_size=12) print(f"db.search returned list of length: {len(results) if results is not None else 'None'}", flush=True) except Exception as e: print("Search raised an exception:", flush=True) diff --git a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/WipeDatabase.bat b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/WipeDatabase.bat index 052ea64..9366f93 100644 --- a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/WipeDatabase.bat +++ b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/WipeDatabase.bat @@ -1,7 +1,8 @@ @echo off cd /d "%~dp0" -robocopy documents_added documents_to_add /MOV +robocopy documents_added documents_to_add /MOV /S rmdir /S /Q lancedb +rmdir /S /Q documents_added echo ----------------------------------------- echo Farewall, my old friend! echo ----------------------------------------- diff --git a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/document_vectordb.py b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/document_vectordb.py index 5250f49..fca005e 100644 --- a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/document_vectordb.py +++ b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/document_vectordb.py @@ -2,10 +2,13 @@ import lancedb import pandas as pd from sentence_transformers import SentenceTransformer from sentence_transformers import CrossEncoder -import PyPDF2 +from pdfminer.high_level import extract_text +from langchain_text_splitters import RecursiveCharacterTextSplitter import pyarrow as pa import os import torch +import time +from colorama import Fore from typing import List, Dict import re from hashlib import sha1 @@ -14,11 +17,17 @@ class DocumentVectorDB: def __init__(self, db_path: str = "./lancedb"): self.db = lancedb.connect(db_path) # Model will be initialized in create_table() based on table vector dimension + + #Debug Variables + self.start_time = None + self.end_time = None + self.model = None self.model_dim = None self.model_name = None self.table_name = "documents" - self.reranker = None # lazy + self.reranker = None + self._load_reranker() def _load_reranker(self, model_name: str = "cross-encoder/ms-marco-MiniLM-L-6-v2"): if self.reranker is None: @@ -63,17 +72,11 @@ class DocumentVectorDB: # Default if undetectable return 384 - def create_table(self): - # Create table schema - schema = { - "id": str, - "content": str, - "source": str, - "vector": list - } - - # Create empty table if it doesn't exist - if self.table_name not in self.db.table_names(): + def _ensure_table(self, table_name: str): + if getattr(self, "table", None) is not None and self.table_name == table_name and self.model is not None: + return + self.table_name = table_name + if table_name not in self.db.table_names(): # Initialize a default 384-d model for a fresh table self._load_model_for_dim(384) emb_dim = self.model.get_sentence_embedding_dimension() @@ -86,61 +89,95 @@ class DocumentVectorDB: "doc_id": "sample.txt", "chunk_index": 0 }]) - self.table = self.db.create_table(self.table_name, sample_data) + self.table = self.db.create_table(table_name, sample_data) # Delete the sample data self.table.delete("id = 'sample'") else: - self.table = self.db.open_table(self.table_name) + self.table = self.db.open_table(table_name) # Infer vector dimension from existing table and load matching model dim = self._infer_table_vector_dim() self._load_model_for_dim(dim) + def create_table(self, table_name: str = "documents"): + # Create table schema + schema = { + "id": str, + "content": str, + "source": str, + "vector": list + } + self._ensure_table(table_name) + + def _create_index_for_current_table(self): + try: + row_count = self.table.count_rows() + except Exception: + row_count = 0 + + if row_count < 100: + return + + if row_count < 1000: + num_partitions = 1 + num_sub_vectors = 8 + elif row_count < 10000: + num_partitions = 8 + num_sub_vectors = 16 + elif row_count < 100000: + num_partitions = 32 + num_sub_vectors = 64 + else: + num_partitions = 90 + num_sub_vectors = 96 + + try: + self.table.create_index( + vector_column_name="vector", + index_type="IVF_PQ", + metric="cosine", + num_partitions=num_partitions, + num_sub_vectors=num_sub_vectors + ) + except Exception: + pass + + def clean_extracted_text(self, text: str) -> str: + """ + Cleans up common PDF extraction artifacts: + 1. Removes line-break hyphens. + 2. Replaces excessive whitespace and newlines. + """ + + text = re.sub(r'([a-z])-(\n\s*)(\n?)', r'\1', text, flags=re.IGNORECASE) + text = re.sub(r'\s+', ' ', text).strip() + + return text + def extract_text_from_pdf(self, pdf_path: str) -> str: text = "" - with open(pdf_path, 'rb') as file: - pdf_reader = PyPDF2.PdfReader(file) - for page in pdf_reader.pages: - text += page.extract_text() or "" - return text + # pdfminer.six is often more tolerant of broken PDF structure + try: + text = self.clean_extracted_text(extract_text(pdf_path)) + return text + except Exception as e: + print(f"pdfminer.six failed on {pdf_path}: {e}") + return "" # Or try pypdf as a fallback here + + def chunk_text(self, text: str, chunk_size: int = 512, overlap: int = 50) -> List[str]: + # Use characters (tokens) for chunking, not just words + splitter = RecursiveCharacterTextSplitter( + chunk_size=512, # Set chunk size to token/char count, not word count + chunk_overlap=50, + length_function=len, # Use character length + separators=["\n\n", "\n", ". ", " ", ""] # Hierarchical splitting + ) + # The splitters are highly optimized and handle the logic efficiently + chunks = splitter.split_text(text) + return chunks - def chunk_text(self, text: str, chunk_size: int = 150, overlap: int = 25) -> List[str]: - sentences = re.split(r'(?<=[\.\!\?])\s+', text.strip()) if text else [] - chunks = [] - current = [] - current_len = 0 - for s in sentences: - w = s.split() - if current_len + len(w) > chunk_size and current: - chunks.append(' '.join(current)) - if overlap > 0 and chunks: - back = [] - words_acc = 0 - for sent in reversed(current): - ws = sent.split() - if words_acc + len(ws) > overlap: - break - back.insert(0, sent) - words_acc += len(ws) - current = back - current_len = sum(len(x.split()) for x in current) - else: - current = [] - current_len = 0 - current.append(s) - current_len += len(w) - if current: - chunks.append(' '.join(current)) - return [c for c in chunks if c.strip()] - - def add_document(self, file_path: str, doc_type: str = "auto"): + def add_document(self, file_path: str, doc_type: str = "auto", table_name: str = "documents"): # Ensure model is initialized (in case add_document is used without create_table()) - if self.model is None: - if self.table_name in self.db.table_names(): - self.table = self.db.open_table(self.table_name) - dim = self._infer_table_vector_dim() - self._load_model_for_dim(dim) - else: - self._load_model_for_dim(384) + self._ensure_table(table_name) # Extract text based on file type if doc_type == "auto": @@ -155,6 +192,9 @@ class DocumentVectorDB: # Chunk the text chunks = self.chunk_text(text) + print(Fore.GREEN + f"{len(chunks)} chunks added.") + print(Fore.BLUE + chunks[0]) + # Create embeddings and store data_to_add = [] if chunks: @@ -166,6 +206,8 @@ class DocumentVectorDB: ) base = os.path.basename(file_path) for i, (chunk, emb) in enumerate(zip(chunks, embeddings)): + if len(chunk) < 1: + continue did = sha1(f"{base}|{i}|{len(chunk)}".encode("utf-8")).hexdigest()[:16] doc_data = { "id": did, @@ -181,32 +223,23 @@ class DocumentVectorDB: if data_to_add: df = pd.DataFrame(data_to_add) self.table.add(df) - try: - self.table.create_index(metric="cosine", num_partitions=90, num_sub_vectors=96) - except Exception: - pass + self._create_index_for_current_table() def finalize_db(self): - self.table.create_index( - vector_column_name="vector", # or your actual vector column name - index_type="IVF_PQ", - metric="cosine", - num_partitions=90, # tune by data size - num_sub_vectors=96 # tune by recall/latency - ) + self._create_index_for_current_table() + + def DebugTimeIt(self, TimedLabel=""): + if self.start_time is not None: + self.end_time = time.time() + elapsed_time = self.end_time - self.start_time + print(f"{TimedLabel}: Elapsed time {elapsed_time}") + self.start_time = time.time() - def search(self, query: str, limit: int = 5, rerank: bool = False, candidates: int = 100, batch_size: int = 64) -> List[Dict]: + def search(self, query: str, limit: int = 5, table_name: str = "documents", rerank: bool = False, candidates: int = 100, batch_size: int = 64) -> List[Dict]: # Ensure model is initialized - if self.model is None: - if self.table_name in self.db.table_names(): - self.table = self.db.open_table(self.table_name) - dim = self._infer_table_vector_dim() - self._load_model_for_dim(dim) - else: - self._load_model_for_dim(384) - + self._ensure_table(table_name) query_embedding = self.model.encode(query, normalize_embeddings=True).tolist() - + if rerank: print("Reranking...") raw = ( @@ -217,6 +250,7 @@ class DocumentVectorDB: .limit(candidates) .to_pandas() ) + if raw is None or raw.empty: return [] @@ -288,15 +322,46 @@ class DocumentVectorDB: def get_stats(self) -> Dict: try: - count = self.table.count_rows() + tables_info = [] + table_names = list(self.db.table_names()) + + old_table = getattr(self, "table", None) + old_table_name = getattr(self, "table_name", None) + + for name in table_names: + try: + tbl = self.db.open_table(name) + self.table = tbl + self.table_name = name + count = tbl.count_rows() + vector_dim = self._infer_table_vector_dim() + tables_info.append({ + "table_name": name, + "total_chunks": count, + "vector_dim": vector_dim, + }) + except Exception as inner_e: + tables_info.append({ + "table_name": name, + "error": str(inner_e), + }) + + if old_table is not None: + self.table = old_table + if old_table_name is not None: + self.table_name = old_table_name + return { "lancedb_version": lancedb.__version__, "pyarrow_version": pa.__version__, "torch_version": torch.__version__, - "total_chunks": count, - "table_name": self.table_name, - "model": self.model_name, - "vector_dim": self.model_dim, + "tables": tables_info, } except Exception as e: - return {"error": str(e)} \ No newline at end of file + return {"error": str(e)} + + def get_tables(self) -> List[str]: + try: + return list(self.db.table_names()) + except Exception: + return [] \ No newline at end of file diff --git a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/lancedb/documents.lance/_transactions/0-7032d51b-cf75-439d-b546-8a49bd9f7c9c.txn b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/lancedb/documents.lance/_transactions/0-7032d51b-cf75-439d-b546-8a49bd9f7c9c.txn new file mode 100644 index 0000000..e513fa8 Binary files /dev/null and b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/lancedb/documents.lance/_transactions/0-7032d51b-cf75-439d-b546-8a49bd9f7c9c.txn differ diff --git a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/lancedb/documents.lance/_transactions/1-e4aac62e-df93-4f1b-a546-43a8516937ae.txn b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/lancedb/documents.lance/_transactions/1-e4aac62e-df93-4f1b-a546-43a8516937ae.txn new file mode 100644 index 0000000..83e34b8 Binary files /dev/null and b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/lancedb/documents.lance/_transactions/1-e4aac62e-df93-4f1b-a546-43a8516937ae.txn differ diff --git a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/lancedb/documents.lance/_versions/1.manifest b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/lancedb/documents.lance/_versions/1.manifest new file mode 100644 index 0000000..d779827 Binary files /dev/null and b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/lancedb/documents.lance/_versions/1.manifest differ diff --git a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/lancedb/documents.lance/_versions/2.manifest b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/lancedb/documents.lance/_versions/2.manifest new file mode 100644 index 0000000..8ad68c7 Binary files /dev/null and b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/lancedb/documents.lance/_versions/2.manifest differ diff --git a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/lancedb/documents.lance/data/3e53aab5-bc5c-4db7-9f2c-31cdd3423b52.lance b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/lancedb/documents.lance/data/3e53aab5-bc5c-4db7-9f2c-31cdd3423b52.lance new file mode 100644 index 0000000..cd06d9e Binary files /dev/null and b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/lancedb/documents.lance/data/3e53aab5-bc5c-4db7-9f2c-31cdd3423b52.lance differ diff --git a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/requirements.txt b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/requirements.txt index 7fa987a..598871a 100644 --- a/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/requirements.txt +++ b/Unreal/Plugins/AvatarCore_AI/Source/ThirdParty/MCPServer/FastMCP/requirements.txt @@ -8,7 +8,7 @@ fastmcp==2.10.6 lancedb==0.25.3 pandas==2.3.1 sentence-transformers==5.1.0 -PyPDF2==3.0.1 +pdfminer.six==20251107 torch==2.9.0+cu128 transformers==4.55.0 huggingface-hub==0.34.4 @@ -28,4 +28,5 @@ httpx==0.28.1 httpx-sse==0.4.1 pydantic==2.11.7 pydantic-settings==2.10.1 +langchain-text-splitters==1.0.0 PyYAML==6.0.2 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/AvatarCoreLogging.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/AvatarCoreLogging.uasset index da74a9b..d7e23e4 100644 Binary files a/Unreal/Plugins/AvatarCore_Manager/Content/AvatarCoreLogging.uasset and b/Unreal/Plugins/AvatarCore_Manager/Content/AvatarCoreLogging.uasset differ diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/AvatarCoreManager.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/AvatarCoreManager.uasset index ebedb15..f0b2ca9 100644 Binary files a/Unreal/Plugins/AvatarCore_Manager/Content/AvatarCoreManager.uasset and b/Unreal/Plugins/AvatarCore_Manager/Content/AvatarCoreManager.uasset differ diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/PlaceableCamera_BP.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/PlaceableCamera_BP.uasset index a64c66c..6e2c233 100644 Binary files a/Unreal/Plugins/AvatarCore_Manager/Content/PlaceableCamera_BP.uasset and b/Unreal/Plugins/AvatarCore_Manager/Content/PlaceableCamera_BP.uasset differ diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/S_AvatarCoreSettings.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/S_AvatarCoreSettings.uasset index eb4d86b..a1a63f0 100644 Binary files a/Unreal/Plugins/AvatarCore_Manager/Content/S_AvatarCoreSettings.uasset and b/Unreal/Plugins/AvatarCore_Manager/Content/S_AvatarCoreSettings.uasset differ diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/BFL_ProjectHelper.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/BFL_ProjectHelper.uasset new file mode 100644 index 0000000..33873be --- /dev/null +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/BFL_ProjectHelper.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:283aab650b234d29f170c4483d9c5e275e8682631784727cef6939929a5b50df +size 181186 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/BP_StateManager.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/BP_StateManager.uasset new file mode 100644 index 0000000..7d80d0d --- /dev/null +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/BP_StateManager.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d5d3d4446c8709761ad242aa53d1e118ead318b1459adb6d0cf49e88044b901 +size 438359 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/S_StateProcedure.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/S_StateProcedure.uasset new file mode 100644 index 0000000..3b18ce4 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/S_StateProcedure.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5baae4670d0bd968b935dd97eea0f755bf2bcaecc38b2fbac4bfdd79dfceb857 +size 18251 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/S_WidgetConfigs.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/S_WidgetConfigs.uasset new file mode 100644 index 0000000..8c8e0b7 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/S_WidgetConfigs.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5a51ece9e7912195effab2ec2366a1a59ef046aa73630f27b5d5094b9fb2662d +size 13553 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/W_BaseWidget.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/W_BaseWidget.uasset new file mode 100644 index 0000000..263aacb --- /dev/null +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/W_BaseWidget.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21f6fccacac05b0ee9800d56ef1605ff4a2e84776b58134b72165ef795e6b7a6 +size 177821 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/AICommand_CurrentTime.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/AICommand_CurrentTime.uasset new file mode 100644 index 0000000..4382dc7 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/AICommand_CurrentTime.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:76d8c0a10ba8097afb47a1e62d6164888733408388a255f548b5e2c516d58886 +size 27908 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreAI.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreAI.uasset index 074ad0d..8a29a0e 100644 Binary files a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreAI.uasset and b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreAI.uasset differ diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreAnimation.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreAnimation.uasset index b3469cb..b6b5633 100644 Binary files a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreAnimation.uasset and b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreAnimation.uasset differ diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreSTT.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreSTT.uasset index 0f91e24..3cf3fe9 100644 Binary files a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreSTT.uasset and b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreSTT.uasset differ diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreStat.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreStat.uasset index fc1c7c6..c8a1a06 100644 Binary files a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreStat.uasset and b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreStat.uasset differ diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreTTS.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreTTS.uasset index 045e92e..4323b64 100644 Binary files a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreTTS.uasset and b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreTTS.uasset differ diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugProjectStates.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugProjectStates.uasset new file mode 100644 index 0000000..52315c9 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugProjectStates.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5374f6019d831cce46ed73faaae3a7edcc1e1f8813ef3ea29c5a4c0018f8a2a1 +size 149671 diff --git a/Unreal/Plugins/AvatarCore_Manager/Source/AvatarCore_Manager/AvatarCore_Manager.Build.cs b/Unreal/Plugins/AvatarCore_Manager/Source/AvatarCore_Manager/AvatarCore_Manager.Build.cs index 055f7d3..e785ee8 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Source/AvatarCore_Manager/AvatarCore_Manager.Build.cs +++ b/Unreal/Plugins/AvatarCore_Manager/Source/AvatarCore_Manager/AvatarCore_Manager.Build.cs @@ -25,7 +25,8 @@ public class AvatarCore_Manager : ModuleRules PublicDependencyModuleNames.AddRange( new string[] { - "Core" + "Core", + "GameplayTags" // ... add other public dependencies that you statically link with here ... } ); @@ -40,7 +41,8 @@ public class AvatarCore_Manager : ModuleRules "SlateCore", "AvatarCore_TTS", "AvatarCore_STT", - "AvatarCore_AI" + "AvatarCore_AI", + "DeveloperSettings" // ... add private dependencies that you statically link with here ... } ); diff --git a/Unreal/Plugins/AvatarCore_Manager/Source/AvatarCore_Manager/Private/AvatarCoreSettings.cpp b/Unreal/Plugins/AvatarCore_Manager/Source/AvatarCore_Manager/Private/AvatarCoreSettings.cpp new file mode 100644 index 0000000..e37dee1 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_Manager/Source/AvatarCore_Manager/Private/AvatarCoreSettings.cpp @@ -0,0 +1,9 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "AvatarCoreSettings.h" + +UAvatarCoreSettings::UAvatarCoreSettings(const FObjectInitializer& ObjectInitializer) +{ + +} \ No newline at end of file diff --git a/Unreal/Plugins/AvatarCore_Manager/Source/AvatarCore_Manager/Private/AvatarCore_Manager.cpp b/Unreal/Plugins/AvatarCore_Manager/Source/AvatarCore_Manager/Private/AvatarCore_Manager.cpp index 2431374..c7f12f3 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Source/AvatarCore_Manager/Private/AvatarCore_Manager.cpp +++ b/Unreal/Plugins/AvatarCore_Manager/Source/AvatarCore_Manager/Private/AvatarCore_Manager.cpp @@ -1,5 +1,7 @@ // Copyright Epic Games, Inc. All Rights Reserved. +#include "AvatarCoreSettings.h" +#include "Developer/Settings/Public/ISettingsModule.h" #include "AvatarCore_Manager.h" #define LOCTEXT_NAMESPACE "FAvatarCore_ManagerModule" @@ -7,6 +9,15 @@ void FAvatarCore_ManagerModule::StartupModule() { // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module + ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings"); + if (SettingsModule) + { + SettingsModule->RegisterSettings("Project", "Plugins", "Avatar Core Manager", + LOCTEXT("RuntimeSettingsName", "AvatarCoreManager"), + LOCTEXT("RuntimeSettingsDescription", "Configure the Avatar Core Manager plug-in"), + GetMutableDefault() + ); + } } void FAvatarCore_ManagerModule::ShutdownModule() diff --git a/Unreal/Plugins/AvatarCore_Manager/Source/AvatarCore_Manager/Public/AvatarCoreSettings.h b/Unreal/Plugins/AvatarCore_Manager/Source/AvatarCore_Manager/Public/AvatarCoreSettings.h new file mode 100644 index 0000000..cdacb0b --- /dev/null +++ b/Unreal/Plugins/AvatarCore_Manager/Source/AvatarCore_Manager/Public/AvatarCoreSettings.h @@ -0,0 +1,23 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/NoExportTypes.h" +#include "GameplayTagContainer.h" +#include "AvatarCoreSettings.generated.h" + +/** + * + */ +UCLASS(config = Engine, defaultconfig) +class AVATARCORE_MANAGER_API UAvatarCoreSettings : public UDeveloperSettings +{ + GENERATED_BODY() + +public: + explicit UAvatarCoreSettings(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(Config, EditAnywhere, Category = "AvatarCoreSettings", meta = (ConfigRestartRequired = true, ToolTip = "All available states of this project.")) + TArray States; +}; diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_Body.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_Body.uasset new file mode 100644 index 0000000..5e8954d --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_Body.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21a66a566cacefda717f146e1ed497fa9149f73147fc3e9f67f9a077c71b734a +size 700378 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_BodyRetarget.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_BodyRetarget.uasset new file mode 100644 index 0000000..7d97c46 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_BodyRetarget.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3281c6acce6ac41b0b30ab81a3a2a28d5655fa4a373ea372170c1e736af53844 +size 345782 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_Face.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_Face.uasset new file mode 100644 index 0000000..9260203 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_Face.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f9e2e61edbb172e00ade2d9ce4d07c05ec69c7f49d42fd6e7971510ce0d68b2a +size 1835898 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/Curves/CF_AvatarCore_Animation_LookAt_Macro_Strength.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/Curves/CF_AvatarCore_Animation_LookAt_Macro_Strength.uasset new file mode 100644 index 0000000..8bd16d9 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/Curves/CF_AvatarCore_Animation_LookAt_Macro_Strength.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:657b6ae889630ff292c47fe61ed94ee9caa208e54d3f941abcce5fb812ee683a +size 4692 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/Curves/CF_AvatarCore_Animation_LookAt_Micro_Timer.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/Curves/CF_AvatarCore_Animation_LookAt_Micro_Timer.uasset new file mode 100644 index 0000000..bf755b1 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/Curves/CF_AvatarCore_Animation_LookAt_Micro_Timer.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb63239e1196708729d8f5f6dfd30dfabbed0fb28a7f75b050889b652647da5f +size 5039 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/Curves/CF_AvatarCore_Animation_PupilConstriction.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/Curves/CF_AvatarCore_Animation_PupilConstriction.uasset new file mode 100644 index 0000000..0660108 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/Curves/CF_AvatarCore_Animation_PupilConstriction.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2eb3e5f47aa7f346f69191390238c1f3d7090f685b06b4b94e87c7636b4397b9 +size 5011 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/Interface/BI_AvatarAnimBP.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/Interface/BI_AvatarAnimBP.uasset new file mode 100644 index 0000000..239a024 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/Interface/BI_AvatarAnimBP.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:769fbb64c8d73a9ccfd365cf0892e9d4a3ab38a1c8878d6ecea24ccfdeafbc3a +size 56211 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimationConfigs/AnimationConfig.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimationConfigs/AnimationConfig.uasset index 5c4f83f..39ae5bc 100644 Binary files a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimationConfigs/AnimationConfig.uasset and b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimationConfigs/AnimationConfig.uasset differ diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimationConfigs/AnimationConfig_Base.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimationConfigs/AnimationConfig_Base.uasset index 3524552..aba4dd6 100644 --- a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimationConfigs/AnimationConfig_Base.uasset +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimationConfigs/AnimationConfig_Base.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9dc7cdbe0987d6538c91d6e4bb1c7553c5aa273943d1e940a01d569e7a2c2a9 -size 13700 +oid sha256:f6c37c719f4df19c40c1ed5f9410c2e756f4f2afd6d8ef392dad4cae1b6f97c0 +size 21031 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/DefaultIdle/AS_M_Idle_Breath_2_Looped.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/DefaultIdle/AS_M_Idle_Breath_2_Looped.uasset new file mode 100644 index 0000000..9a75da9 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/DefaultIdle/AS_M_Idle_Breath_2_Looped.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:215c14020443ec67587fc592c368d1dbb518e196be8812c2b04f0cdbf9da65b9 +size 50017056 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/DefaultIdle/M_Idle_Breath_2_Looped.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/DefaultIdle/M_Idle_Breath_2_Looped.uasset new file mode 100644 index 0000000..c01a303 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/DefaultIdle/M_Idle_Breath_2_Looped.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:73e5bb498654ab884612e1d7e255c623a603e5f9ed7c34443d6bbd31d550801c +size 4286425 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_02_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_02_adjusted.uasset new file mode 100644 index 0000000..415c674 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_02_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b3323d450e4356206dfc7ca052406cf0e30306364b3b64013e420d27cec8b8f +size 48237449 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_03_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_03_adjusted.uasset new file mode 100644 index 0000000..c4f01e0 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_03_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5dd31e4bb1c060a363b36cb38417e720d0771c92971caa4dae3caa5a3defb2d +size 40872699 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_04_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_04_adjusted.uasset new file mode 100644 index 0000000..09e6571 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_04_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50b5d09cd33808fbc317d5fd8b5479ad46ead71bf12ef45dcd160be97ba7791a +size 28351753 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_06_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_06_adjusted.uasset new file mode 100644 index 0000000..32f8b01 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_06_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa57d8c3a1b731998359ba4530eb216a91ca21a04bbfadf5b0be8c0079c5b6d2 +size 41138893 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_09_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_09_adjusted.uasset new file mode 100644 index 0000000..765f67a --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_09_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ebd862d8754699fc858fb2ef7d24591985b3e30c728342efe20fbab0739b2e99 +size 23172030 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_16.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_16.uasset new file mode 100644 index 0000000..a6be0db --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_16.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e33b9d4fd4407c6925f049e524cd6a5da63c9c8a4c135b4d84f790144ede4971 +size 1697034 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_23_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_23_adjusted.uasset new file mode 100644 index 0000000..24b1a4e --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_23_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:795aa34469e7ee341d7fc83cdf098c5bb0fe95699dbb091570d7caa8f4e79866 +size 19991660 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_25_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_25_adjusted.uasset new file mode 100644 index 0000000..8588996 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/AS_LL_JG_01_Idle_25_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b3927b42979503bc5641306db2d346adccde3a8d52a7b6e2c77f45be064d8185 +size 38115377 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/M_Idle_Head_eyes_1.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/M_Idle_Head_eyes_1.uasset new file mode 100644 index 0000000..f1d99d4 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/M_Idle_Head_eyes_1.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f9a80faa939b3e1f37eebecb3d4320694f8bf17121f8f771b3e14bcb2708385d +size 151340598 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/M_Idle_breath_2.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/M_Idle_breath_2.uasset new file mode 100644 index 0000000..42561fd --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/M_Idle_breath_2.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb19977c801ae184dd9ae6918a4e45f0887947b8d000f0a257abfd8ceb68b9c4 +size 81736140 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/M_Idle_cross_arms_long.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/M_Idle_cross_arms_long.uasset new file mode 100644 index 0000000..5d09e13 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/M_Idle_cross_arms_long.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:117bacbf2097b2ce186d1cdeac7a4bc73a17f2c3a8f693b7041e51ced6b72818 +size 8662872 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/M_Listening_Smiling_02.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/M_Listening_Smiling_02.uasset new file mode 100644 index 0000000..3afedca --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Idle/M_Listening_Smiling_02.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ce15ebaa1552b5f6db093fc95422f3065e86eb8b88b0107422fc237792c5dd01 +size 49414624 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_02_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_02_adjusted.uasset new file mode 100644 index 0000000..9dd3104 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_02_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:070ca57863e86fcb672525e2d7a2743975e199e2974e8314f948dedbac013595 +size 26054487 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_03_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_03_adjusted.uasset new file mode 100644 index 0000000..faae089 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_03_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:651197fd1715f9dddc90c6526667ec7a9d51eade8631f5f79be1e4bae402e7e1 +size 28716447 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_04_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_04_adjusted.uasset new file mode 100644 index 0000000..6f9f7cf --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_04_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c1625128fbcd11e9061b6aa14a6ca56ff1c16286cb26ae1e726da24fb5fa6a6 +size 23211124 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_05_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_05_adjusted.uasset new file mode 100644 index 0000000..789aabc --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_05_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32b597fffa47f6518fbf55a1562609260c87d741a1f3f6756fc7cb54d923a07f +size 24275723 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_06_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_06_adjusted.uasset new file mode 100644 index 0000000..732d755 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_06_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b2f43bbf96fff3b6424d39cfc5f3e711661c3a32c5f1c22ca1885a11ff002d9b +size 15209209 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_09_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_09_adjusted.uasset new file mode 100644 index 0000000..027df77 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_09_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ce156f8040c0d2e670b375bb9f5386458ae7ef3e358e88dae42978a5db9fe825 +size 9364227 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_15_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_15_adjusted.uasset new file mode 100644 index 0000000..3bc3a90 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_15_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0de4fd1f8f3aa72e76a441123d21a8bf0d1ebd816f252e755dd8f926c5011fef +size 27474197 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_16_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_16_adjusted.uasset new file mode 100644 index 0000000..6f49a20 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_16_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f75abe9d0f30e43551a206bbc6e0c9912bd08b55d17a512c14b9620d5a610fef +size 23207191 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_17_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_17_adjusted.uasset new file mode 100644 index 0000000..9332dbc --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/AS_LL_JG_01_Listening_17_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18ed949b1eef6a506356f8b4fde82cad8cc108f1d6af7f2ab041244d82420fd3 +size 22856261 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/M_Listening_Nod.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/M_Listening_Nod.uasset new file mode 100644 index 0000000..913b730 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/M_Listening_Nod.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f200e78173d24a04e1585970857d7a2866bbbec7f41eb5b971078f06b89397ce +size 288942812 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/M_Listening_Smiling_02.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/M_Listening_Smiling_02.uasset new file mode 100644 index 0000000..e8618a9 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/M_Listening_Smiling_02.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4434cdadc3f3cbcb91fa994c6cf39b4e587c3700b2eb41ffaa723e739286524c +size 49414634 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/M_Listening_Surprise.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/M_Listening_Surprise.uasset new file mode 100644 index 0000000..7643845 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Listening/M_Listening_Surprise.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:86c341be67b77b5d1b3d9f980be20cfaa8817b7c7616e45b9191f128fbe7da3b +size 69896828 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_01.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_01.uasset new file mode 100644 index 0000000..f9e09d3 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_01.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:66db4338f5e90332765744a6396976bc43dbe0e2d2bfb34990d83d90c264b50a +size 4279959 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_02.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_02.uasset new file mode 100644 index 0000000..ca06b2e --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_02.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50a73eb37c3ed4489420f5e5203e2d6794c0dec995018f28ec63b4552352f2a7 +size 5040308 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_03.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_03.uasset new file mode 100644 index 0000000..9683435 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_03.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:20104f95d3cd642e251aaf34de37c5dfaeae00a3dad85e72191fc3afd0fb4d48 +size 6891991 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_04.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_04.uasset new file mode 100644 index 0000000..2aab1f9 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_04.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:906960743c1ce6e191b5266e507073b4ab22e96913c4ac5dcb8450f263b7097d +size 5519753 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_05_01_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_05_01_adjusted.uasset new file mode 100644 index 0000000..75fbee4 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_05_01_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92373329f21d3a971b1ece2845636ea94f829bfa509047013e27467479619987 +size 88786185 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_05_02_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_05_02_adjusted.uasset new file mode 100644 index 0000000..c579787 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_05_02_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4db8257c43e88b96b8af33b035a56aff31302d3dcc1b15c5617e2d2a32b7af3 +size 109341411 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_06.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_06.uasset new file mode 100644 index 0000000..422ec20 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_LL_JG_02_Talking_06.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29e07d6159c2350c9778ff1fe99d00a30b00b9dac226897ce25543d7f2161745 +size 4643529 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take01_Anim01_Cascadeur.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take01_Anim01_Cascadeur.uasset new file mode 100644 index 0000000..1e21ca6 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take01_Anim01_Cascadeur.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:998b756ced5363bc0f09ea1aed422a12151afc47f47c26567daa8fb014e91bec +size 7919559 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take01_Anim02_Cascadeur.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take01_Anim02_Cascadeur.uasset new file mode 100644 index 0000000..7987e22 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take01_Anim02_Cascadeur.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca6c7913a03971b1ac4e8aa5562ed667c94fde9276f478f82435f100c1efe6ce +size 10348225 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take01_Anim03_Cascadeur.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take01_Anim03_Cascadeur.uasset new file mode 100644 index 0000000..006b2f5 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take01_Anim03_Cascadeur.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:22013362a1449ef0e581e6f49184591baf46c032125578ec0c7a7c1bbfe5e352 +size 10175956 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take01_Anim04_Cascadeur.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take01_Anim04_Cascadeur.uasset new file mode 100644 index 0000000..610abd6 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take01_Anim04_Cascadeur.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7fd3f89ee95c2dd6f8897a87eacdba5e0d80fe547bcdfc663f256a1ba544e401 +size 8997481 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take02_Anim01_Cascadeur.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take02_Anim01_Cascadeur.uasset new file mode 100644 index 0000000..3e64cc3 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take02_Anim01_Cascadeur.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:449f05924e39f385bbc170a5f3ba65c7475dcaf163ee0e9ba71ee6250940a380 +size 10169638 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take02_Anim02_Cascadeur.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take02_Anim02_Cascadeur.uasset new file mode 100644 index 0000000..74cc5ed --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take02_Anim02_Cascadeur.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10aa90f2699bb4ac29d602339b6145d38add6b43a04576b4fed7fc843eb31487 +size 12092071 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take02_Anim03_Cascadeur.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take02_Anim03_Cascadeur.uasset new file mode 100644 index 0000000..bd6af1c --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take02_Anim03_Cascadeur.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ef30d18612e845f8479763f2567b8c985d83aaabfcce0e5fe82a1a5a6baec5d +size 11723669 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take02_Anim04_Cascadeur.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take02_Anim04_Cascadeur.uasset new file mode 100644 index 0000000..3b70569 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take02_Anim04_Cascadeur.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:998e8a92504c97addb09a731b4268b1b03c1c897b4b4d53ea7b773c81982184d +size 7950160 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take03_Anim01_Cascadeur.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take03_Anim01_Cascadeur.uasset new file mode 100644 index 0000000..f5efedd --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take03_Anim01_Cascadeur.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ec79191ec9eadbf9201990ade9701efa7d30c959ccb45214050c049369c0546 +size 9444672 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take03_Anim02_Cascadeur.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take03_Anim02_Cascadeur.uasset new file mode 100644 index 0000000..7e5469c --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take03_Anim02_Cascadeur.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ccf133c03f4840660359e372e9139bc000a97bc2dda95be51237f6e9c7c83880 +size 11745062 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take03_Anim03_Cascadeur.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take03_Anim03_Cascadeur.uasset new file mode 100644 index 0000000..fa09f2d --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Talking/AS_MC_Tim_01_Take03_Anim03_Cascadeur.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a30dd84fe55dcb624fcb5e15c9f22a864aab442523bee8912e8216f73d03f6a +size 13826878 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_01_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_01_adjusted.uasset new file mode 100644 index 0000000..5d605b6 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_01_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36766cdbdc4ce9758cc058cae244e4fb8abb9d5889dc035d97428deffeb7af8c +size 19485050 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_02_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_02_adjusted.uasset new file mode 100644 index 0000000..a3c0502 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_02_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:62fc1f2e3c9441c6c2e83269e18ccb6eb5b80454395e3a1eec756712f5d00e50 +size 17167034 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_03_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_03_adjusted.uasset new file mode 100644 index 0000000..8ddd22a --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_03_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b184c84cf34adf2a8d5a05626bb393b9e8cd7d6466bd7cb2d62e79dc0acc1bde +size 15396519 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_04_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_04_adjusted.uasset new file mode 100644 index 0000000..fea3b24 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_04_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e45094ee1f622ce57f8464e99df4f16af65ece0ffc794f0dccadb1fca661f4d +size 14738047 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_05_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_05_adjusted.uasset new file mode 100644 index 0000000..0783dd3 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_05_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f1e88834cf72a30adcc73fba03d39fa9d88d54d8bab7256e2a8eb25d274d22e9 +size 26895713 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_06_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_06_adjusted.uasset new file mode 100644 index 0000000..9838965 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_06_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1fc7e8edda67bd1a18ff306b0653bb05ce73a7c5dee68e4a313c0fee885590a4 +size 15106862 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_07_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_07_adjusted.uasset new file mode 100644 index 0000000..562896e --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_07_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c5c569bbb2c5305ec13211900bc7f27395d2768156dd7e429ea0ae55c0dce8a +size 13393606 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_08_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_08_adjusted.uasset new file mode 100644 index 0000000..f02ed18 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_08_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d9d15519e82e17459cef0894082794e6f0d51bba9f30bcdba1912a287565d39a +size 17181271 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_09_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_09_adjusted.uasset new file mode 100644 index 0000000..b2bbad4 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_09_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b04d9d1e0bc8591404a2556e43a4c7f10515aa0ed5c80ddf85fb953f85d347b +size 15022550 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_10_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_10_adjusted.uasset new file mode 100644 index 0000000..3701814 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_10_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f6f543349b2d61fa14f5dcc7897aed013c766380ff9c957996d819facea7019c +size 13984654 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_11_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_11_adjusted.uasset new file mode 100644 index 0000000..21d40d6 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_11_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd2501bed8b6013e656b298ae791576f5b35bdfe1416dcbf30f18ddbd8069a99 +size 21447333 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_12_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_12_adjusted.uasset new file mode 100644 index 0000000..2cb4a1e --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/AS_LL_JG_01_Thinking_12_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed3c51d3de885a30dec24a5393aa97c06aae824fbd47eaad92ec93e4cf4386b2 +size 17710723 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/M_Thinking_Standing.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/M_Thinking_Standing.uasset new file mode 100644 index 0000000..d8b3677 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/M_Thinking_Standing.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:99874934b852f59a9de1680308a06b52dd2140d1757641f53854221bc3ce122d +size 6770837 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/M_Thinking_hand.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/M_Thinking_hand.uasset new file mode 100644 index 0000000..d4c6c84 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/M_Thinking_hand.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b9e66047f44edd4d86e74ad268a28a609b241aa479e4156a7662c9262162287 +size 92800611 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/M_Thinking_headup.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/M_Thinking_headup.uasset new file mode 100644 index 0000000..2f6fb8a --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/Thinking/M_Thinking_headup.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04097fda6a3b961573ef9696036d5466c45c1ea159314c55a783f26e4439012a +size 40328776 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_01_Wink.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_01_Wink.uasset new file mode 100644 index 0000000..a7dc574 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_01_Wink.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d05c6c6e034cab3390b9b70d1b461c52869d198490a2d06f3741c3f746e5e38 +size 1542515 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_02_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_02_adjusted.uasset new file mode 100644 index 0000000..39c7914 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_02_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0ed86254f85d8e572b61bdbb345f171dd3e5a3f25c357f4db9f6cea527092963 +size 48237457 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_03_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_03_adjusted.uasset new file mode 100644 index 0000000..6aed4b9 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_03_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4832d78ad285bdcd409a8279fa5f5de0bf993d90eff17211347ca59d046644e +size 40872706 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_04_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_04_adjusted.uasset new file mode 100644 index 0000000..ffcbf33 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_04_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c2fbac071d566bd69ef82f12c7fb3288398f71897a5431d5adbe49a6e6d2671 +size 28351765 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_06_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_06_adjusted.uasset new file mode 100644 index 0000000..30d55bc --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_06_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:38f53aa1c618fd7c6686250ed463f5f7822b95fade701280e955a9404efaa9dc +size 41138902 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_07_Eyebrows.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_07_Eyebrows.uasset new file mode 100644 index 0000000..ae866f1 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_07_Eyebrows.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:857c4db661a8a39d27814f69d5527a6aa5ffc48091dca9f3bc40650c21a198b3 +size 1836424 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_08.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_08.uasset new file mode 100644 index 0000000..d864a0f --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_08.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b57ca20233e39f5a96852b7297d5d66a9b8f1ebc3cb2a5023019220a243b3ec1 +size 2625374 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_09_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_09_adjusted.uasset new file mode 100644 index 0000000..4ffc728 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_09_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f60ddd3c58b46860f50e6b84063fd0587b1e021d8bfd3ae048f3636df7ac2ff6 +size 23172041 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_10.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_10.uasset new file mode 100644 index 0000000..4c75df5 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_10.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9060b046b3c0a61b57877bcc71d238a0404661a9779e07b466cc2edc161a7e7b +size 1898259 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_11.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_11.uasset new file mode 100644 index 0000000..a514a50 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_11.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e224ae774cdab54379bc65c28015145260a08eb84b1b11e00eb4a5914dbeb4c +size 1867400 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_13.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_13.uasset new file mode 100644 index 0000000..5a2e59c --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_13.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36327a1dcaeaedf0f7bb38c4eae356cbc368ac143930aee6fd6437abde000989 +size 3120471 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_14.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_14.uasset new file mode 100644 index 0000000..917f47b --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_14.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9fa502d6e1daacb57e9cefb175d1ddbad06b066e09032cca85864e0da11bcfa7 +size 1697162 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_15.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_15.uasset new file mode 100644 index 0000000..02dc8f1 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_15.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e32d6ebf674affc4ccc2ef81b8a9db9e5b020192f6e9d993026d3d3b44e6c265 +size 1944727 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_17.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_17.uasset new file mode 100644 index 0000000..2c23d3c --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_17.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d3a2f64a794cd9cc325d8f017f56a8fc6dd91ebb9596d303784541b9decf843 +size 2625533 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_18_adjusted.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_18_adjusted.uasset new file mode 100644 index 0000000..eaf35d1 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_18_adjusted.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:251cd4f49e9bad8e130d5dd11495d54c2f017114c0075878373141cf9b87fb46 +size 25655514 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_20.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_20.uasset new file mode 100644 index 0000000..9659639 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_20.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ebc1b09899cc710073a05df5461f078fa209c939d1bffff1c2b70dcfa90f651c +size 2083946 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_23.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_23.uasset new file mode 100644 index 0000000..7124b05 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_23.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18329ebb46d4b9064b7b499b7842c52d8b6a29679d7301e0cc139e623630f9b1 +size 1926551 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_25.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_25.uasset new file mode 100644 index 0000000..865a64b --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_25.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e9dab8ce5b231554b0412cc3eab3ff651d11d62757b663ba8b375887d3d8754 +size 3553650 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_26.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_26.uasset new file mode 100644 index 0000000..1003d4a --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_26.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2252f5e7431e891289dc0b5b593dbadc2e2c6a95e92678fc40db5112431652ac +size 3569132 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_28_01.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_28_01.uasset new file mode 100644 index 0000000..2eeb2ae --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_28_01.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9c6b38b02926a3d3f9ccd73c7dc241d037bea5476a8ab11a3baeec1d1cc874ad +size 5657647 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_28_02.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_28_02.uasset new file mode 100644 index 0000000..364fccf --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/AS_LL_JG_01_Idle_28_02.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29be6c526bd671db2b08ce243c5f8cb0b7bdfef9d042750871820f17cb8e4d29 +size 16734920 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/M_Idle_breath_2.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/M_Idle_breath_2.uasset new file mode 100644 index 0000000..5d763ee --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/M_Idle_breath_2.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dcabdcab3cf588859c817a14c499de165002d428d343f4734c7d40bc4cd91283 +size 81736148 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/M_Idle_cross_arms_fixed.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/M_Idle_cross_arms_fixed.uasset new file mode 100644 index 0000000..edae74c --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/M_Idle_cross_arms_fixed.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7d470be565f316dd50b446cb33f074e7ae10fc29a8cf213226edd43eddd2e2f2 +size 4921427 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/M_Idle_shoulders_1.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/M_Idle_shoulders_1.uasset new file mode 100644 index 0000000..c0b40a9 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/Animations/VeryIdle/M_Idle_shoulders_1.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:262c28f1bf6578a3d566373f905feb6576726f6ce88b539b738e7e55e41e4734 +size 61110950 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/ControlRig/CR_AvatarCore_Body.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/ControlRig/CR_AvatarCore_Body.uasset new file mode 100644 index 0000000..07e2f3f --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/ControlRig/CR_AvatarCore_Body.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca2c34d176e4fa972be3c990300e93f0a898ba90f275598bbf19a63b11596eda +size 2029039 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/ControlRig/CR_AvatarCore_Head.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/ControlRig/CR_AvatarCore_Head.uasset new file mode 100644 index 0000000..81315ce --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/ControlRig/CR_AvatarCore_Head.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba0b766d28e58c9eb4ae0ff4bfcf3a5391007f038e996f6917ee790a4accde44 +size 3134922 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_ArmsClosed.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_ArmsClosed.uasset index 329ae01..e30b9aa 100644 Binary files a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_ArmsClosed.uasset and b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_ArmsClosed.uasset differ diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_ArmsOpen.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_ArmsOpen.uasset index 44bf1b9..440b519 100644 Binary files a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_ArmsOpen.uasset and b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_ArmsOpen.uasset differ diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_FingerUp.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_FingerUp.uasset index 883556b..0e70783 100644 Binary files a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_FingerUp.uasset and b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_FingerUp.uasset differ diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_Forward.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_Forward.uasset index 5fed2f5..808bdfc 100644 Binary files a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_Forward.uasset and b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_Forward.uasset differ diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_HandsTouching.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_HandsTouching.uasset index f30b408..d11da0f 100644 Binary files a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_HandsTouching.uasset and b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/Body/m_med_nrw_Pose_HandsTouching.uasset differ diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/BP_DemopTarget.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/BP_DemopTarget.uasset index 3c3c0d3..2cefc2e 100644 Binary files a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/BP_DemopTarget.uasset and b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/BP_DemopTarget.uasset differ diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/EditorUtility/EUS_MetahumanToAvatar.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/EditorUtility/EUS_MetahumanToAvatar.uasset index b5eed89..769d184 100644 Binary files a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/EditorUtility/EUS_MetahumanToAvatar.uasset and b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/EditorUtility/EUS_MetahumanToAvatar.uasset differ diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/MetaHuman/AvatarBase.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/MetaHuman/AvatarBase.uasset index dc912b4..9da4cb6 100644 --- a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/MetaHuman/AvatarBase.uasset +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/MetaHuman/AvatarBase.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd820b881fee7352101b30121c01f94fb4312b63277d591c9d8467ef16b20e30 -size 2397291 +oid sha256:e3a96ba066f6b1fc3b4e283fb39e1a21059f93e8200eb7d5df3cedf7c3b95b34 +size 1759210 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/PersonalityConfig/DA_PersonalityConfig_Empty.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/PersonalityConfig/DA_PersonalityConfig_Empty.uasset index 9efc5c2..286744c 100644 Binary files a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/PersonalityConfig/DA_PersonalityConfig_Empty.uasset and b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/PersonalityConfig/DA_PersonalityConfig_Empty.uasset differ diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/E_AnimationConfig.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/E_AnimationConfig.uasset index 3270e24..0f500b0 100644 Binary files a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/E_AnimationConfig.uasset and b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/E_AnimationConfig.uasset differ diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/E_AnimationType.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/E_AnimationType.uasset index f3ca2f2..224e279 100644 Binary files a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/E_AnimationType.uasset and b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/E_AnimationType.uasset differ diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/E_LookAtSetup.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/E_LookAtSetup.uasset new file mode 100644 index 0000000..6342f11 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/E_LookAtSetup.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe9f118a9145d7465bb44b10931c1a7088c13171ba0f1f8992cb892c1e02efcc +size 3024 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/E_LookTargetType.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/E_LookTargetType.uasset index 426331d..6c81a33 100644 Binary files a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/E_LookTargetType.uasset and b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/E_LookTargetType.uasset differ diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/S_AnimAsset.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/S_AnimAsset.uasset index e564e17..5ef11b8 100644 Binary files a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/S_AnimAsset.uasset and b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/S_AnimAsset.uasset differ diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/S_AnimationTypeConfig.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/S_AnimationTypeConfig.uasset new file mode 100644 index 0000000..c90093e --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/StructsAndEnums/S_AnimationTypeConfig.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f18fb491883a573b0c281c4cc56b305c6ac57be967756ac374df5338aaf9a06c +size 5363 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Meshes/m_med_nrw_bodyWithHead_preview.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Meshes/m_med_nrw_bodyWithHead_preview.uasset new file mode 100644 index 0000000..35f6f1b --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Meshes/m_med_nrw_bodyWithHead_preview.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:40ce13e4099d227f07bb72992437e08dda6e012f60954bbf4443c37f98b5557a +size 16074424 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Meshes/m_med_nrw_body_preview.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Meshes/m_med_nrw_body_preview.uasset index 575c367..d4b7667 100644 Binary files a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Meshes/m_med_nrw_body_preview.uasset and b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Meshes/m_med_nrw_body_preview.uasset differ diff --git a/Unreal/Plugins/AvatarCore_STT/Content/Preprocessor/STTPreprocessorConverter24000.uasset b/Unreal/Plugins/AvatarCore_STT/Content/Preprocessor/STTPreprocessorConverter24000.uasset index 2f08206..c43e78d 100644 Binary files a/Unreal/Plugins/AvatarCore_STT/Content/Preprocessor/STTPreprocessorConverter24000.uasset and b/Unreal/Plugins/AvatarCore_STT/Content/Preprocessor/STTPreprocessorConverter24000.uasset differ diff --git a/Unreal/Plugins/AvatarCore_STT/Content/STTManager_Freespeech.uasset b/Unreal/Plugins/AvatarCore_STT/Content/STTManager_Freespeech.uasset index 181e11e..990a1e7 100644 Binary files a/Unreal/Plugins/AvatarCore_STT/Content/STTManager_Freespeech.uasset and b/Unreal/Plugins/AvatarCore_STT/Content/STTManager_Freespeech.uasset differ diff --git a/Unreal/Plugins/AvatarCore_STT/Content/STTManager_PTT.uasset b/Unreal/Plugins/AvatarCore_STT/Content/STTManager_PTT.uasset index b0f543e..47a6ffd 100644 Binary files a/Unreal/Plugins/AvatarCore_STT/Content/STTManager_PTT.uasset and b/Unreal/Plugins/AvatarCore_STT/Content/STTManager_PTT.uasset differ diff --git a/Unreal/Plugins/AvatarCore_STT/Content/STTManager_Realtime.uasset b/Unreal/Plugins/AvatarCore_STT/Content/STTManager_Realtime.uasset index 26649af..8f5a808 100644 Binary files a/Unreal/Plugins/AvatarCore_STT/Content/STTManager_Realtime.uasset and b/Unreal/Plugins/AvatarCore_STT/Content/STTManager_Realtime.uasset differ diff --git a/Unreal/Plugins/AvatarCore_STT/Content/STTManager_RealtimeAPI.uasset b/Unreal/Plugins/AvatarCore_STT/Content/STTManager_RealtimeAPI.uasset index 3e4346b..4cc33f6 100644 Binary files a/Unreal/Plugins/AvatarCore_STT/Content/STTManager_RealtimeAPI.uasset and b/Unreal/Plugins/AvatarCore_STT/Content/STTManager_RealtimeAPI.uasset differ diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/AvatarCore_STT.Build.cs b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/AvatarCore_STT.Build.cs index 8533c4f..46b07f7 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/AvatarCore_STT.Build.cs +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/AvatarCore_STT.Build.cs @@ -98,6 +98,7 @@ public class AvatarCore_STT : ModuleRules "HTTP", "Json", "JsonUtilities", + "WebRTC", // ... add private dependencies that you statically link with here ... } ); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/AvatarWebRTCAudioChannel.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/AvatarWebRTCAudioChannel.cpp new file mode 100644 index 0000000..b09bb6e --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/AvatarWebRTCAudioChannel.cpp @@ -0,0 +1,149 @@ +#include "AvatarWebRTCAudioChannel.h" + +FAvatarTTSAudioWebRTCChannel::FAvatarTTSAudioWebRTCChannel() +{ +} + +FAvatarTTSAudioWebRTCChannel::~FAvatarTTSAudioWebRTCChannel() +{ + AudioProcessing = nullptr; +} + +void FAvatarTTSAudioWebRTCChannel::Initialize(int32 InSampleRate, int32 InNumChannels, FWebRTCSettings WebRTCSettings) +{ + FScopeLock Lock(&CriticalSection); + SampleRate = InSampleRate; + NumChannels = InNumChannels; + EnsureConfigured(WebRTCSettings); +} + +void FAvatarTTSAudioWebRTCChannel::EnsureConfigured(FWebRTCSettings WebRTCSettings) +{ + if (AudioProcessing) + { + return; + } + + webrtc::AudioProcessing::Config Config; + Config.pipeline.multi_channel_capture = WebRTCSettings.pipeline_multi_channel_capture; + Config.pipeline.multi_channel_render = WebRTCSettings.pipeline_multi_channel_render; + Config.pipeline.maximum_internal_processing_rate = WebRTCSettings.pipeline_maximum_internal_processing_rate; + Config.echo_canceller.enabled = WebRTCSettings.echo_canceller; + Config.pre_amplifier.enabled = WebRTCSettings.pre_amplifier; + Config.high_pass_filter.enabled = WebRTCSettings.high_pass_filter; + Config.noise_suppression.enabled = WebRTCSettings.noise_suppression; + Config.noise_suppression.level = static_cast( + FMath::Clamp(WebRTCSettings.noise_suppression_level, 0, 3)); + Config.transient_suppression.enabled = WebRTCSettings.transient_suppression; + Config.gain_controller1.enabled = WebRTCSettings.gain_controller1; + Config.gain_controller2.enabled = WebRTCSettings.gain_controller2; +#if !WEBRTC_5414 + Config.voice_detection.enabled = WebRTCSettings.voice_detection; + Config.residual_echo_detector.enabled = WebRTCSettings.residual_echo_detector; + Config.level_estimation.enabled = WebRTCSettings.level_estimation; +#endif + + auto NewAp = webrtc::AudioProcessingBuilder().Create(); + if (NewAp) + { + NewAp->ApplyConfig(Config); + AudioProcessing = NewAp; + } +} + +void FAvatarTTSAudioWebRTCChannel::FeedFarEndPlayback(const int16* Samples, int32 NumSamples) +{ + if (!Samples || NumSamples <= 0) + { + return; + } + + FScopeLock Lock(&CriticalSection); + if (SampleRate <= 0 || NumChannels <= 0) + { + return; + } + + const int32 MaxBufferSamples = SampleRate * NumChannels * 3; + ReverseBuffer.Append(Samples, NumSamples); + if (ReverseBuffer.Num() > MaxBufferSamples) + { + const int32 Excess = ReverseBuffer.Num() - MaxBufferSamples; + ReverseBuffer.RemoveAt(0, Excess, EAllowShrinking::No); + } +} + +void FAvatarTTSAudioWebRTCChannel::ProcessCapture(const int16* Samples, + int32 NumSamples, + TArray& OutSamples, + int32 DelayMs) +{ + OutSamples.Reset(); + if (!Samples || NumSamples <= 0) + { + return; + } + FScopeLock Lock(&CriticalSection); + if (!AudioProcessing || SampleRate <= 0 || NumChannels <= 0) + { + return; + } + if (DelayMs > 0) + { + AudioProcessing->set_stream_delay_ms(DelayMs); + } + const int32 FrameSizePerChannel = webrtc::AudioProcessing::GetFrameSize(SampleRate); + const int32 FrameSamples = FrameSizePerChannel * NumChannels; + CaptureBuffer.Append(Samples, NumSamples); + const int32 TotalSamples = CaptureBuffer.Num(); + const int32 NumFrames = TotalSamples / FrameSamples; + if (NumFrames <= 0) + { + return; + } + webrtc::StreamConfig StreamCfg(SampleRate, NumChannels); + OutSamples.Reserve(NumFrames * FrameSamples); + int32 ConsumedSamples = 0; + for (int32 FrameIdx = 0; FrameIdx < NumFrames; ++FrameIdx) + { + int16* CaptureFrame = CaptureBuffer.GetData() + FrameIdx * FrameSamples; + // Prepare far-end (render) frame (zeros if underflow). + TArray RenderFrame; + RenderFrame.SetNumZeroed(FrameSamples); + if (ReverseBuffer.Num() >= FrameSamples) + { + FMemory::Memcpy(RenderFrame.GetData(), + ReverseBuffer.GetData(), + sizeof(int16) * FrameSamples); + const int32 Remaining = ReverseBuffer.Num() - FrameSamples; + if (Remaining > 0) + { + FMemory::Memmove(ReverseBuffer.GetData(), + ReverseBuffer.GetData() + FrameSamples, + sizeof(int16) * Remaining); + } + ReverseBuffer.SetNum(Remaining, EAllowShrinking::No); + } + // Feed reverse path (render). + AudioProcessing->ProcessReverseStream( + RenderFrame.GetData(), StreamCfg, StreamCfg, RenderFrame.GetData()); + // Process capture frame in-place. + TArray CaptureFrameBuffer; + CaptureFrameBuffer.SetNumUninitialized(FrameSamples); + FMemory::Memcpy(CaptureFrameBuffer.GetData(), + CaptureFrame, + sizeof(int16) * FrameSamples); + AudioProcessing->ProcessStream( + CaptureFrameBuffer.GetData(), StreamCfg, StreamCfg, CaptureFrameBuffer.GetData()); + OutSamples.Append(CaptureFrameBuffer); + ConsumedSamples += FrameSamples; + } + const int32 RemainingCapture = CaptureBuffer.Num() - ConsumedSamples; + if (RemainingCapture > 0) + { + FMemory::Memmove(CaptureBuffer.GetData(), + CaptureBuffer.GetData() + ConsumedSamples, + sizeof(int16) * RemainingCapture); + } + CaptureBuffer.SetNum(RemainingCapture, EAllowShrinking::No); +} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Preprocessor/STTPreprocessorSpeexDSP.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Preprocessor/STTPreprocessorSpeexDSP.cpp index a635eb9..528dd43 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Preprocessor/STTPreprocessorSpeexDSP.cpp +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Preprocessor/STTPreprocessorSpeexDSP.cpp @@ -8,11 +8,6 @@ #include "UObject/SoftObjectPath.h" #include "Kismet/GameplayStatics.h" #include "Sound/SoundBase.h" -#ifdef RECORD_REFERENCE_CAPTURE -#include "Misc/Paths.h" -#include "HAL/PlatformFilemanager.h" -#include "Misc/FileHelper.h" -#endif void USTTPreprocessorSpeexDSP::InitSTTPreprocessor(USTTManagerBase* BaseSTTManager, FSTTBaseSettings InSTTBaseSettings, bool InDebugMode) { @@ -27,14 +22,6 @@ void USTTPreprocessorSpeexDSP::InitSTTPreprocessor(USTTManagerBase* BaseSTTManag { SetSpeexDSPValue(SettingEntry.TypeName, SettingEntry.Value); } - - // If AEC was pre-enabled, ensure echo state is ready and linked - if (SpeexDSPSettings.bSpeexDSP_UseAEC) - { - EchoTailMs = InSTTBaseSettings.SpeexDSPSettings.SpeexDSP_AEC_Tail; - InitializeAEC(); - //StartAECCalibration(2, 200); - } } void USTTPreprocessorSpeexDSP::DestroySTTPreprocessor() @@ -45,73 +32,11 @@ void USTTPreprocessorSpeexDSP::DestroySTTPreprocessor() speex_preprocess_state_destroy(Preprocessor); Preprocessor = nullptr; } - ShutdownAEC(); - -#ifdef RECORD_REFERENCE_CAPTURE - // Persist captured reference to WAV if available - SaveReferenceCaptureToWav(); -#endif - - // Clear reference buffer - { - FScopeLock Lock(&ReferenceFIFOCS); - ReferenceFIFO.Reset(); - } } void USTTPreprocessorSpeexDSP::PostInitProperties() { Super::PostInitProperties(); - - if (SpeexDSPSettings.bSpeexDSP_UseAEC) - { - // Register submix buffer listener on the most relevant audio device (prefer world device in PIE) - { - FAudioDeviceHandle AudioDevice; - UWorld* World = GetWorld(); - if (World) - { - AudioDevice = World->GetAudioDevice(); - UE_LOG(LogTemp, Log, TEXT("InitializeAEC: World=%s, WorldAudioDevice=%s"), *World->GetName(), AudioDevice.IsValid() ? TEXT("Valid") : TEXT("Invalid")); - } - if (!AudioDevice.IsValid() && GEngine) - { - AudioDevice = GEngine->GetMainAudioDevice(); - UE_LOG(LogTemp, Log, TEXT("InitializeAEC: MainAudioDevice=%s"), AudioDevice.IsValid() ? TEXT("Valid") : TEXT("Invalid")); - } - if (!AudioDevice.IsValid()) - { - if (UGameEngine* GE = Cast(GEngine)) - { - AudioDevice = GE->GetActiveAudioDevice(); - UE_LOG(LogTemp, Log, TEXT("InitializeAEC: ActiveAudioDevice=%s"), AudioDevice.IsValid() ? TEXT("Valid") : TEXT("Invalid")); - } - } - if (AudioDevice && !HasAnyFlags(RF_ClassDefaultObject)) - { - if (!SharedSubmixListener.IsValid()) - { - SharedSubmixListener = MakeShared(); - SharedSubmixListener->Owner = this; - } - - USoundSubmix* TargetSubmix = nullptr; - if (ReferenceSubmix.IsValid()) - { - TargetSubmix = ReferenceSubmix.Get(); - } - if (TargetSubmix == nullptr) - { - TargetSubmix = &AudioDevice->GetMainSubmixObject(); - } - - if (TargetSubmix) - { - AudioDevice->RegisterSubmixBufferListener(SharedSubmixListener.ToSharedRef(), *TargetSubmix); - } - } - } - } } void USTTPreprocessorSpeexDSP::OnChunkReceived(TArray PCMData, FAudioInformation AudioInformation) @@ -123,50 +48,6 @@ void USTTPreprocessorSpeexDSP::OnChunkReceived(TArray PCMData, FAudioInfo return; } int vadReturnvalue = 0; - - // Temporary buffers for AEC path - int16 RefFrame[/*160*/ 160]; - int16 OutFrame[/*160*/ 160]; - - for (int32 i = 0; i < PCMData.Num(); i += FrameSizeSamples) - { - int16* FrameData = PCMData.GetData() + i; - - // During calibration, capture mic frame before AEC processing (convert to float [-1,1]) - if (bCalibratingAEC && CalibMic16k.Num() < CalibTargetSamples) - { - const float Inv32767 = 1.0f / 32767.0f; - for (int32 n = 0; n < FrameSizeSamples; ++n) - { - CalibMic16k.Add(FMath::Clamp((float)FrameData[n] * Inv32767, -1.0f, 1.0f)); - } - TryFinishAECCalibration(); - } - - if (SpeexDSPSettings.bSpeexDSP_UseAEC && EchoState != nullptr) - { - // Pull reference frame (or zeros if underflow) - bool bGotRef = PopReferenceFrame(RefFrame, FrameSizeSamples); - if (!bGotRef) - { - FMemory::Memset(RefFrame, 0, sizeof(int16) * FrameSizeSamples); - } - - // Echo cancellation: mic (near) + reference (far) => out - speex_echo_cancellation(EchoState, FrameData, RefFrame, OutFrame); - - // Preprocessor (denoise/AGC/VAD/residual echo suppression) on echo-cancelled frame - vadReturnvalue = vadReturnvalue + speex_preprocess_run(Preprocessor, OutFrame); - - // Copy processed frame back to output buffer - FMemory::Memcpy(FrameData, OutFrame, sizeof(int16) * FrameSizeSamples); - } - else - { - // AEC disabled: original path - vadReturnvalue = vadReturnvalue + speex_preprocess_run(Preprocessor, FrameData); - } - } if (vadReturnvalue > 1 || m_vad < 1) { OnChunkProcessed.ExecuteIfBound(PCMData, AudioInformation); } @@ -182,618 +63,6 @@ void USTTPreprocessorSpeexDSP::SetSpeexDSPValue(ESpeexDSPState Label, int Value) speex_preprocess_ctl(Preprocessor, static_cast(Label), &Value); } -void USTTPreprocessorSpeexDSP::SetAECDelayMs(int32 DelayMs) -{ - if (DelayMs < 0) - { - DelayMs = 0; - } - - if (!bIsInitialized) - return; - - const int32 Samples = (DelayMs * TargetSampleRate) / 1000; // 16 samples per ms at 16k - { - FScopeLock Lock(&ReferenceFIFOCS); - AECDelaySamples = Samples; - ReferenceFIFO.Reset(); - if (AECDelaySamples > 0) - { - ReferenceFIFO.AddZeroed(AECDelaySamples); - } - } - - UE_LOG(LogTemp, Log, TEXT("USTTPreprocessorSpeexDSP::SetAECDelayMs -> %d ms (%d samples at 16 kHz)"), DelayMs, AECDelaySamples); -} - -void USTTPreprocessorSpeexDSP::SetAECTailMs(int32 NewTailMs) -{ - // Clamp to reasonable range to avoid excessive CPU or too short tails - NewTailMs = FMath::Clamp(NewTailMs, 50, SpeexDSPSettings.SpeexDSP_AEC_Tail); // 50ms..1000ms - if (EchoTailMs == NewTailMs) - { - UE_LOG(LogTemp, Verbose, TEXT("SetAECTailMs: unchanged (%d ms)"), NewTailMs); - return; - } - - EchoTailMs = NewTailMs; - UE_LOG(LogTemp, Log, TEXT("SetAECTailMs: %d ms"), EchoTailMs); - - // If AEC is active, rebuild echo state with the new tail - if (SpeexDSPSettings.bSpeexDSP_UseAEC) - { - ShutdownAEC(); // unregister listener, destroy resampler/echo state, unlink preproc - InitializeAEC(); // re-register listener, create echo state with new tail, set SR, relink preproc - } -} - -void USTTPreprocessorSpeexDSP::FeedReferenceAudio(const TArray& RefPCM16) -{ - if (RefPCM16.Num() <= 0) - { - return; - } - FScopeLock Lock(&ReferenceFIFOCS); - const int32 OldSize = ReferenceFIFO.Num(); - ReferenceFIFO.AddUninitialized(RefPCM16.Num()); - FMemory::Memcpy(ReferenceFIFO.GetData() + OldSize, RefPCM16.GetData(), sizeof(int16) * RefPCM16.Num()); - -#ifdef RECORD_REFERENCE_CAPTURE - // Also append to persistent capture (no need to lock; not read elsewhere) - ReferenceCapturePCM16k.Append(RefPCM16); -#endif -} - -void USTTPreprocessorSpeexDSP::FeedReferenceAudioFloat(const TArray& RefMonoFloat16k) -{ - if (RefMonoFloat16k.Num() <= 0) - { - return; - } - - // During calibration, capture reference frames post-resample - if (bCalibratingAEC && CalibRef16k.Num() < CalibTargetSamples) - { - const int32 Room = CalibTargetSamples - CalibRef16k.Num(); - const int32 ToCopy = FMath::Min(Room, RefMonoFloat16k.Num()); - CalibRef16k.Append(RefMonoFloat16k.GetData(), ToCopy); - TryFinishAECCalibration(); - } - - // Convert float [-1,1] to PCM16 with clamping and rounding - TArray Temp; - Temp.Reserve(RefMonoFloat16k.Num()); - for (float Sample : RefMonoFloat16k) - { - float Clamped = FMath::Clamp(Sample, -1.0f, 1.0f); - int32 Scaled = FMath::RoundToInt(Clamped * 32767.0f); - Scaled = FMath::Clamp(Scaled, -32768, 32767); - Temp.Add(static_cast(Scaled)); - } - - FeedReferenceAudio(Temp); -} - -void USTTPreprocessorSpeexDSP::InitializeAEC() -{ - if (EchoState != nullptr) - { - return; // already initialized - } - - const int EchoTailSamples = (TargetSampleRate * EchoTailMs) / 1000; // e.g., 1600 for 100 ms - UE_LOG(LogTemp, Log, TEXT("InitializeAEC: FrameSize=%d, TargetSR=%d, EchoTailMs=%d, TailSamples=%d"), FrameSizeSamples, TargetSampleRate, EchoTailMs, EchoTailSamples); - - EchoState = speex_echo_state_init(FrameSizeSamples, EchoTailSamples); - if (!EchoState) - { - UE_LOG(LogTemp, Error, TEXT("InitializeAEC: speex_echo_state_init failed.")); - return; - } - - // Set echo state sample rate explicitly - { - int SR = TargetSampleRate; - speex_echo_ctl(EchoState, SPEEX_ECHO_SET_SAMPLING_RATE, &SR); - } - - // Link preprocessor to echo canceller for residual echo suppression - if (Preprocessor != nullptr) - { - speex_preprocess_ctl(Preprocessor, static_cast(ESpeexDSPState::SPEEXPREPROCESS_SET_ECHO_STATE), EchoState); - } - - // Attempt to bind to default master submix if not set - if (!ReferenceSubmix.IsValid()) - { - BindToDefaultMasterSubmix(); - } - - // Log resolved master - if (ReferenceSubmix.IsValid()) - { - UE_LOG(LogTemp, Log, TEXT("InitializeAEC: Resolved Master Submix: %s"), *ReferenceSubmix->GetName()); - } - else - { - UE_LOG(LogTemp, Warning, TEXT("InitializeAEC: No Master Submix resolved.")); - } -} - -void USTTPreprocessorSpeexDSP::ShutdownAEC() -{ - if (!SpeexDSPSettings.bSpeexDSP_UseAEC) - return; - - // Unregister listener from audio device (try world device first) - { - FAudioDeviceHandle AudioDevice; - if (UWorld* World = GetWorld()) - { - AudioDevice = World->GetAudioDevice(); - } - if (!AudioDevice.IsValid() && GEngine) - { - AudioDevice = GEngine->GetMainAudioDevice(); - } - if (!AudioDevice.IsValid()) - { - if (UGameEngine* GE = Cast(GEngine)) - { - AudioDevice = GE->GetActiveAudioDevice(); - } - } - if (AudioDevice) - { - if (SharedSubmixListener.IsValid()) - { - USoundSubmix* TargetSubmix = nullptr; - if (ReferenceSubmix.IsValid()) - { - TargetSubmix = ReferenceSubmix.Get(); - } - if (TargetSubmix == nullptr) - { - TargetSubmix = &AudioDevice->GetMainSubmixObject(); - } - if (TargetSubmix) - { - AudioDevice->UnregisterSubmixBufferListener(SharedSubmixListener.ToSharedRef(), *TargetSubmix); - } - SharedSubmixListener.Reset(); - } - UE_LOG(LogTemp, Log, TEXT("ShutdownAEC: Unregistered submix listener.")); - } - } - - // Destroy resampler if any - if (Resampler) - { - speex_resampler_destroy(Resampler); - Resampler = nullptr; - ResamplerInRate = 0; - UE_LOG(LogTemp, Log, TEXT("ShutdownAEC: Destroyed resampler.")); - } - - if (EchoState != nullptr) - { - speex_echo_state_destroy(EchoState); - EchoState = nullptr; - UE_LOG(LogTemp, Log, TEXT("ShutdownAEC: Destroyed echo state.")); - } - - // Unlink from preprocessor - if (Preprocessor != nullptr) - { - void* NullEcho = nullptr; - speex_preprocess_ctl(Preprocessor, static_cast(ESpeexDSPState::SPEEXPREPROCESS_SET_ECHO_STATE), NullEcho); - } -} - -bool USTTPreprocessorSpeexDSP::PopReferenceFrame(int16* OutBuffer, int32 NumSamples) -{ - static int32 S_RefUnderflows = 0; - FScopeLock Lock(&ReferenceFIFOCS); - if (ReferenceFIFO.Num() < NumSamples) - { - if ((++S_RefUnderflows % 100) == 1) - { - UE_LOG(LogTemp, Verbose, TEXT("PopReferenceFrame: underflow (%d samples needed, %d available). Count=%d"), NumSamples, ReferenceFIFO.Num(), S_RefUnderflows); - } - return false; - } - - FMemory::Memcpy(OutBuffer, ReferenceFIFO.GetData(), sizeof(int16) * NumSamples); - - // Pop from the front efficiently by memmove - const int32 Remaining = ReferenceFIFO.Num() - NumSamples; - if (Remaining > 0) - { - FMemory::Memmove(ReferenceFIFO.GetData(), ReferenceFIFO.GetData() + NumSamples, sizeof(int16) * Remaining); - } - ReferenceFIFO.SetNum(Remaining, EAllowShrinking::No); - return true; -} - -void USTTPreprocessorSpeexDSP::SetReferenceSubmix(USoundSubmix* InSubmix) -{ - ReferenceSubmix = InSubmix; - // Registration is global per audio device in UE5.3, so nothing else to do here. -} - -void USTTPreprocessorSpeexDSP::BindToDefaultMasterSubmix() -{ - const UAudioSettings* AudioSettings = GetDefault(); - if (!AudioSettings) - { - UE_LOG(LogTemp, Warning, TEXT("BindToDefaultMasterSubmix: No AudioSettings found.")); - return; - } - - const FSoftObjectPath MasterPath = AudioSettings->MasterSubmix; - UE_LOG(LogTemp, Log, TEXT("BindToDefaultMasterSubmix: MasterSubmix path: %s"), *MasterPath.ToString()); - if (!MasterPath.IsValid()) - { - UE_LOG(LogTemp, Warning, TEXT("BindToDefaultMasterSubmix: MasterSubmix is invalid.")); - return; - } - - UObject* LoadedObj = MasterPath.TryLoad(); - USoundSubmix* Submix = Cast(LoadedObj); - if (!Submix) - { - UE_LOG(LogTemp, Warning, TEXT("BindToDefaultMasterSubmix: Failed to load MasterSubmix from path.")); - return; - } - - ReferenceSubmix = Submix; - UE_LOG(LogTemp, Log, TEXT("BindToDefaultMasterSubmix: Loaded Master Submix: %s"), *Submix->GetName()); -} - -void USTTPreprocessorSpeexDSP::EnsureResampler(int32 InSampleRate) -{ - if (Resampler && ResamplerInRate == InSampleRate) - { - return; - } - - if (Resampler) - { - speex_resampler_destroy(Resampler); - Resampler = nullptr; - } - - int Err = 0; - // Use slightly higher quality for stability - Resampler = speex_resampler_init(1 /*channels*/, InSampleRate, TargetSampleRate, 7 /*quality*/, &Err); - if (Err != RESAMPLER_ERR_SUCCESS) - { - UE_LOG(LogTemp, Error, TEXT("EnsureResampler: init failed InRate=%d OutRate=%d Err=%d"), InSampleRate, TargetSampleRate, Err); - if (Resampler) - { - speex_resampler_destroy(Resampler); - Resampler = nullptr; - } - ResamplerInRate = 0; - return; - } - - ResamplerInRate = InSampleRate; - UE_LOG(LogTemp, Log, TEXT("EnsureResampler: init OK InRate=%d OutRate=%d"), InSampleRate, TargetSampleRate); -} - -void USTTPreprocessorSpeexDSP::OnNewSubmixBuffer(const USoundSubmix* OwningSubmix, float* AudioData, int32 NumSamples, int32 NumChannels, const int32 SampleRate, double /*AudioClock*/) -{ - if (!SpeexDSPSettings.bSpeexDSP_UseAEC) - { - return; - } - - // Identify which submix delivered this buffer - UE_LOG(LogTemp, VeryVerbose, TEXT("OnNewSubmixBuffer: Owning=%s, Samples=%d, Ch=%d, SR=%d"), OwningSubmix ? *OwningSubmix->GetName() : TEXT(""), NumSamples, NumChannels, SampleRate); - - // Optionally filter to resolved master submix only - if (ReferenceSubmix.IsValid() && OwningSubmix && OwningSubmix != ReferenceSubmix.Get()) - { - // Skip non-master submixes - return; - } - - // Compute peak and RMS over the whole buffer (lightweight once per callback) - float Peak = 0.f; - double SumSq = 0.0; - for (int32 i = 0; i < NumSamples; ++i) - { - float v = AudioData[i]; - Peak = FMath::Max(Peak, FMath::Abs(v)); - SumSq += (double)v * (double)v; - } - const float Rms = NumSamples > 0 ? FMath::Sqrt((float)(SumSq / (double)NumSamples)) : 0.f; - UE_LOG(LogTemp, Verbose, TEXT("SubmixBuffer Stats: Peak=%.6f RMS=%.6f"), Peak, Rms); - - // Downmix to mono - const int32 NumFrames = (NumChannels > 0) ? (NumSamples / NumChannels) : 0; - if (NumFrames <= 0) - { - return; - } - - DownmixBuffer.SetNumUninitialized(NumFrames); - - if (NumChannels == 1) - { - // Already mono - FMemory::Memcpy(DownmixBuffer.GetData(), AudioData, sizeof(float) * NumFrames); - } - else - { - const float* In = AudioData; - for (int32 n = 0; n < NumFrames; ++n) - { - float Sum = 0.0f; - for (int32 ch = 0; ch < NumChannels; ++ch) - { - Sum += In[n * NumChannels + ch]; - } - DownmixBuffer[n] = Sum / static_cast(NumChannels); - } - } - - // Resample to 16 kHz mono using Speex resampler - EnsureResampler(SampleRate); - if (!Resampler) - { - return; - } - - const int32 EstOutFrames = FMath::CeilToInt((double)DownmixBuffer.Num() * (double)TargetSampleRate / (double)SampleRate) + 16; - ResampleOutBuffer.SetNumUninitialized(EstOutFrames); - - spx_uint32_t InLen = (spx_uint32_t)DownmixBuffer.Num(); - spx_uint32_t OutLen = (spx_uint32_t)ResampleOutBuffer.Num(); - int Res = speex_resampler_process_float(Resampler, 0 /*channel*/, DownmixBuffer.GetData(), &InLen, ResampleOutBuffer.GetData(), &OutLen); - if (Res != RESAMPLER_ERR_SUCCESS) - { - UE_LOG(LogTemp, Warning, TEXT("OnNewSubmixBuffer: Resampler process error %d"), Res); - return; - } - - UE_LOG(LogTemp, VeryVerbose, TEXT("Resampler: InFrames=%u OutFrames=%u"), (uint32)InLen, (uint32)OutLen); - - if (OutLen > 0) - { - ResampleOutBuffer.SetNum(OutLen, EAllowShrinking::No); - FeedReferenceAudioFloat(ResampleOutBuffer); - } -} - -void USTTPreprocessorSpeexDSP::StartAECCalibration(int32 Seconds, int32 MaxLagMs) -{ - Seconds = FMath::Clamp(Seconds, 1, 5); - MaxLagMs = FMath::Clamp(MaxLagMs, 20, 500); - - CalibTargetSamples = Seconds * TargetSampleRate; // 16k per second - CalibMaxLagSamples = MaxLagMs * (TargetSampleRate / 1000); // 16 per ms - - CalibMic16k.Reset(); - CalibRef16k.Reset(); - bCalibratingAEC = true; - - // Ensure we have audible content during calibration: play configured calibration sound if available - if (UWorld* World = GetWorld()) - { - USoundBase* SoundToPlay = nullptr; - if (CalibrationSound.IsValid()) - { - SoundToPlay = CalibrationSound.Get(); - } - else if (CalibrationSound.ToSoftObjectPath().IsValid()) - { - SoundToPlay = Cast(CalibrationSound.ToSoftObjectPath().TryLoad()); - } - else - { - // Try default plugin asset path if not set in Details: /AvatarCore_STT/Miscellaneous/aec_calibration.aec_calibration - static const FSoftObjectPath DefaultCalibPath(TEXT("/AvatarCore_STT/Miscellaneous/aec_calibration.aec_calibration")); - if (DefaultCalibPath.IsValid()) - { - SoundToPlay = Cast(DefaultCalibPath.TryLoad()); - } - } - - if (SoundToPlay) - { - UGameplayStatics::PlaySound2D(World, SoundToPlay, CalibrationSoundVolume); - UE_LOG(LogTemp, Log, TEXT("StartAECCalibration: Playing calibration sound '%s' at volume %.2f"), *SoundToPlay->GetName(), CalibrationSoundVolume); - } - else - { - UE_LOG(LogTemp, Verbose, TEXT("StartAECCalibration: No calibration sound configured or found at default path.")); - } - } - - UE_LOG(LogTemp, Log, TEXT("StartAECCalibration: Seconds=%d TargetSamples=%d MaxLagMs=%d"), Seconds, CalibTargetSamples, MaxLagMs); -} - -void USTTPreprocessorSpeexDSP::TryFinishAECCalibration() -{ - if (!bCalibratingAEC) - { - return; - } - - if (CalibMic16k.Num() < CalibTargetSamples || CalibRef16k.Num() < CalibTargetSamples) - { - return; // keep collecting - } - - // Trim to equal length - const int32 N = FMath::Min(CalibMic16k.Num(), CalibRef16k.Num()); - CalibMic16k.SetNum(N, EAllowShrinking::No); - CalibRef16k.SetNum(N, EAllowShrinking::No); - - const int32 BestLagSamples = EstimateDelaySamplesNCC(CalibMic16k, CalibRef16k, CalibMaxLagSamples); - const int32 DelayMs = FMath::RoundToInt(FMath::Abs((float)BestLagSamples) / (TargetSampleRate / 1000.0f)); - - UE_LOG(LogTemp, Log, TEXT("AECCalibration: BestLag=%d samples (~%d ms). Applying SetAECDelayMs(%d) and initializing AEC."), BestLagSamples, DelayMs, DelayMs); - - SetAECDelayMs(DelayMs); - - bCalibratingAEC = false; - CalibMic16k.Reset(); - CalibRef16k.Reset(); - - // Now bring AEC online - InitializeAEC(); -} - -int32 USTTPreprocessorSpeexDSP::EstimateDelaySamplesNCC(const TArray& Mic, const TArray& Ref, int32 MaxLag) -{ - const int32 N = FMath::Min(Mic.Num(), Ref.Num()); - if (N < 4000) - { - return 0; // not enough data - } - - auto Normalize = [](const TArray& In, TArray& Out) - { - Out = In; - double sum = 0.0, sumSq = 0.0; - for (float v : Out) - { - sum += v; - sumSq += (double)v * (double)v; - } - const float mean = (float)(sum / FMath::Max(1, Out.Num())); - for (float& v : Out) - { - v -= mean; - } - const float var = (float)FMath::Max(1e-9, (sumSq / FMath::Max(1, Out.Num())) - (double)mean * (double)mean); - const float rms = FMath::Sqrt(var); - if (rms > 1e-6f) - { - const float inv = 1.0f / rms; - for (float& v : Out) - { - v *= inv; - } - } - }; - - TArray X, Y; - Normalize(Mic, X); - Normalize(Ref, Y); - - int32 bestLag = 0; - float bestCorr = -1.0f; - MaxLag = FMath::Min(MaxLag, N / 4); - - for (int32 lag = -MaxLag; lag <= MaxLag; ++lag) - { - double c = 0.0; - int32 count = 0; - if (lag >= 0) - { - for (int32 n = lag; n < N; ++n) - { - c += (double)X[n] * (double)Y[n - lag]; - ++count; - } - } - else - { - const int32 k = -lag; - for (int32 n = k; n < N; ++n) - { - c += (double)X[n - k] * (double)Y[n]; - ++count; - } - } - if (count > 0) - { - const float nc = (float)(c / (double)count); - if (nc > bestCorr) - { - bestCorr = nc; - bestLag = lag; - } - } - } - - UE_LOG(LogTemp, Log, TEXT("EstimateDelaySamplesNCC: bestLag=%d samples, bestCorr=%.3f"), bestLag, bestCorr); - return bestLag; -} - -#ifdef RECORD_REFERENCE_CAPTURE - -static void WriteUInt16LE_TArray(TArray& A, uint16 V) -{ - A.Add((uint8)(V & 0xFF)); - A.Add((uint8)((V >> 8) & 0xFF)); -} - -static void WriteUInt32LE_TArray(TArray& A, uint32 V) -{ - A.Add((uint8)(V & 0xFF)); - A.Add((uint8)((V >> 8) & 0xFF)); - A.Add((uint8)((V >> 16) & 0xFF)); - A.Add((uint8)((V >> 24) & 0xFF)); -} - -void USTTPreprocessorSpeexDSP::SaveReferenceCaptureToWav() -{ - if (ReferenceCapturePCM16k.Num() == 0) - { - UE_LOG(LogTemp, Verbose, TEXT("SaveReferenceCaptureToWav: no samples to save.")); - return; - } - - const uint16 NumChannels = 1; - const uint32 SampleRate = TargetSampleRate; // 16000 - const uint16 BitsPerSample = 16; - const uint32 ByteRate = SampleRate * NumChannels * (BitsPerSample / 8); - const uint16 BlockAlign = NumChannels * (BitsPerSample / 8); - const uint32 DataSize = ReferenceCapturePCM16k.Num() * sizeof(int16); - const uint32 ChunkSize = 36u + DataSize; - - TArray Bytes; - Bytes.Reserve(44 + DataSize); - - // RIFF header - Bytes.Append((const uint8*)"RIFF", 4); - WriteUInt32LE_TArray(Bytes, ChunkSize); - Bytes.Append((const uint8*)"WAVE", 4); - - // fmt chunk - Bytes.Append((const uint8*)"fmt ", 4); - WriteUInt32LE_TArray(Bytes, 16u); // PCM fmt chunk size - WriteUInt16LE_TArray(Bytes, 1u); // AudioFormat = PCM - WriteUInt16LE_TArray(Bytes, NumChannels); - WriteUInt32LE_TArray(Bytes, SampleRate); - WriteUInt32LE_TArray(Bytes, ByteRate); - WriteUInt16LE_TArray(Bytes, BlockAlign); - WriteUInt16LE_TArray(Bytes, BitsPerSample); - - // data chunk - Bytes.Append((const uint8*)"data", 4); - WriteUInt32LE_TArray(Bytes, DataSize); - - // PCM data - Bytes.Append(reinterpret_cast(ReferenceCapturePCM16k.GetData()), DataSize); - - const FString SavePath = FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("reference.wav")); - if (FFileHelper::SaveArrayToFile(Bytes, *SavePath)) - { - UE_LOG(LogTemp, Log, TEXT("Saved AEC reference capture: %s (%u samples)"), *SavePath, ReferenceCapturePCM16k.Num()); - } - else - { - UE_LOG(LogTemp, Warning, TEXT("Failed to save AEC reference capture: %s"), *SavePath); - } -} -#endif - FSpeexDSPSettings USTTPreprocessorSpeexDSP::GetSpeexDSPDefault() { FSpeexDSPSettings Result; diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Preprocessor/STTPreprocessorVAD.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Preprocessor/STTPreprocessorVAD.cpp index e5af28b..3d445ea 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Preprocessor/STTPreprocessorVAD.cpp +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Preprocessor/STTPreprocessorVAD.cpp @@ -8,13 +8,6 @@ void USTTPreprocessorVAD::InitSTTPreprocessor(USTTManagerBase* BaseSTTManager, F USTTPreprocessorBase::InitSTTPreprocessor(BaseSTTManager, InSTTBaseSettings, InDebugMode); talkingState = BaseSTTManager->GetCurrentSpeechState(); BaseSTTManager->OnSpeechStateChanged.AddUniqueDynamic(this, &USTTPreprocessorVAD::OnUserSpeechStateChanged); - - UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "How aggressive is the VAD Module (quality, normal, aggressive, very aggresive)", Category = "STT|SpeexDSP")) - uint8 VAD_Mode = 2; - UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "How many ms threshold until considered speaking?", Category = "STT|SpeexDSP")) - int32 VAD_SilenceThreshold = 300; - UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "How long to record to consider it, user wants to interrupt avatar", Category = "STT|SpeexDSP")) - float VAD_SpeechWhileBlocked = 0.5f; } void USTTPreprocessorVAD::DestroySTTPreprocessor() @@ -36,16 +29,62 @@ void USTTPreprocessorVAD::OnChunkReceived(TArray PCMData, FAudioInformati fvad_set_mode(fvad, (STTBaseSettings.VADSettings.VAD_Mode)); } + const int32 NumChannels = FMath::Max(1, AudioInformation.NumChannels); + const int32 NumFrames = PCMData.Num() / NumChannels; + if (NumFrames <= 0) + return; + + const int16* VADDataPtr = PCMData.GetData(); + TArray FirstChannel; + if (NumChannels > 1) + { + FirstChannel.SetNumUninitialized(NumFrames); + for (int32 FrameIndex = 0; FrameIndex < NumFrames; ++FrameIndex) + { + FirstChannel[FrameIndex] = PCMData[FrameIndex * NumChannels]; + } + VADDataPtr = FirstChannel.GetData(); + } + + int32 currentVADState = fvad_process(fvad, VADDataPtr, NumFrames); + + bool isLoadEnough = true; + if (currentVADState == 1) + { + double SumSquares = 0.0; + for (int32 SampleIndex = 0; SampleIndex < NumFrames; ++SampleIndex) + { + const float Normalized = static_cast(VADDataPtr[SampleIndex]) / 32768.0f; + SumSquares += static_cast(Normalized) * static_cast(Normalized); + } + + const float MeanSquares = static_cast(SumSquares / static_cast(NumFrames)); + const float Rms = FMath::Sqrt(FMath::Max(MeanSquares, 1.e-12f)); + const float Dbfs = 20.0f * FMath::LogX(10.0f, Rms); + //UE_LOG(LogTemp, Warning, TEXT("Dbfs %f"), Dbfs); + isLoadEnough = (Dbfs < static_cast(STTBaseSettings.VADSettings.VAD_MinSpeechAmplitude)); + } + + if(currentVADState != lastVADState || !isLoadEnough) + { + lastStateChangedTime = std::chrono::high_resolution_clock::now(); + Buffer.Empty(); + timeInStateInSeconds = 0.0f; + } + else + { + timeInStateInSeconds += 0.03f; //Too lazy to calulate this and it's 30ms always anyway... + } - switch (fvad_process(fvad, PCMData.GetData(), PCMData.Num())){ + lastVADState = currentVADState; + + switch (currentVADState){ case -1: STTManager->OnSTTError.Broadcast(FString::Printf(TEXT("Invalid frame length %i entries in buffer"), PCMData.Num())); break; - case 0: - SpeechWhileBlockedSeconds = 0.0f; + case 0: if (talkingState == ESTTTalkingState::TALKING) { - std::chrono::duration duration = std::chrono::high_resolution_clock::now() - lastTalkingTime; - if (duration.count() * 1000.0f > STTBaseSettings.VADSettings.VAD_SpeechWhileBlocked) { // Get the duration in seconds as a float + if (timeInStateInSeconds > STTBaseSettings.FreespeechPostRollTime) { STTManager->UserSpeechStateChanged(ESTTTalkingState::SILENCE); } else { @@ -54,23 +93,51 @@ void USTTPreprocessorVAD::OnChunkReceived(TArray PCMData, FAudioInformati } break; case 1: - if (talkingState == ESTTTalkingState::BLOCKED) { - SpeechWhileBlockedSeconds += 0.03f; //Too lazy to calulate this and it's 30ms always anyway... - if(SpeechWhileBlockedSeconds>0.5f) + if (talkingState == ESTTTalkingState::BLOCKED) { + if(timeInStateInSeconds > STTBaseSettings.VADSettings.VAD_SpeechWhileBlocked) + { STTManager->OnSpeechDetectedWhileBlocked.Broadcast(); + if (STTBaseSettings.bCanInterrupt) + { + STTManager->SetBlocked(false); + if (Buffer.Num() > 0) + { + Buffer.Append(PCMData); + OnChunkProcessed.ExecuteIfBound(Buffer, AudioInformation); + Buffer.Empty(); + } + } + } + else + Buffer.Append(PCMData); //Buffer Data return; } - lastTalkingTime = std::chrono::high_resolution_clock::now(); if (talkingState == ESTTTalkingState::SILENCE) { - STTManager->UserSpeechStateChanged(ESTTTalkingState::TALKING); + if (timeInStateInSeconds > STTBaseSettings.VADSettings.VAD_MinSpeechTime) + { + STTManager->UserSpeechStateChanged(ESTTTalkingState::TALKING); + if(Buffer.Num() > 0) + { + Buffer.Append(PCMData); + OnChunkProcessed.ExecuteIfBound(Buffer, AudioInformation); + Buffer.Empty(); + } + } + else + Buffer.Append(PCMData); //Buffer Data } - OnChunkProcessed.ExecuteIfBound(PCMData, AudioInformation); + else + OnChunkProcessed.ExecuteIfBound(PCMData, AudioInformation); break; } } void USTTPreprocessorVAD::OnUserSpeechStateChanged(ESTTTalkingState NewSpeechState) { - SpeechWhileBlockedSeconds = 0; + timeInStateInSeconds = 0; talkingState = NewSpeechState; + if (NewSpeechState == ESTTTalkingState::BLOCKED) + { + Buffer.Empty(); + } } diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Preprocessor/STTPreprocessorWebRTC.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Preprocessor/STTPreprocessorWebRTC.cpp new file mode 100644 index 0000000..f599fa0 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Preprocessor/STTPreprocessorWebRTC.cpp @@ -0,0 +1,273 @@ +#include "Preprocessor/STTPreprocessorWebRTC.h" +#include "Engine/GameEngine.h" +#include "Engine/Engine.h" +#include "AudioDevice.h" + +namespace +{ + // WebRTC APM int16 interfaces require one of: 8000, 16000, 32000, 48000 + static const int32 WebRTCProcessingSampleRate = 48000; + // Simple linear resampler for int16 PCM (mono or interleaved, treated as 1D stream) + void ResampleInt16Linear(const TArray& InSamples, + int32 InRate, + int32 OutRate, + TArray& OutSamples) + { + if (InSamples.Num() == 0 || InRate <= 0 || OutRate <= 0) + { + OutSamples.Reset(); + return; + } + if (InRate == OutRate) + { + OutSamples = InSamples; + return; + } + const float Ratio = static_cast(OutRate) / static_cast(InRate); + const int32 NewCount = FMath::CeilToInt(InSamples.Num() * Ratio); + OutSamples.SetNumUninitialized(NewCount); + for (int32 i = 0; i < NewCount; ++i) + { + const float SourceIndex = static_cast(i) / Ratio; + const int32 Index1 = FMath::Clamp(FMath::FloorToInt(SourceIndex), 0, InSamples.Num() - 1); + const int32 Index2 = FMath::Clamp(Index1 + 1, 0, InSamples.Num() - 1); + const float Fraction = SourceIndex - static_cast(Index1); + const float Sample1 = static_cast(InSamples[Index1]); + const float Sample2 = static_cast(InSamples[Index2]); + const float Interpolated = FMath::Lerp(Sample1, Sample2, Fraction); + const int32 IntSample = FMath::Clamp(FMath::RoundToInt(Interpolated), -32768, 32767); + OutSamples[i] = static_cast(IntSample); + } + } +} + +void USTTPreprocessorWebRTC::InitSTTPreprocessor(USTTManagerBase* BaseSTTManager, + FSTTBaseSettings InSTTBaseSettings, + bool InDebugMode) +{ + USTTPreprocessorBase::InitSTTPreprocessor(BaseSTTManager, InSTTBaseSettings, InDebugMode); + WebRTCSettings = InSTTBaseSettings.WebRTCSettings; + bInitialized = true; +} + +void USTTPreprocessorWebRTC::DestroySTTPreprocessor() +{ + FAudioDeviceHandle AudioDevice; + if (UWorld* World = GetWorld()) + { + AudioDevice = World->GetAudioDevice(); + } + if (!AudioDevice.IsValid() && GEngine) + { + AudioDevice = GEngine->GetMainAudioDevice(); + } + if (!AudioDevice.IsValid()) + { + if (UGameEngine* GE = Cast(GEngine)) + { + AudioDevice = GE->GetActiveAudioDevice(); + } + } + + if (AudioDevice && SharedSubmixListener.IsValid()) + { + USoundSubmix* TargetSubmix = nullptr; + if (ReferenceSubmix.IsValid()) + { + TargetSubmix = ReferenceSubmix.Get(); + } + else + { + TargetSubmix = &AudioDevice->GetMainSubmixObject(); + } + + if (TargetSubmix) + { + AudioDevice->UnregisterSubmixBufferListener( + SharedSubmixListener.ToSharedRef(), *TargetSubmix); + } + } + + SharedSubmixListener.Reset(); + bSubmixListenerRegistered = false; +} + +void USTTPreprocessorWebRTC::PostInitProperties() +{ + Super::PostInitProperties(); + + FAudioDeviceHandle AudioDevice; + UWorld* World = GetWorld(); + if (World) + { + AudioDevice = World->GetAudioDevice(); + } + if (!AudioDevice.IsValid() && GEngine) + { + AudioDevice = GEngine->GetMainAudioDevice(); + } + if (!AudioDevice.IsValid()) + { + if (UGameEngine* GE = Cast(GEngine)) + { + AudioDevice = GE->GetActiveAudioDevice(); + } + } + + if (!AudioDevice.IsValid() || HasAnyFlags(RF_ClassDefaultObject)) + { + return; + } + + if (!SharedSubmixListener.IsValid()) + { + SharedSubmixListener = MakeShared(); + SharedSubmixListener->Owner = this; + } + + USoundSubmix* TargetSubmix = nullptr; + if (ReferenceSubmix.IsValid()) + { + TargetSubmix = ReferenceSubmix.Get(); + } + else + { + TargetSubmix = &AudioDevice->GetMainSubmixObject(); + } + + if (TargetSubmix) + { + AudioDevice->RegisterSubmixBufferListener( + SharedSubmixListener.ToSharedRef(), *TargetSubmix); + bSubmixListenerRegistered = true; + } +} + +void USTTPreprocessorWebRTC::OnChunkReceived(TArray PCMData, + FAudioInformation AudioInformation) +{ + if (PCMData.Num() <= 0) + { + return; + } + // Remember the *actual* capture format once + if (CaptureSampleRate == 0 || CaptureNumChannels == 0) + { + CaptureSampleRate = AudioInformation.SampleRate; + CaptureNumChannels = AudioInformation.NumChannels; + // Initialize WebRTC APM at 48 kHz regardless of capture SR + WebRTCChannel.Initialize(WebRTCProcessingSampleRate, CaptureNumChannels, WebRTCSettings); + } + if (WebRTCChannel.GetSampleRate() <= 0 || + WebRTCChannel.GetNumChannels() <= 0) + { + return; + } + // Resample mic from its device rate (44100) to WebRTC processing rate (48000) + TArray Upsampled; + ResampleInt16Linear( + PCMData, + AudioInformation.SampleRate, + WebRTCChannel.GetSampleRate(), + Upsampled); + if (Upsampled.Num() <= 0) + { + return; + } + TArray Processed; + WebRTCChannel.ProcessCapture( + Upsampled.GetData(), + Upsampled.Num(), + Processed, + WebRTCStreamDelayMs); + if (Processed.Num() > 0) + { + // Forward as 48 kHz; downstream converter can resample to 16k or whatever is needed + AudioInformation.SampleRate = WebRTCChannel.GetSampleRate(); + OnChunkProcessed.ExecuteIfBound(Processed, AudioInformation); + } +} + +void USTTPreprocessorWebRTC::SetReferenceSubmix(USoundSubmix* InSubmix) +{ + ReferenceSubmix = InSubmix; +} + +void USTTPreprocessorWebRTC::OnNewSubmixBuffer(const USoundSubmix* OwningSubmix, + float* AudioData, + int32 NumSamples, + int32 NumChannels, + const int32 SampleRate, + double /*AudioClock*/) +{ + //UE_LOG(LogTemp, Warning, TEXT("NewSubmixBuffer with %d Hz and %d channels"), SampleRate, NumChannels); + + if (CaptureSampleRate <= 0 || CaptureNumChannels <= 0) + { + return; // capture side not initialized yet + } + + if (WebRTCChannel.GetSampleRate() <= 0) + { + return; + } + + // Only accept submix buffers that match the WebRTC processing rate (48 kHz) + if (SampleRate != WebRTCChannel.GetSampleRate()) + { + return; + } + + if (ReferenceSubmix.IsValid() && OwningSubmix && OwningSubmix != ReferenceSubmix.Get()) + { + return; + } + + const int32 NumFrames = (NumChannels > 0) ? (NumSamples / NumChannels) : 0; + if (NumFrames <= 0) + { + return; + } + + DownmixBuffer.SetNumUninitialized(NumFrames); + + if (CaptureNumChannels == 1) + { + if (NumChannels == 1) + { + FMemory::Memcpy(DownmixBuffer.GetData(), AudioData, sizeof(float) * NumFrames); + } + else + { + for (int32 Frame = 0; Frame < NumFrames; ++Frame) + { + float Sum = 0.0f; + for (int32 Ch = 0; Ch < NumChannels; ++Ch) + { + Sum += AudioData[Frame * NumChannels + Ch]; + } + DownmixBuffer[Frame] = Sum / static_cast(NumChannels); + } + } + } + else + { + for (int32 Frame = 0; Frame < NumFrames; ++Frame) + { + DownmixBuffer[Frame] = AudioData[Frame * NumChannels]; + } + } + + // Now DownmixBuffer is mono @ 48 kHz; convert to PCM16 and feed far-end + TArray RenderPCM; + RenderPCM.SetNumUninitialized(DownmixBuffer.Num()); + for (int32 i = 0; i < DownmixBuffer.Num(); ++i) + { + float Clamped = FMath::Clamp(DownmixBuffer[i], -1.0f, 1.0f); + int32 Scaled = FMath::RoundToInt(Clamped * 32767.0f); + Scaled = FMath::Clamp(Scaled, -32768, 32767); + RenderPCM[i] = static_cast(Scaled); + } + + WebRTCChannel.FeedFarEndPlayback(RenderPCM.GetData(), RenderPCM.Num()); +} \ No newline at end of file diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Azure/STTProcessorAzure.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Azure/STTProcessorAzure.cpp index 78db7d0..10231eb 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Azure/STTProcessorAzure.cpp +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Azure/STTProcessorAzure.cpp @@ -74,8 +74,9 @@ void USTTProcessorAzure::OnChunkReceived(TArray PCMData, FAudioInformatio if (!AzureRunnable) //Runable not ready { - return; + USTTProcessorAzure::StartRecognition(); } + // Get the pointer to the raw PCM data int16* rawPCMData = PCMData.GetData(); // Calculate the size of the data buffer in bytes @@ -105,11 +106,6 @@ void USTTProcessorAzure::OnSpeechStateChanged(ESTTTalkingState TalkingState) intermediateResult = ""; } } - } - else if (TalkingState == ESTTTalkingState::TALKING) { - if (AzureRunnable) - StopRecognition(true); - StartRecognition(); } } @@ -182,10 +178,10 @@ void USTTProcessorAzure::OnRunnableEnded() { bTranscriptionRunning = false; - /*if (!intermediateResult.IsEmpty()) { + if (!intermediateResult.IsEmpty()) { STTManager->OnTranscriptionReceived.Broadcast(TranscriptionCounter, *intermediateResult); intermediateResult.Empty(); - }*/ + } AzureRunnable = nullptr; } diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/STTProcessorBase.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/STTProcessorBase.cpp similarity index 95% rename from Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/STTProcessorBase.cpp rename to Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/STTProcessorBase.cpp index 259c8f5..da01c66 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/STTProcessorBase.cpp +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/STTProcessorBase.cpp @@ -1,4 +1,4 @@ -#include "STTProcessorBase.h" +#include "Processor/STTProcessorBase.h" #include "STTManagerBase.h" void USTTProcessorBase::ClearSTTProcessor() diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Whisper/STTProcessorWhisper.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Whisper/STTProcessorWhisper.cpp index cbc1069..11775fe 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Whisper/STTProcessorWhisper.cpp +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Whisper/STTProcessorWhisper.cpp @@ -204,6 +204,8 @@ void USTTProcessorWhisper::StartTranscriptionFromBuffer() return; } + STTManager->UserSpeechStateChanged(ESTTTalkingState::TRANSCRIBING); + SendWhisperRequest(MoveTemp(WavData)); } diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/STTManagerBase.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/STTManagerBase.cpp index 752daaf..607b147 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/STTManagerBase.cpp +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/STTManagerBase.cpp @@ -210,16 +210,6 @@ void USTTManagerBase::ClearSpecialWords() SpecialWords.Empty(); } -void USTTManagerBase::SetMaxTalkingTime(int NewMaxTalkingTime) -{ - MaxTalkingTime = NewMaxTalkingTime; -} - -int USTTManagerBase::GetMaxTalkingTime() -{ - return MaxTalkingTime; -} - float USTTManagerBase::GetCurrentTalkingTime() { if (CurrentSpeechState == ESTTTalkingState::TALKING) @@ -230,8 +220,8 @@ float USTTManagerBase::GetCurrentTalkingTime() float USTTManagerBase::GetRemainingTalkingTimeFactor() { - if(MaxTalkingTime > 0) - return 1.0f - (1.0f / MaxTalkingTime * GetCurrentTalkingTime()); + if(ProcessorConfig->BaseSettings.MaxTalkingTime > 0) + return 1.0f - (1.0f / ProcessorConfig->BaseSettings.MaxTalkingTime * GetCurrentTalkingTime()); else return 1; } @@ -291,12 +281,12 @@ void USTTManagerBase::UserSpeechStateChanged(ESTTTalkingState NewSpeechState) CurrentSpeechState = NewSpeechState; if (CurrentSpeechState == ESTTTalkingState::TALKING) { - if (MaxTalkingTime > 0) { + if (ProcessorConfig->BaseSettings.MaxTalkingTime > 0) { GetWorld()->GetTimerManager().SetTimer( TimeoutTimerHandle, // handle to cancel timer at a later time this, // the owning object &USTTManagerBase::InterruptListening, // function to call on elapsed - MaxTalkingTime, // float delay until elapsed + ProcessorConfig->BaseSettings.MaxTalkingTime, // float delay until elapsed false); // looping? } diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/AvatarWebRTCAudioChannel.h b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/AvatarWebRTCAudioChannel.h new file mode 100644 index 0000000..4dd443a --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/AvatarWebRTCAudioChannel.h @@ -0,0 +1,41 @@ +#pragma once + +#include "CoreMinimal.h" +#include "STTStructs.h" +#include "HAL/CriticalSection.h" + +THIRD_PARTY_INCLUDES_START +#include "modules/audio_processing/include/audio_processing.h" +#include "api/scoped_refptr.h" +THIRD_PARTY_INCLUDES_END + +class FAvatarTTSAudioWebRTCChannel +{ +public: + FAvatarTTSAudioWebRTCChannel(); + ~FAvatarTTSAudioWebRTCChannel(); + + void Initialize(int32 InSampleRate, int32 InNumChannels, FWebRTCSettings WebRTCSettings); + + void FeedFarEndPlayback(const int16* Samples, int32 NumSamples); + + void ProcessCapture(const int16* Samples, + int32 NumSamples, + TArray& OutSamples, + int32 DelayMs = 0); + + int32 GetSampleRate() const { return SampleRate; } + int32 GetNumChannels() const { return NumChannels; } + +private: + void EnsureConfigured(FWebRTCSettings WebRTCSettings); + + FCriticalSection CriticalSection; + int32 SampleRate = 0; + int32 NumChannels = 0; + + rtc::scoped_refptr AudioProcessing; + + TArray ReverseBuffer; + TArray CaptureBuffer; +}; diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Preprocessor/STTPreprocessorSpeexDSP.h b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Preprocessor/STTPreprocessorSpeexDSP.h index c2312c1..cc252d0 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Preprocessor/STTPreprocessorSpeexDSP.h +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Preprocessor/STTPreprocessorSpeexDSP.h @@ -4,21 +4,15 @@ #include "speex/speex_echo.h" #include "speex/speex_resampler.h" #include "CoreMinimal.h" -#include "Sound/SoundSubmix.h" -#include "Sound/AudioSettings.h" -#include "AudioDevice.h" -#include "ISubmixBufferListener.h" #include "Preprocessor/STTPreprocessorBase.h" #include "STTStructs.h" #include "STTPreprocessorSpeexDSP.generated.h" -//#define RECORD_REFERENCE_CAPTURE - /** * */ UCLASS() -class AVATARCORE_STT_API USTTPreprocessorSpeexDSP : public USTTPreprocessorBase, public ISubmixBufferListener +class AVATARCORE_STT_API USTTPreprocessorSpeexDSP : public USTTPreprocessorBase { GENERATED_BODY() @@ -36,53 +30,10 @@ class AVATARCORE_STT_API USTTPreprocessorSpeexDSP : public USTTPreprocessorBase, UFUNCTION(BlueprintCallable, Category = STTManager) void SetSpeexDSPValue(ESpeexDSPState Label, int Value); - // Add a configurable pre-delay (ms) to the AEC reference path to help align with mic. - // This clears the current reference FIFO and pre-fills it with zeros equal to DelayMs at 16 kHz. - UFUNCTION(BlueprintCallable, Category = STTManager) - void SetAECDelayMs(int32 DelayMs); - - // Set Speex AEC tail length in milliseconds. Reinitializes the echo state with the new tail. - UFUNCTION(BlueprintCallable, Category = STTManager) - void SetAECTailMs(int32 NewTailMs); - - // One-time auto-alignment: collect a few seconds of mic/ref at 16kHz, estimate delay, call SetAECDelayMs, then InitializeAEC. - UFUNCTION(BlueprintCallable, Category = STTManager) - void StartAECCalibration(int32 Seconds = 2, int32 MaxLagMs = 200); - - // Blueprint-friendly: feed reference audio as 16 kHz mono floats in [-1,1] - UFUNCTION(BlueprintCallable, Category = STTManager) - void FeedReferenceAudioFloat(const TArray& RefMonoFloat16k); - - // Native helper: feed reference audio as 16 kHz mono PCM16 (not exposed to Blueprints) - void FeedReferenceAudio(const TArray& RefPCM16); - - // Optional: allow projects to override which submix to bind as AEC reference - UFUNCTION(BlueprintCallable, Category = STTManager) - void SetReferenceSubmix(USoundSubmix* InSubmix); - - // ISubmixBufferListener interface - virtual void OnNewSubmixBuffer(const USoundSubmix* OwningSubmix, float* AudioData, int32 NumSamples, int32 NumChannels, const int32 SampleRate, double AudioClock) override; - UFUNCTION(BlueprintPure, Category = "STT|SpeexDSP") static FSpeexDSPSettings GetSpeexDSPDefault(); private: - - class FForwardingSubmixListener : public ISubmixBufferListener - { - public: - TWeakObjectPtr Owner; - virtual void OnNewSubmixBuffer(const USoundSubmix* OwningSubmix, float* AudioData, int32 NumSamples, int32 NumChannels, const int32 SampleRate, double AudioClock) override - { - if (Owner.IsValid()) - { - Owner.Get()->OnNewSubmixBuffer(OwningSubmix, AudioData, NumSamples, NumChannels, SampleRate, AudioClock); - } - } - }; - - TSharedPtr SharedSubmixListener; - SpeexPreprocessState* Preprocessor = nullptr; int m_vad = 0; //Manual VAD through another preprocessor // Internal processing operates on 10 ms frames at 16 kHz => 160 samples per frame @@ -95,29 +46,6 @@ private: FSpeexDSPSettings SpeexDSPSettings; - // Speex Echo Canceller state (mono) - SpeexEchoState* EchoState = nullptr; - int EchoTailMs = 250; // default tail length - - // Reference audio FIFO (thread-safe) - TArray ReferenceFIFO; - FCriticalSection ReferenceFIFOCS; - -#ifdef RECORD_REFERENCE_CAPTURE - // Persisted reference capture (PCM16 @16k mono) to be saved as WAV on destroy - TArray ReferenceCapturePCM16k; -#endif - - // Reference alignment - int32 AECDelaySamples = 0; // how many 16k mono samples of pre-delay are currently applied - - // Auto-calibration state - bool bCalibratingAEC = false; - TArray CalibMic16k; - TArray CalibRef16k; - int32 CalibTargetSamples = 0; - int32 CalibMaxLagSamples = 0; - // Optional calibration audio to ensure non-silent content during delay estimation UPROPERTY(EditAnywhere, Category = "AEC|Calibration") TSoftObjectPtr CalibrationSound; @@ -125,22 +53,6 @@ private: UPROPERTY(EditAnywhere, Category = "AEC|Calibration") float CalibrationSoundVolume = 0.5f; - // Submix binding and resampler - TWeakObjectPtr ReferenceSubmix; - SpeexResamplerState* Resampler = nullptr; // mono - int32 ResamplerInRate = 0; - TArray DownmixBuffer; // reused per callback - TArray ResampleOutBuffer; // reused per callback - - // Helpers - void InitializeAEC(); - void ShutdownAEC(); - bool PopReferenceFrame(int16* OutBuffer, int32 NumSamples); - void BindToDefaultMasterSubmix(); - void EnsureResampler(int32 InSampleRate); - void TryFinishAECCalibration(); - static int32 EstimateDelaySamplesNCC(const TArray& Mic, const TArray& Ref, int32 MaxLag); - #ifdef RECORD_REFERENCE_CAPTURE void SaveReferenceCaptureToWav(); #endif diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Preprocessor/STTPreprocessorVAD.h b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Preprocessor/STTPreprocessorVAD.h index 9ce4181..379baa1 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Preprocessor/STTPreprocessorVAD.h +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Preprocessor/STTPreprocessorVAD.h @@ -39,7 +39,9 @@ public: private: Fvad* fvad = nullptr; ESTTTalkingState talkingState; - std::chrono::high_resolution_clock::time_point lastTalkingTime; + std::chrono::high_resolution_clock::time_point lastStateChangedTime; + TArray Buffer; + int32 lastVADState = -1; - float SpeechWhileBlockedSeconds = 0; + float timeInStateInSeconds = 0; }; diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Preprocessor/STTPreprocessorWebRTC.h b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Preprocessor/STTPreprocessorWebRTC.h new file mode 100644 index 0000000..0536c54 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Preprocessor/STTPreprocessorWebRTC.h @@ -0,0 +1,67 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Sound/SoundSubmix.h" +#include "ISubmixBufferListener.h" +#include "Preprocessor/STTPreprocessorBase.h" +#include "AvatarWebRTCAudioChannel.h" +#include "STTPreprocessorWebRTC.generated.h" + + +/** + * + */ +UCLASS() +class AVATARCORE_STT_API USTTPreprocessorWebRTC : public USTTPreprocessorBase, public ISubmixBufferListener +{ + GENERATED_BODY() +public: + virtual void InitSTTPreprocessor(USTTManagerBase* BaseSTTManager, + FSTTBaseSettings InSTTBaseSettings, + bool InDebugMode = false) override; + virtual void DestroySTTPreprocessor() override; + virtual void PostInitProperties() override; + virtual void OnChunkReceived(TArray PCMData, + FAudioInformation AudioInformation) override; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "STT|WebRTC") + int32 WebRTCStreamDelayMs = 50; + UFUNCTION(BlueprintCallable, Category = "STT|WebRTC") + void SetReferenceSubmix(USoundSubmix* InSubmix); + // ISubmixBufferListener + virtual void OnNewSubmixBuffer(const USoundSubmix* OwningSubmix, + float* AudioData, + int32 NumSamples, + int32 NumChannels, + const int32 SampleRate, + double AudioClock) override; +private: + class FForwardingSubmixListener : public ISubmixBufferListener + { + public: + TWeakObjectPtr Owner; + virtual void OnNewSubmixBuffer(const USoundSubmix* OwningSubmix, + float* AudioData, + int32 NumSamples, + int32 NumChannels, + const int32 SampleRate, + double AudioClock) override + { + if (Owner.IsValid()) + { + Owner.Get()->OnNewSubmixBuffer( + OwningSubmix, AudioData, NumSamples, NumChannels, SampleRate, AudioClock); + } + } + }; + bool bInitialized = false; + bool bSubmixListenerRegistered = false; + FWebRTCSettings WebRTCSettings; + int32 CaptureSampleRate = 0; + int32 CaptureNumChannels = 0; + FAvatarTTSAudioWebRTCChannel WebRTCChannel; + TSharedPtr SharedSubmixListener; + TWeakObjectPtr ReferenceSubmix; + TArray DownmixBuffer; +}; diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTManagerBase.h b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTManagerBase.h index 5fab279..b459f74 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTManagerBase.h +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTManagerBase.h @@ -55,9 +55,6 @@ public: OnSpeechDetectedWhileBlocked.Broadcast(); } - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AvatarCoreSTT") - int MaxTalkingTime = 10.0f; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT") TSubclassOf STTRecorderClass = nullptr; @@ -115,12 +112,6 @@ public: UFUNCTION(BlueprintCallable, Category = "AvatarCoreSTT") void ClearSpecialWords(); - UFUNCTION(BlueprintCallable, Category = "AvatarCoreSTT") - void SetMaxTalkingTime(int NewMaxTalkingTime); - - UFUNCTION(BlueprintCallable, Category = "AvatarCoreSTT") - int GetMaxTalkingTime(); - UFUNCTION(BlueprintCallable, Category = "AvatarCoreSTT") float GetCurrentTalkingTime(); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTStructs.h b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTStructs.h index 6c21132..620d5ff 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTStructs.h +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTStructs.h @@ -32,6 +32,13 @@ enum ESTTTalkingState BLOCKED = 3 UMETA(DisplayName = "BLOCKED - User not allowed to talk") }; +UENUM(BlueprintType) +enum class ESTTTranscriptionType : uint8 +{ + OpenAI = 0 UMETA(DisplayName = "OpenAI Transcription"), + Azure = 1 UMETA(DisplayName = "Mircosoft Azure Congnitive Speech Services"), +}; + UENUM(BlueprintType) enum class ESpeexDSPState : uint8 { @@ -54,6 +61,84 @@ enum class ESpeexDSPState : uint8 SPEEXPREPROCESS_SET_AGC_TARGET = 46 UMETA(DisplayName = "preprocessor Automatic Gain Control level (int32)") }; +USTRUCT(BlueprintType) +struct FWebRTCSettings +{ + GENERATED_USTRUCT_BODY() + // Enables capturing audio with more than one input channel. + // When false, WebRTC downmixes input to mono before processing. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|WebRTC", meta = (ExposeOnSpawn = "true")) + bool pipeline_multi_channel_capture = false; + + // Enables rendering audio with multiple output channels. + // When false, WebRTC outputs mono audio. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|WebRTC", meta = (ExposeOnSpawn = "true")) + bool pipeline_multi_channel_render = false; + + // Upper limit for WebRTC�s internal audio processing sample rate in Hz. + // WebRTC will resample input audio to this rate for all DSP stages. + // Common values are 16000, 32000, or 48000. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|WebRTC", meta = (ExposeOnSpawn = "true")) + int32 pipeline_maximum_internal_processing_rate = 48000; + + // Enables WebRTC Acoustic Echo Cancellation (AEC). + // Removes far end audio that is played through speakers from the microphone signal. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|WebRTC", meta = (ExposeOnSpawn = "true")) + bool echo_canceller = true; + + // Enables a fixed pre amplification stage before other processing. + // This boosts quiet microphone signals but can amplify noise if misused. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|WebRTC", meta = (ExposeOnSpawn = "true")) + bool pre_amplifier = false; + + // Enables a high pass filter that removes low frequency noise. + // Useful for eliminating rumble or microphone DC offset. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|WebRTC", meta = (ExposeOnSpawn = "true")) + bool high_pass_filter = true; + + // Enables WebRTC noise suppression. + // Reduces stationary background noise like fans or ambient hum. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|WebRTC", meta = (ExposeOnSpawn = "true")) + bool noise_suppression = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|WebRTC", meta = (ExposeOnSpawn = "true", ClampMin = "0", ClampMax = "3")) + int32 noise_suppression_level = 3; + + // Enables transient noise suppression. + // Targets short non speech sounds like keyboard clicks or taps. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|WebRTC", meta = (ExposeOnSpawn = "true")) + bool transient_suppression = true; + + // Enables legacy automatic gain control (AGC1). + // Adjusts microphone gain slowly over time. + // Usually disabled in favor of gain_controller2. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|WebRTC", meta = (ExposeOnSpawn = "true")) + bool gain_controller1 = false; + + // Enables modern automatic gain control (AGC2). + // Provides faster and more stable loudness normalization. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|WebRTC", meta = (ExposeOnSpawn = "true")) + bool gain_controller2 = false; + + // Enables voice activity detection. + // WebRTC estimates whether the current audio frame contains speech. + // Only available when WEBRTC_5414 or newer is used which it is by default in 5.6. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|WebRTC", meta = (ExposeOnSpawn = "true")) + bool voice_detection = false; + + // Enables residual echo detection. + // Detects remaining echo after echo cancellation and can influence other DSP stages. + // Only available when WEBRTC_5414 or newer is used which it is by default in 5.6. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|WebRTC", meta = (ExposeOnSpawn = "true")) + bool residual_echo_detector = true; + + // Enables internal audio level estimation. + // WebRTC computes signal loudness metrics for diagnostics or downstream logic. + // Only available when WEBRTC_5414 or newer is used which it is by default in 5.6. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|WebRTC", meta = (ExposeOnSpawn = "true")) + bool level_estimation = false; +}; + USTRUCT(BlueprintType) struct FSpeexDSPSettingEntry { @@ -74,11 +159,6 @@ struct FSpeexDSPSettings TArray StateNames; UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ShowOnlyInnerProperties, ToolTip = "", Category = "STT|SpeexDSP")) TArray SettingEntries; - UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "", Category = "STT|SpeexDSP")) - int32 SpeexDSP_AEC_Tail = 500; - UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "", Category = "STT|SpeexDSP")) - bool bSpeexDSP_UseAEC = false; - FSpeexDSPSettings() { @@ -96,27 +176,30 @@ USTRUCT(BlueprintType) struct FVADSettings { GENERATED_USTRUCT_BODY() - UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "How aggressive is the VAD Module (quality, normal, aggressive, very aggresive)", Category = "STT|SpeexDSP")) + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "How aggressive is the VAD Module (quality, normal, aggressive, very aggresive)", Category = "STT|VAD")) uint8 VAD_Mode = 2; - UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "How many ms threshold until considered speaking?", Category = "STT|SpeexDSP")) - int32 VAD_SilenceThreshold = 300; - UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "How long to record to consider it, user wants to interrupt avatar", Category = "STT|SpeexDSP")) - float VAD_SpeechWhileBlocked = 0.5f; + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "How long to record to consider it, user wants to interrupt avatar", Category = "STT|VAD")) + float VAD_SpeechWhileBlocked = 1.0f; + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "How long to speak until it is considert speaking?", Category = "STT|VAD")) + float VAD_MinSpeechTime = 0.25f; + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "Much background noise? Try to raise the limit of -dB to even consider speech?", Category = "STT|VAD")) + int32 VAD_MinSpeechAmplitude = -55; }; USTRUCT(BlueprintType) struct FSTTBaseSettings { GENERATED_USTRUCT_BODY() - + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "Which Service to use, can be overridden by �I settings to stream directly to OpenAI", Category = "STT|Base")) + ESTTTranscriptionType STTTranscriptionType = ESTTTranscriptionType::Azure; UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "Use the push to talk button or allow freespeech", Category = "STT|Base")) bool bUsePTT = true; UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "Can the suer interrupt the avatar", Category = "STT|Base")) bool bCanInterrupt = true; UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "Save the final recording from microphone to Saved/STT folder", Category = "STT|Base")) bool bSTTDebugAudioSave = false; - UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "Loopback AudioData from Avatar to Microphone for AEC testing", Category = "STT|Base")) - bool bSTTDebugLoopback = false; + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "Settings of the WebRTC Channel.", Category = "STT|Base")) + FWebRTCSettings WebRTCSettings; UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "Settings of the VAD module that determine if user is currently speaking.", Category = "STT|Base")) FVADSettings VADSettings; UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "Settings of the SpeexDSP Module", Category = "STT|Base")) @@ -126,6 +209,10 @@ struct FSTTBaseSettings UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "Special words that the transcription service needs to know (e.g. b.ReX or Bruce-B).", Category = "STT|Base")) TArray STTSpecialWords; UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "How long to keep transcribing when user released button. Users like to finish early ;)", Category = "STT|Base")) - float PTTPostRollTime = 0.4; + float PTTPostRollTime = 0.5; + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "How long to keep transcribing when user stopped talking.)", Category = "STT|Base")) + float FreespeechPostRollTime = 0.5; + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "How long can the user press the button?)", Category = "STT|Base")) + float MaxTalkingTime = 15.0f; }; \ No newline at end of file diff --git a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/AvatarCore_TTS.Build.cs b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/AvatarCore_TTS.Build.cs index 3853683..04caf67 100644 --- a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/AvatarCore_TTS.Build.cs +++ b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/AvatarCore_TTS.Build.cs @@ -46,7 +46,8 @@ public class AvatarCore_TTS : ModuleRules "Engine", "Slate", "SlateCore", - "PterodacTools" + "PterodacTools", + "WebRTC" // ... add private dependencies that you statically link with here ... } ); diff --git a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/Elevenlabs/ElevenlabsTTSManager.cpp b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/Elevenlabs/ElevenlabsTTSManager.cpp index 0388817..dbde99e 100644 --- a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/Elevenlabs/ElevenlabsTTSManager.cpp +++ b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/Elevenlabs/ElevenlabsTTSManager.cpp @@ -134,7 +134,8 @@ void UElevenlabsTTSManager::GenerateAudio(FTTSTask& Task) return; } - const FString OutputFmt = ChooseOutputFormat(); + FString OutputFmt = ChooseOutputFormat(); + const FString Url = BuildElevenLabsWSUrl( ElevenlabsTTSConfig->ElevenlabsURI, ElevenlabsTTSConfig->ElevenlabsVoiceID, diff --git a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/RealtimeAPI/TTSRealtimeAPIConfig.cpp b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/RealtimeAPI/TTSRealtimeAPIConfig.cpp index 60d8028..02deb89 100644 --- a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/RealtimeAPI/TTSRealtimeAPIConfig.cpp +++ b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/RealtimeAPI/TTSRealtimeAPIConfig.cpp @@ -6,7 +6,6 @@ UTTSRealtimeAPIConfig::UTTSRealtimeAPIConfig(const FObjectInitializer& ObjectInitializer) { TSSManagerClass = URealtimeAPI_TTSManager::StaticClass(); - SilenceThresholdForPlaybackInMS = 0; bCommaSplitRule = ECommaSplitRule::NeverSplit; } diff --git a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/TTSManagerBase.cpp b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/TTSManagerBase.cpp index 9072a1a..7b6d547 100644 --- a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/TTSManagerBase.cpp +++ b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/TTSManagerBase.cpp @@ -13,11 +13,22 @@ UTTSManagerBase::UTTSManagerBase() , ProceduralSoundWave(nullptr) , CurrentState(ETTSState::UNDEFINED) , bIsRooted(false) + , VadInstance(nullptr) + { InitializeDefaultExcludedDelimiters(); AddToRootManager(); } +UTTSManagerBase::~UTTSManagerBase() +{ + if (VadInstance) + { + fvad_free(VadInstance); + VadInstance = nullptr; + } +} + void UTTSManagerBase::InitTTSManager(UTTSBaseConfig* InTSSConfig, bool DebugMode) { bDebugMode = DebugMode; @@ -31,17 +42,28 @@ void UTTSManagerBase::InitTTSManager(UTTSBaseConfig* InTSSConfig, bool DebugMode TTSConfig = InTSSConfig; TTSConfig->AddToRoot(); - OutSampleRate = TTSConfig->ResampleToSampleRate > 0 ? TTSConfig->ResampleToSampleRate : TTSConfig->AudioSampleRate; - - //For Silence detection - if(InTSSConfig->SilenceThresholdForPlaybackInMS > 0) + // Initialize VAD + if (VadInstance) { - fvad = fvad_new(); - fvad_set_sample_rate(fvad, 8000); - fvad_set_mode(fvad, TTSConfig->VADInputSilenceStateValue); + fvad_free(VadInstance); + VadInstance = nullptr; + } + VadInstance = fvad_new(); + if (VadInstance) + { + int32 Rate = (TTSConfig->ResampleToSampleRate > 0) ? TTSConfig->ResampleToSampleRate : TTSConfig->AudioSampleRate; + if (fvad_set_sample_rate(VadInstance, Rate) < 0) + { + TTSLog(FString::Printf(TEXT("VAD init failed: Sample rate %d Hz not supported (use 8000, 16000, 32000, 48000). VAD will be disabled."), Rate), true); + fvad_free(VadInstance); + VadInstance = nullptr; + } + else + { + fvad_set_mode(VadInstance, 30); + TTSLog(FString::Printf(TEXT("VAD initialized with sample rate %d Hz"), Rate)); + } } - - TTSLog(FString::Printf(TEXT("TTS Manager initialized with sample rate: %d"), OutSampleRate)); } void UTTSManagerBase::AddSilenceToTask(FTTSTask& Task, float SilenceLength) @@ -117,7 +139,7 @@ void UTTSManagerBase::PlaybackSoundWave(USoundWave* InSoundWave) int32 NumSamplesToRemove = SampleRate * NumChannels * 0.1f; if (PCMData.Num() >= NumSamplesToRemove) { - PCMData.RemoveAt(0, NumSamplesToRemove, EAllowShrinking::No); + PCMData.RemoveAt(0, NumSamplesToRemove, EAllowShrinking::No); } AddAudioChunk(PCMData, true); } @@ -285,6 +307,8 @@ void UTTSManagerBase::OnTTSManagerFinished() ProceduralSoundWave->Duration = 0.0f; } + bInitialSilenceDone = false; + SetTTSState(ETTSState::Ready); } @@ -505,7 +529,7 @@ void UTTSManagerBase::OnGeneratedAudioChunkReceived(FTTSTask& Task, const TArray if (TTSConfig->ResampleToSampleRate > 0 && TTSConfig->ResampleToSampleRate != TTSConfig->AudioSampleRate) { - ProcessedAudio = ResampleAudio(AudioData, TTSConfig->AudioSampleRate, TTSConfig->AudioNumChannels); + ProcessedAudio = ResampleAudio(AudioData); TTSLog(FString::Printf(TEXT("Resampled audio from %d Hz to %d Hz"), TTSConfig->AudioSampleRate, TTSConfig->ResampleToSampleRate)); } @@ -551,67 +575,6 @@ void UTTSManagerBase::OnGeneratedAudioChunkReceived(FTTSTask& Task, const TArray CheckPlayback(); } } - else - { - if (IsLastChunk) - { - TargetTaskPtr->BytesFromEndToMostRecentSilence = 0; - } - else - { - // Default if no silence in this new chunk: extend the previous trailing length by the bytes added - int32 NewBytesTailFromEnd = TargetTaskPtr->BytesFromEndToMostRecentSilence + ProcessedAudio.Num(); - - const int32 BytesPerSample = 2; - const int32 NumChannels = TTSConfig->AudioNumChannels; - const int32 EffectiveRate = (TTSConfig->ResampleToSampleRate > 0) ? TTSConfig->ResampleToSampleRate : TTSConfig->AudioSampleRate; - if (fvad && EffectiveRate > 0 && NumChannels > 0 && ProcessedAudio.Num() >= BytesPerSample * NumChannels) - { - const int32 Samples = (EffectiveRate / 1000) * TTSConfig->SilenceThresholdForPlaybackInMS; // Samples at current sample rate - if (Samples > 0) - { - const int32 FrameBytes = Samples * NumChannels * BytesPerSample; - const int32 ChunkBytes = ProcessedAudio.Num(); - const int32 EndAligned = (ChunkBytes / FrameBytes) * FrameBytes; - for (int32 FrameStart = EndAligned - FrameBytes; FrameStart >= 0; FrameStart -= FrameBytes) - { - const int32 Mono8kSize = TTSConfig->SilenceThresholdForPlaybackInMS * 8; // 10ms, 20ms or 30ms at 8kHz - TArray Mono8k; - Mono8k.SetNumZeroed(Mono8kSize); - double step = static_cast(Samples) / Mono8kSize; // map current rate to 8kHz - double pos = 0.0; - for (int32 o = 0; o < Mono8kSize; ++o) - { - int32 inIdx = static_cast(pos); - if (inIdx >= Samples) inIdx = Samples - 1; - const int32 baseByte = FrameStart + (inIdx * NumChannels) * BytesPerSample; - int16 sample = 0; - if (baseByte + 1 < ChunkBytes) - { - // take first channel, little-endian 16-bit - sample = static_cast(static_cast(ProcessedAudio[baseByte] | (ProcessedAudio[baseByte + 1] << 8))); - } - Mono8k[o] = sample; - pos += step; - } - int vad = fvad_process(fvad, Mono8k.GetData(), Mono8kSize); - if (vad == 0) - { - // Silence found within the new chunk: set tail = bytes from end of the entire buffer - NewBytesTailFromEnd = ChunkBytes - (FrameStart + FrameBytes); - break; // stop at first silence from chunk end - } - } - } - } - else - { - NewBytesTailFromEnd = 0; - } - - TargetTaskPtr->BytesFromEndToMostRecentSilence = NewBytesTailFromEnd; - } - } TargetTaskPtr->bIsGenerating = !IsLastChunk; } @@ -623,28 +586,29 @@ void UTTSManagerBase::OnGeneratedAudioChunkReceived(FTTSTask& Task, const TArray UTTSManagerBase::CheckPlayback(); } -TArray UTTSManagerBase::ResampleAudio(const TArray& InputPCM, int32 InputSampleRate, int32 NumChannels) +TArray UTTSManagerBase::ResampleAudio(const TArray& InputPCM) { - // Simple linear resampling from input rate to 24000Hz + // Check for invalid inputs or no resampling needed + if (TTSConfig->ResampleToSampleRate <= 0 || TTSConfig->AudioSampleRate <= 0 || TTSConfig->AudioNumChannels <= 0) + { + return InputPCM; + } + + // Simple linear resampling from InputSampleRate to ResampleToSampleRate // This assumes 16-bit PCM audio (2 bytes per sample) - // Calculate resampling ratio - const double ResampleRatio = TTSConfig->ResampleToSampleRate / static_cast(InputSampleRate); + // Calculate step size: how much to advance in input for each output sample + // Step > 1.0 means downsampling (skipping input samples) + // Step < 1.0 means upsampling (interpolating between input samples) + const double Step = static_cast(TTSConfig->AudioSampleRate) / static_cast(TTSConfig->ResampleToSampleRate); - // Extract a fixed chunk of audio (250ms) from the continuous buffer - // We'll use a small overlap between chunks (10ms) to avoid cracking sounds - const float ChunkDuration = 0.25f; // 250ms chunk size const int32 BytesPerSample = 2; // 16-bit PCM audio is 2 bytes per sample - const int32 BytesPerSecond = TTSConfig->AudioSampleRate * TTSConfig->AudioNumChannels * BytesPerSample; - const int32 ChunkSize = static_cast(BytesPerSecond * ChunkDuration); - - // Ensure the chunk size is aligned to the frame size (bytes per sample * channels) - const int32 FrameSize = TTSConfig->AudioNumChannels * BytesPerSample; // Total bytes per audio frame - - // Calculate output buffer size - each sample is 2 bytes (16-bit) per channel - const int32 NumSamplesInput = InputPCM.Num() / (NumChannels * BytesPerSample); - const int32 NumSamplesOutput = FMath::CeilToInt(NumSamplesInput * ResampleRatio); - const int32 OutputSize = NumSamplesOutput * NumChannels * BytesPerSample; + const int32 NumSamplesInput = InputPCM.Num() / (TTSConfig->AudioNumChannels * BytesPerSample); + + // We want output duration roughly equal to input duration + // NumSamplesOutput = NumSamplesInput / Step + const int32 NumSamplesOutput = FMath::CeilToInt(NumSamplesInput / Step); + const int32 OutputSize = NumSamplesOutput * TTSConfig->AudioNumChannels * BytesPerSample; // Create output buffer TArray OutputPCM; @@ -654,33 +618,34 @@ TArray UTTSManagerBase::ResampleAudio(const TArray& InputPCM, int3 for (int32 OutSampleIdx = 0; OutSampleIdx < NumSamplesOutput; ++OutSampleIdx) { // Calculate the corresponding input sample index - const double InSampleIdxF = OutSampleIdx / ResampleRatio; + const double InSampleIdxF = OutSampleIdx * Step; const int32 InSampleIdx = FMath::FloorToInt(InSampleIdxF); const double Fraction = InSampleIdxF - InSampleIdx; // For each channel - for (int32 Channel = 0; Channel < NumChannels; ++Channel) + for (int32 Channel = 0; Channel < TTSConfig->AudioNumChannels; ++Channel) { // Get the two input samples to interpolate between - int32 InSample1 = InSampleIdx; + // Clamp indices to ensure valid range + int32 InSample1 = FMath::Min(InSampleIdx, NumSamplesInput - 1); int32 InSample2 = FMath::Min(InSampleIdx + 1, NumSamplesInput - 1); // Calculate input buffer indices - int32 InIndex1 = (InSample1 * NumChannels + Channel) * BytesPerSample; - int32 InIndex2 = (InSample2 * NumChannels + Channel) * BytesPerSample; + int32 InIndex1 = (InSample1 * TTSConfig->AudioNumChannels + Channel) * BytesPerSample; + int32 InIndex2 = (InSample2 * TTSConfig->AudioNumChannels + Channel) * BytesPerSample; - // Ensure indices are within bounds + // Ensure indices are within bounds (double check) if (InIndex1 >= 0 && InIndex1 + 1 < InputPCM.Num() && InIndex2 >= 0 && InIndex2 + 1 < InputPCM.Num()) { - // Convert bytes to 16-bit samples + // Convert bytes to 16-bit samples (Little Endian) int16 Sample1 = (static_cast(InputPCM[InIndex1 + 1]) << 8) | InputPCM[InIndex1]; int16 Sample2 = (static_cast(InputPCM[InIndex2 + 1]) << 8) | InputPCM[InIndex2]; // Linear interpolation - int16 OutputSample = static_cast((1.0 - Fraction) * Sample1 + Fraction * Sample2); + int16 OutputSample = static_cast(Sample1 + Fraction * (Sample2 - Sample1)); // Write to output buffer - int32 OutIndex = (OutSampleIdx * NumChannels + Channel) * BytesPerSample; + int32 OutIndex = (OutSampleIdx * TTSConfig->AudioNumChannels + Channel) * BytesPerSample; if (OutIndex + 1 < OutputPCM.Num()) { OutputPCM[OutIndex] = OutputSample & 0xFF; @@ -691,7 +656,7 @@ TArray UTTSManagerBase::ResampleAudio(const TArray& InputPCM, int3 } TTSLog(FString::Printf(TEXT("Resampled audio from %dHz to %d: %d bytes -> %d bytes"), - InputSampleRate, TTSConfig->ResampleToSampleRate, InputPCM.Num(), OutputPCM.Num())); + TTSConfig->AudioSampleRate, TTSConfig->ResampleToSampleRate, InputPCM.Num(), OutputPCM.Num())); return OutputPCM; } @@ -812,6 +777,7 @@ void UTTSManagerBase::PollReadyOnce() { // Periodic poll: finalize Ready only when audio fully stopped const bool bIsPlaying = (RegisteredAudioComponent && RegisteredAudioComponent->IsPlaying()); + if (!bIsPlaying && CheckAllDone()) { if (UWorld* World = GetWorld()) @@ -859,6 +825,23 @@ bool UTTSManagerBase::FlushToAudioQueue() const int32 BytesPerSample = 2; // 16-bit PCM const int32 NumChannels = TTSConfig->AudioNumChannels; const int32 EffectiveSampleRate = (TTSConfig->ResampleToSampleRate > 0) ? TTSConfig->ResampleToSampleRate : TTSConfig->AudioSampleRate; + const int32 BytesPerFrame = BytesPerSample * NumChannels; + const int32 BytesPerSecond = EffectiveSampleRate * BytesPerFrame; + + // VAD Frame Size (20ms) + const int32 VadFrameMs = 30; + const int32 SamplesPerVadFrame = EffectiveSampleRate * VadFrameMs / 1000; + const int32 BytesPerVadFrame = SamplesPerVadFrame * BytesPerFrame; + + // Calculate currently buffered audio + int32 TotalBufferedBytes = 0; + { + FScopeLock SegLock(&SegmentQueueCriticalSection); + for (const FQueuedSegment& Seg : QueuedSegments) + { + TotalBufferedBytes += Seg.BytesRemaining; + } + } FScopeLock TaskLock(&TaskQueueCriticalSection); // Remove fully generated AND fully played tasks from the front before flushing @@ -877,6 +860,7 @@ bool UTTSManagerBase::FlushToAudioQueue() { return false; } + for (int32 i = 0; i < ActiveTasks.Num(); ++i) { // Ensure all predecessors are fully drained and complete before we can flush task i (except i==0 which has no predecessors) @@ -897,45 +881,132 @@ bool UTTSManagerBase::FlushToAudioQueue() FTTSTask& Curr = ActiveTasks[i]; // Compute how many bytes are available to queue beyond what has already been queued - const int32 BytesTotal = Curr.AudioData.Num() - Curr.BytesFromEndToMostRecentSilence; + const int32 BytesTotal = Curr.AudioData.Num(); const int32 BytesQueuedSoFar = Curr.QueueCursor; const int32 BytesAvail = BytesTotal - BytesQueuedSoFar; - if (BytesAvail > 0) + + // Initial silence check + if (!bInitialSilenceDone && BytesAvail < (TTSConfig->BufferSeconds * BytesPerSecond)) + return false; + else + bInitialSilenceDone = true; + + if (BytesAvail <= 0) continue; + + int32 BytesToQueue = BytesAvail; + + // VAD Logic + bool bStoppedEarly = false; + if (TTSConfig->BufferSeconds > 0 && VadInstance && Curr.bIsGenerating) { - if (ProceduralSoundWave) + // Only process full VAD frames + int32 NumFrames = BytesAvail / BytesPerVadFrame; + + // If we have less than a frame, wait for more data + if (NumFrames == 0) + { + bStoppedEarly = true; + BytesToQueue = 0; + } + else { - if (CurrentState == ETTSState::Producing || CurrentState == ETTSState::WaitingForChunks) + int32 ProcessedBytes = 0; + // Temp buffer for VAD (mono) + TArray MonoFrame; + MonoFrame.SetNumUninitialized(SamplesPerVadFrame); + + const uint8* AudioPtr = Curr.AudioData.GetData() + BytesQueuedSoFar; + + for (int32 f = 0; f < NumFrames; ++f) { - AddSilenceToTask(Curr, TTSConfig->SilenceInitial); + // Extract frame and convert to mono if needed + const int16_t* FrameData = reinterpret_cast(AudioPtr + ProcessedBytes); + + if (NumChannels == 1) + { + FMemory::Memcpy(MonoFrame.GetData(), FrameData, SamplesPerVadFrame * sizeof(int16_t)); + } + else + { + // Average channels to mono + for (int32 s = 0; s < SamplesPerVadFrame; ++s) + { + int32 Sum = 0; + for (int32 c = 0; c < NumChannels; ++c) + { + Sum += FrameData[s * NumChannels + c]; + } + MonoFrame[s] = static_cast(Sum / NumChannels); + } + } + + int IsVoice = fvad_process(VadInstance, MonoFrame.GetData(), SamplesPerVadFrame); + if (IsVoice == 0) // Silence + { + float CurrentBufferSec = (float)TotalBufferedBytes / (float)BytesPerSecond; + if (CurrentBufferSec < TTSConfig->BufferSeconds) + { + UE_LOG(LogTemp, Warning, TEXT("Let's stop here and wait for the buffer")); + // Buffer is low, stop here + bStoppedEarly = true; + break; + } + else + UE_LOG(LogTemp, Warning, TEXT("Buffer says plenty to play back %f"), CurrentBufferSec); + } + + ProcessedBytes += BytesPerVadFrame; + // Virtual update for next frame check (we assume this frame WILL be queued) + TotalBufferedBytes += BytesPerVadFrame; } - TTSLog(FString::Printf(TEXT("Queueing %d bytes for task %d"), BytesAvail, Curr.TaskID)); + BytesToQueue = ProcessedBytes; + } + } + + if (BytesToQueue > 0) + { + if (ProceduralSoundWave) + { + TTSLog(FString::Printf(TEXT("Queueing %d bytes for task %d"), BytesToQueue, Curr.TaskID)); // Queue the available tail for this task - ProceduralSoundWave->QueueAudio(Curr.AudioData.GetData() + BytesQueuedSoFar, BytesAvail); + ProceduralSoundWave->QueueAudio(Curr.AudioData.GetData() + BytesQueuedSoFar, BytesToQueue); // Update duration accurately: seconds = bytes / (bytesPerSample * channels * sampleRate) - const float DurationInc = static_cast(BytesAvail) / static_cast(BytesPerSample * NumChannels * EffectiveSampleRate); + const float DurationInc = static_cast(BytesToQueue) / static_cast(BytesPerSecond); ProceduralSoundWave->Duration += DurationInc; } // Advance queue cursor but DO NOT mark as rendered yet - Curr.QueueCursor += BytesAvail; + Curr.QueueCursor += BytesToQueue; // Track this queued segment for precise playback accounting { FScopeLock SegLock(&SegmentQueueCriticalSection); - QueuedSegments.Add({ Curr.TaskID, BytesAvail }); + QueuedSegments.Add({ Curr.TaskID, BytesToQueue }); + } + // If we didn't use VAD loop logic to increment TotalBufferedBytes (e.g. bIsGenerating=false), do it now + // Note: If VAD was used, we already incremented TotalBufferedBytes in the loop for the bytes we are queuing. + if (!VadInstance || !Curr.bIsGenerating) + { + TotalBufferedBytes += BytesToQueue; } bQueuedAny = true; + + if (bQueuedAny) + { + SetTTSState(ETTSState::Speaking); + if (RegisteredAudioComponent && ProceduralSoundWave && !RegisteredAudioComponent->IsPlaying()) + { + RegisteredAudioComponent->SetSound(ProceduralSoundWave); + RegisteredAudioComponent->Play(); + TTSLog(TEXT("Started AudioComponent playback")); + } + } } - if (bQueuedAny) + if (bStoppedEarly) { - SetTTSState(ETTSState::Speaking); - if (RegisteredAudioComponent && ProceduralSoundWave && !RegisteredAudioComponent->IsPlaying()) - { - RegisteredAudioComponent->SetSound(ProceduralSoundWave); - RegisteredAudioComponent->Play(); - TTSLog(TEXT("Started AudioComponent playback")); - } + break; // Stop flushing as we are blocked on this task } } + // Fallback: if nothing was queued now but there are already queued segments, ensure playback is running if (!bQueuedAny && RegisteredAudioComponent && ProceduralSoundWave && !RegisteredAudioComponent->IsPlaying()) { @@ -952,7 +1023,7 @@ bool UTTSManagerBase::FlushToAudioQueue() TTSLog(TEXT("Started AudioComponent playback (resume: existing queued segments)")); } } - return (bQueuedAny); + return bQueuedAny; } //Add a new Task to the Queue @@ -972,6 +1043,7 @@ void UTTSManagerBase::OnAudioSample(const TArray& PCMData, int32 NumSampl // First, account for actually rendered bytes to advance per-task playback ConsumeRenderedBytes(PCMData.Num()); + // Convert to float for Blueprint streaming TArray FloatPCMData; FloatPCMData.Reserve(PCMData.Num() / 2); for (int32 j = 0; j < PCMData.Num(); j += 2) @@ -983,7 +1055,9 @@ void UTTSManagerBase::OnAudioSample(const TArray& PCMData, int32 NumSampl FloatPCMData.Add(NormalizedSample); } } - OnTTSAudioChunkForBP.Broadcast(FloatPCMData, TTSConfig->AudioSampleRate, TTSConfig->AudioNumChannels); + // Broadcast with the effective sample rate, not the input rate + const int32 EffectiveSampleRate = (TTSConfig->ResampleToSampleRate > 0) ? TTSConfig->ResampleToSampleRate : TTSConfig->AudioSampleRate; + OnTTSAudioChunkForBP.Broadcast(FloatPCMData, EffectiveSampleRate, TTSConfig->AudioNumChannels); } void UTTSManagerBase::AddExcludedDelimiters(const TArray& Delimiters) diff --git a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/Elevenlabs/ElevenlabsTTSConfig.h b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/Elevenlabs/ElevenlabsTTSConfig.h index df38abe..87f2914 100644 --- a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/Elevenlabs/ElevenlabsTTSConfig.h +++ b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/Elevenlabs/ElevenlabsTTSConfig.h @@ -28,7 +28,7 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreTTS|Elevenlabs", meta = (ExposeOnSpawn = "true")) FString ElevenlabsModelID = TEXT("eleven_flash_v2_5"); - // Model to use for TTS generation (e.g., "eleven_multilingual_v2") + // Model to use for TTS generation (e.g., "eleven_multilingual_v2") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreTTS|Elevenlabs", meta = (ExposeOnSpawn = "true")) FString ElevenlabsModelID_PreCache = TEXT("eleven_multilingual_v2"); diff --git a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSBaseConfig.h b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSBaseConfig.h index 135845e..910e0d2 100644 --- a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSBaseConfig.h +++ b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSBaseConfig.h @@ -50,9 +50,9 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreTTS|Base", meta = (ExposeOnSpawn = "true")) int32 ResampleToSampleRate = -1; - //Silence as buffer before starting to speak - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreTTS|Base", meta = (ExposeOnSpawn = "true")) - float SilenceInitial = 0.0f; + //Buffer seconds for the WebRTC Channel + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreTTS|Base", meta = (ExposeOnSpawn = "true")) + float BufferSeconds = 0.25f; //Chunk length in seconds UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreTTS|Base", meta = (ExposeOnSpawn = "true")) @@ -70,14 +70,6 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreTTS|Base", meta = (ExposeOnSpawn = "true")) float ChunkLength = 0.01f; - //Must be 10ms, 20ms or 30ms - How long of a pause to consider silence (0 ms to disable it) - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreTTS|Base", meta = (ExposeOnSpawn = "true")) - int SilenceThresholdForPlaybackInMS = 10; - - //How aggressive is the silence detection for the detection of silence for playback - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreTTS|Base", meta = (ExposeOnSpawn = "true")) - int VADInputSilenceStateValue = 1; - //Multiplier to make stream audio less loud UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreTTS|Base", meta = (ExposeOnSpawn = "true")) float StreamAmplitudeMultiplier = 0.25f; diff --git a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSManagerBase.h b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSManagerBase.h index 05675b9..9b88cf4 100644 --- a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSManagerBase.h +++ b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSManagerBase.h @@ -1,11 +1,11 @@ #pragma once -#include "fvad.h" #include "CoreMinimal.h" #include "UObject/Object.h" #include "AvatarSoundWave.h" #include "Components/AudioComponent.h" #include "TTSBaseConfig.h" +#include "fvad.h" #include "TTSManagerBase.generated.h" UENUM(BlueprintType) @@ -47,8 +47,6 @@ struct FTTSTask int32 PlaybackCursor = 0; // Current position in the audio buffer in bytes // Queued cursor: how many bytes have been queued to the procedural wave (may be ahead of PlaybackCursor) int32 QueueCursor = 0; - // Where is the most recent silence in the queued audio. - int32 BytesFromEndToMostRecentSilence = 0; bool bIsComplete = false; // True when all chunks are received and played FTTSTask() : Text(), OriginalText(), TaskID(0) {} FTTSTask(const FString& InText, FString& InOriginalText, int32 InTaskID) : Text(InText), OriginalText(InOriginalText), TaskID(InTaskID) {} @@ -68,6 +66,7 @@ class AVATARCORE_TTS_API UTTSManagerBase : public UObject public: UTTSManagerBase(); + virtual ~UTTSManagerBase(); // Blueprint Log Event UPROPERTY(BlueprintAssignable, Category = "AvatarCoreTTS|Events") @@ -227,7 +226,7 @@ protected: UAvatarSoundWave* ProceduralSoundWave; // Helper function to resample PCM audio to 24000Hz for A2F compatibility - TArray ResampleAudio(const TArray& InputPCM, int32 InputSampleRate, int32 NumChannels); + TArray ResampleAudio(const TArray& InputPCM); // State ETTSState CurrentState; @@ -276,14 +275,12 @@ private: // Timer for polling Ready transition after playback finished FTimerHandle ReadyPollTimerHandle; - - Fvad* fvad = nullptr; - - // === Disk cache helpers === + +// === Disk cache helpers === protected: // Compute stable file hash from HashPrefix + Text FString ComputeTaskHash(const FString& InText) const; - // Base cache directory: / +// ... (rest of the code remains the same) FString GetCacheDirectory() const; // Full cache file path for given text (Hash.wav) FString GetCacheFilePath(const FString& InText) const; @@ -292,6 +289,8 @@ protected: // Save Task.AudioData as WAV to cache (returns true on success) bool SaveTaskAudioToCache(const FTTSTask& Task); + bool bInitialSilenceDone = false; + void OnGeneratedAudioChunkReceived(FTTSTask& Task, const TArray& AudioData, bool IsLastChunk); // Accurate playback accounting without relying on underflow: FIFO of queued segments @@ -301,4 +300,7 @@ protected: // Consume bytes actually rendered by the audio device (called from OnAudioSample) void ConsumeRenderedBytes(int32 Bytes); + + // VAD Instance + Fvad* VadInstance = nullptr; }; diff --git a/Unreal/Plugins/BSettings/BSettings.uplugin b/Unreal/Plugins/BSettings/BSettings.uplugin index 7bb8db5..2f80868 100644 --- a/Unreal/Plugins/BSettings/BSettings.uplugin +++ b/Unreal/Plugins/BSettings/BSettings.uplugin @@ -18,7 +18,7 @@ { "Name": "BSettings", "Type": "Runtime", - "LoadingPhase": "Default" + "LoadingPhase": "PostDefault" } ], "Plugins": [ diff --git a/Unreal/Plugins/PterodacTools/Content/ConfigPawn/AC_ConfigComponent.uasset b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/AC_ConfigComponent.uasset new file mode 100644 index 0000000..18149f3 --- /dev/null +++ b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/AC_ConfigComponent.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59ac59fae3c3052ceff81a56939e3ae94879f090625bb1a7fc65b636daa91403 +size 235921 diff --git a/Unreal/Plugins/PterodacTools/Content/ConfigPawn/BP_TestConfigurableObject.uasset b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/BP_TestConfigurableObject.uasset new file mode 100644 index 0000000..4e93baf --- /dev/null +++ b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/BP_TestConfigurableObject.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:882ba49027a222bb289478e935a40153a37904d51847db2b54246fa41b4f5a99 +size 2576 diff --git a/Unreal/Plugins/PterodacTools/Content/ConfigPawn/ConfigPawn.uasset b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/ConfigPawn.uasset index 3cac1b5..bb14d15 100644 Binary files a/Unreal/Plugins/PterodacTools/Content/ConfigPawn/ConfigPawn.uasset and b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/ConfigPawn.uasset differ diff --git a/Unreal/Plugins/PterodacTools/Content/ConfigPawn/Debug/BP_TestConfigurableObject.uasset b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/Debug/BP_TestConfigurableObject.uasset new file mode 100644 index 0000000..aee769e --- /dev/null +++ b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/Debug/BP_TestConfigurableObject.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4361cea6e032a030ebc8a7f9c6bad0598d474ceda28a69e15670cf231742d45c +size 62546 diff --git a/Unreal/Plugins/PterodacTools/Content/ConfigPawn/Structs/ConfigAdditionalValueEntry.uasset b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/Structs/ConfigAdditionalValueEntry.uasset new file mode 100644 index 0000000..cf143c5 --- /dev/null +++ b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/Structs/ConfigAdditionalValueEntry.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b43eb288b35ca5ea5777d0f7d9bb0aadffab512698e9b26e6944693ff06e1d74 +size 4929 diff --git a/Unreal/Plugins/PterodacTools/Content/ConfigPawn/Structs/ConfigEntry.uasset b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/Structs/ConfigEntry.uasset index 6d2c2aa..52859ac 100644 Binary files a/Unreal/Plugins/PterodacTools/Content/ConfigPawn/Structs/ConfigEntry.uasset and b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/Structs/ConfigEntry.uasset differ diff --git a/Unreal/Plugins/PterodacTools/Content/ConfigPawn/Structs/SG_ConfigObjects.uasset b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/Structs/SG_ConfigObjects.uasset new file mode 100644 index 0000000..453fb7c --- /dev/null +++ b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/Structs/SG_ConfigObjects.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc2a10e09071dbd5480d6c9e79d71ca8b339e8077d5ba400c3801d335676b01f +size 103240 diff --git a/Unreal/Plugins/PterodacTools/Content/ConfigPawn/W_DebugConfigModeWidget.uasset b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/W_DebugConfigModeWidget.uasset index 77934a8..1eb0e07 100644 Binary files a/Unreal/Plugins/PterodacTools/Content/ConfigPawn/W_DebugConfigModeWidget.uasset and b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/W_DebugConfigModeWidget.uasset differ diff --git a/Unreal/Plugins/PterodacTools/Content/ConfigPawn/Widgets/W_DebugConfigModeWidget.uasset b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/Widgets/W_DebugConfigModeWidget.uasset new file mode 100644 index 0000000..c97178b --- /dev/null +++ b/Unreal/Plugins/PterodacTools/Content/ConfigPawn/Widgets/W_DebugConfigModeWidget.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff22250f6157ef87e3274b5da4468c1c320f496b8a2ca314cc5c78ac5fe32c3e +size 363509 diff --git a/Unreal/Plugins/PterodacTools/Content/DebugWidget/ChildWidgets/W_DebugWidget_Button.uasset b/Unreal/Plugins/PterodacTools/Content/DebugWidget/ChildWidgets/W_DebugWidget_Button.uasset index 4d8cedc..a4ee02d 100644 Binary files a/Unreal/Plugins/PterodacTools/Content/DebugWidget/ChildWidgets/W_DebugWidget_Button.uasset and b/Unreal/Plugins/PterodacTools/Content/DebugWidget/ChildWidgets/W_DebugWidget_Button.uasset differ diff --git a/Unreal/Plugins/PterodacTools/Content/DebugWidget/ChildWidgets/W_DebugWidget_EditableField.uasset b/Unreal/Plugins/PterodacTools/Content/DebugWidget/ChildWidgets/W_DebugWidget_EditableField.uasset index bdc8a49..c139fe6 100644 Binary files a/Unreal/Plugins/PterodacTools/Content/DebugWidget/ChildWidgets/W_DebugWidget_EditableField.uasset and b/Unreal/Plugins/PterodacTools/Content/DebugWidget/ChildWidgets/W_DebugWidget_EditableField.uasset differ diff --git a/Unreal/Plugins/PterodacTools/Content/DebugWidget/ChildWidgets/W_DebugWidget_Text.uasset b/Unreal/Plugins/PterodacTools/Content/DebugWidget/ChildWidgets/W_DebugWidget_Text.uasset index eea7061..9177e17 100644 Binary files a/Unreal/Plugins/PterodacTools/Content/DebugWidget/ChildWidgets/W_DebugWidget_Text.uasset and b/Unreal/Plugins/PterodacTools/Content/DebugWidget/ChildWidgets/W_DebugWidget_Text.uasset differ diff --git a/Unreal/Plugins/PterodacTools/Content/DebugWidget/DebugWidget.uasset b/Unreal/Plugins/PterodacTools/Content/DebugWidget/DebugWidget.uasset index c0f4cb1..967a992 100644 Binary files a/Unreal/Plugins/PterodacTools/Content/DebugWidget/DebugWidget.uasset and b/Unreal/Plugins/PterodacTools/Content/DebugWidget/DebugWidget.uasset differ diff --git a/Unreal/Plugins/PterodacTools/Content/DebugWidget/Miscellaneous/DebugMaximizeIcon.uasset b/Unreal/Plugins/PterodacTools/Content/DebugWidget/Miscellaneous/DebugMaximizeIcon.uasset new file mode 100644 index 0000000..d6e9ae5 --- /dev/null +++ b/Unreal/Plugins/PterodacTools/Content/DebugWidget/Miscellaneous/DebugMaximizeIcon.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf7ca7bafb7373ec19b2fc2155535a464466f068f9f358f8cdd02fe2ddcf7d1a +size 11407 diff --git a/Unreal/Plugins/PterodacTools/Content/DebugWidget/Miscellaneous/Savegame_DebugWidget.uasset b/Unreal/Plugins/PterodacTools/Content/DebugWidget/Miscellaneous/Savegame_DebugWidget.uasset index 06dc44e..0edd4a2 100644 Binary files a/Unreal/Plugins/PterodacTools/Content/DebugWidget/Miscellaneous/Savegame_DebugWidget.uasset and b/Unreal/Plugins/PterodacTools/Content/DebugWidget/Miscellaneous/Savegame_DebugWidget.uasset differ diff --git a/Unreal/Plugins/PterodacTools/Content/Materials/DefaultTextMaterialEmissive.uasset b/Unreal/Plugins/PterodacTools/Content/Materials/DefaultTextMaterialEmissive.uasset new file mode 100644 index 0000000..830e892 --- /dev/null +++ b/Unreal/Plugins/PterodacTools/Content/Materials/DefaultTextMaterialEmissive.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:142c53d5f9295586cf34517e9b7e9973217e0d4950ff72d66874016213e9dd56 +size 27304 diff --git a/Unreal/Plugins/PterodacTools/Source/PterodacTools/Public/PterodacToolsBPLibrary.h b/Unreal/Plugins/PterodacTools/Source/PterodacTools/Public/PterodacToolsBPLibrary.h index 794ae8c..b7cf3c2 100644 --- a/Unreal/Plugins/PterodacTools/Source/PterodacTools/Public/PterodacToolsBPLibrary.h +++ b/Unreal/Plugins/PterodacTools/Source/PterodacTools/Public/PterodacToolsBPLibrary.h @@ -49,7 +49,7 @@ struct FTheProcHandle GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite) - int processID; + int processID = -1; #if PLATFORM_WINDOWS FProcHandle procHandle;