--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.ComponentModel;
+using Tizen.NUI.Scene3D;
+
+namespace Tizen.AIAvatar
+{
+ /// <summary>
+ /// The AnimationInfo class manages animation data for an Avatar, including motion data and names. It is not meant to be directly edited by users or editors.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class AnimationInfo
+ {
+ /// <summary>
+ /// Gets the motion data associated with this animation.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public MotionData MotionData { get; private set; }
+
+ /// <summary>
+ /// Gets the name of this animation.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string MotionName { get; private set; }
+
+ /// <summary>
+ /// Initializes a new instance of the AnimationInfo class with the specified motion data and name.
+ /// </summary>
+ /// <param name="motionData">TheThe motion data associated with this animation.</param>
+ /// <param name="motionName">The name of this animation.</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AnimationInfo(MotionData motionData, string motionName)
+ {
+ MotionData = motionData;
+ MotionName = motionName;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.ComponentModel;
+
+namespace Tizen.AIAvatar
+{
+ /// <summary>
+ /// This class provides arguments for handling avatar motion change events.
+ /// <member name = "Previous" > The previous state of the avatar's motion.</member>
+ /// <member name = "Current" > The current state of the avatar's motion.</member>
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class AvatarMotionChangedEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Initializes a new instance of the AvatarMotionChangedEventArgs class with the specified previous and current states.
+ /// </summary>
+ /// <param name="previous">The previous state of the avatar's motion.</param>
+ /// <param name="current">The current state of the avatar's motion.</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AvatarMotionChangedEventArgs(AvatarMotionState previous, AvatarMotionState current)
+ {
+ Previous = previous;
+ Current = current;
+ }
+
+ /// <summary>
+ /// The previous state.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AvatarMotionState Previous
+ {
+ get;
+ internal set;
+ }
+
+ /// <summary>
+ /// The current state.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AvatarMotionState Current
+ {
+ get;
+ internal set;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.ComponentModel;
+
+namespace Tizen.AIAvatar
+{
+ /// <summary>
+ /// Enumeration for the states.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public enum AvatarMotionState
+ {
+ /// <summary>
+ /// Created state.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Failed = -1,
+
+ /// <summary>
+ /// Ready state.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Ready = 0,
+
+ /// <summary>
+ /// Playing state.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Playing = 3,
+
+ /// <summary>
+ /// Paused state.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Paused = 4,
+
+ /// <summary>
+ /// Stopped state.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Stopped = 5,
+
+ /// <summary>
+ /// Unavailable state.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Unavailable
+ };
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using Tizen.NUI;
+
+using static Tizen.AIAvatar.AIAvatar;
+
+namespace Tizen.AIAvatar
+{
+ internal class EyeBlinker : IBlendShapeModule, IDisposable
+ {
+ private AvatarMotionState currentMotionState = AvatarMotionState.Unavailable;
+
+ private readonly Object motionChangedLock = new Object();
+ private event EventHandler<AvatarMotionChangedEventArgs> motionChanged;
+
+ private const int blinkIntervalMinimum = 800;
+ private const int blinkIntervalMaximum = 3000;
+ private Animation eyeAnimation;
+
+ private Timer blinkTimer;
+
+ private bool isPlaying = false;
+ private const int blinkDuration = 200;
+
+ public AvatarMotionState CurrentMotionState
+ {
+ get
+ {
+ return currentMotionState;
+ }
+ set
+ {
+ if (currentMotionState == value)
+ {
+ return;
+ }
+ var preState = currentMotionState;
+ currentMotionState = value;
+ if (motionChanged != null)
+ {
+ motionChanged?.Invoke(this, new AvatarMotionChangedEventArgs(preState, currentMotionState));
+ }
+ }
+ }
+
+ public event EventHandler<AvatarMotionChangedEventArgs> MotionStateChanged
+ {
+ add
+ {
+ lock (motionChangedLock)
+ {
+ motionChanged += value;
+ }
+
+ }
+
+ remove
+ {
+ lock (motionChangedLock)
+ {
+ if (motionChanged == null)
+ {
+ Log.Error(LogTag, "Remove StateChanged Failed : motionChanged is null");
+ return;
+ }
+ motionChanged -= value;
+ }
+ }
+ }
+
+ public EyeBlinker()
+ {
+
+ }
+
+ public void Dispose()
+ {
+ DestroyAnimation();
+ }
+
+ public void Init(Animation eyeAnimation)
+ {
+ this.eyeAnimation = eyeAnimation;
+ }
+
+ public void Play()
+ {
+ //data
+ StartEyeBlink();
+ }
+
+ public void Stop()
+ {
+ StopEyeBlink();
+ }
+
+ public void Pause()
+ {
+ eyeAnimation?.Pause();
+ }
+
+ public void Destroy()
+ {
+ DestroyAnimation();
+ }
+
+ private void StartEyeBlink()
+ {
+ DestroyBlinkTimer();
+
+ blinkTimer = new Timer(blinkDuration);
+ if (blinkTimer != null)
+ {
+ blinkTimer.Tick += OnBlinkTimer;
+ blinkTimer?.Start();
+ isPlaying = true;
+ }
+ }
+
+ private void PauseEyeBlink()
+ {
+ blinkTimer?.Stop();
+ isPlaying = false;
+ }
+
+ private void StopEyeBlink()
+ {
+ blinkTimer?.Stop();
+ isPlaying = false;
+ }
+
+ private void DestroyAnimation()
+ {
+ DestroyBlinkTimer();
+ if (eyeAnimation != null)
+ {
+ eyeAnimation.Stop();
+ eyeAnimation.Dispose();
+ eyeAnimation = null;
+ }
+ isPlaying = false;
+ }
+
+ private bool OnBlinkTimer(object source, Timer.TickEventArgs e)
+ {
+ if (eyeAnimation == null)
+ {
+ Log.Error(LogTag, "eye animation is not ready");
+ return false;
+ }
+ eyeAnimation?.Play();
+
+ var random = new Random();
+ var fortimerinterval = (uint)random.Next(blinkIntervalMinimum, blinkIntervalMaximum);
+ blinkTimer.Interval = fortimerinterval;
+ return true;
+ }
+
+ private void DestroyBlinkTimer()
+ {
+ if (blinkTimer != null)
+ {
+ blinkTimer.Tick -= OnBlinkTimer;
+ blinkTimer.Stop();
+ blinkTimer.Dispose();
+ blinkTimer = null;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using Tizen.NUI;
+
+namespace Tizen.AIAvatar
+{
+ internal interface IBlendShapeModule
+ {
+ public void Init(Animation animation);
+
+ public void Play();
+
+ public void Stop();
+
+ public void Pause();
+
+ public void Destroy();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using Tizen.NUI;
+using Tizen.NUI.Scene3D;
+using static Tizen.AIAvatar.AIAvatar;
+
+namespace Tizen.AIAvatar
+{
+ internal class MotionPlayer
+ {
+ private Animation motionAnimation;
+ private EyeBlinker eyeBlinker;
+
+ internal Animation MotionAnimation { get => motionAnimation; private set => motionAnimation = value; }
+
+ internal MotionPlayer()
+ {
+ eyeBlinker = new EyeBlinker();
+
+
+ }
+
+ internal void PlayAnimation(Animation motionAnimation, int duration = 3000, bool isLooping = false, int loopCount = 1)
+ {
+ ResetAnimations();
+ if (motionAnimation != null)
+ {
+ MotionAnimation = motionAnimation;
+ MotionAnimation?.Play();
+ }
+ else
+ {
+ Tizen.Log.Error(LogTag, "motionAnimation is null");
+ }
+ }
+
+ internal void PauseMotionAnimation()
+ {
+ MotionAnimation?.Pause();
+ }
+
+ internal void StopMotionAnimation()
+ {
+ MotionAnimation?.Stop();
+ }
+
+ internal void SetBlinkAnimation(Animation blinkerAnimation)
+ {
+ eyeBlinker?.Init(blinkerAnimation);
+ }
+
+ internal void StartEyeBlink()
+ {
+ eyeBlinker?.Play();
+ }
+
+ internal void PauseEyeBlink()
+ {
+ eyeBlinker?.Pause();
+ }
+
+
+ internal void StopEyeBlink()
+ {
+ eyeBlinker?.Stop();
+ }
+
+ internal void DestroyAnimations()
+ {
+ eyeBlinker?.Destroy();
+ }
+
+ private void ResetAnimations()
+ {
+ if (MotionAnimation != null)
+ {
+ MotionAnimation.Stop();
+ MotionAnimation.Dispose();
+ MotionAnimation = null;
+ }
+ }
+
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.ComponentModel;
+using Tizen.NUI.Scene3D;
+using Tizen.NUI;
+
+using static Tizen.AIAvatar.AIAvatar;
+
+namespace Tizen.AIAvatar
+{
+ /// <summary>
+ /// The Avatar class displays 3D avatars and provides easy access to their animations.
+ /// This class is a sub-class of the Model class which allows us to easily control the Avatar's animations.
+ /// Avatar also supports AR Emoji for humanoid-based 3D models.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class Avatar : Model
+ {
+ private AvatarProperties avatarProperties = new DefaultAvatarProperties();
+
+ private MotionPlayer motionPlayer;
+
+ /// <summary>
+ /// The AvatarProperties property gets or sets the AvatarProperties object containing various information about the Avatar.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AvatarProperties Properties
+ {
+ get => avatarProperties;
+ set
+ {
+ avatarProperties = value;
+ }
+ }
+
+ /// <summary>
+ /// Create an initialized AvatarModel.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Avatar() : base()
+ {
+ InitAvatar();
+ }
+
+ /// <summary>
+ /// Create an initialized AREmojiDefaultAvatar.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Avatar(AvatarInfo avatarInfo) : base(avatarInfo.ResourcePath)
+ {
+ InitAvatar();
+ }
+
+ /// <summary>
+ /// Create an initialized Avatar.
+ /// </summary>
+ /// <param name="avatarUrl">avatar file url.(e.g. glTF).</param>
+ /// <param name="resourceDirectoryUrl"> The url to derectory containing resources: binary, image etc.</param>
+ /// <remarks>
+ /// If resourceDirectoryUrl is empty, the parent directory url of avatarUrl is used for resource url.
+ ///
+ /// http://tizen.org/privilege/mediastorage for local files in media storage.
+ /// http://tizen.org/privilege/externalstorage for local files in external storage.
+ /// </remarks>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Avatar(string avatarUrl, string resourceDirectoryUrl = "") : base(avatarUrl, resourceDirectoryUrl)
+ {
+ InitAvatar();
+ }
+
+ /// <summary>
+ /// Copy constructor.
+ /// </summary>
+ /// <param name="avatar">Source object to copy.</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Avatar(Avatar avatar) : base(avatar)
+ {
+ InitAvatar();
+ }
+
+ #region Manage Animating
+
+ /// <summary>
+ /// Plays the specified avatar animation with an optional duration and loop count.
+ /// </summary>
+ /// <param name="animationInfo">The AnimationInfo object containing information about the desired avatar animation.</param>
+ /// <param name="duration">The duration of the animation in milliseconds (default is 3000).</param>
+ /// <param name="isLooping">A boolean indicating whether the animation should be looped or not.</param>
+ /// <param name="loopCount">The number of times to repeat the animation if it's set to loop.</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void PlayAnimation(AnimationInfo animationInfo, int duration = 3000, bool isLooping = false, int loopCount = 1)
+ {
+ if (animationInfo == null)
+ {
+ Tizen.Log.Error(LogTag, "animationInfo is null");
+ return;
+ }
+ if (animationInfo.MotionData == null)
+ {
+ Tizen.Log.Error(LogTag, "animationInfo.MotionData is null");
+ return;
+ }
+ motionAnimation = GenerateMotionDataAnimation(animationInfo.MotionData);
+ if (motionAnimation != null)
+ {
+ motionAnimation.Duration = duration;
+ motionAnimation.Looping = isLooping;
+ motionAnimation.LoopCount = loopCount;
+ motionAnimation.BlendPoint = 0.2f;
+ motionPlayer.PlayAnimation(motionAnimation);
+ }
+ else
+ {
+ Tizen.Log.Error(LogTag, "motionAnimation is null");
+ }
+ }
+
+ Animation motionAnimation;
+
+ /// <summary>
+ /// Plays the specified avatar animation with MotionData and an optional duration and loop count.
+ /// </summary>
+ /// <param name="motionData">The MotionData object containing information about the desired avatar animation.</param>
+ /// <param name="duration">The duration of the animation in milliseconds (default is 3000).</param>
+ /// <param name="isLooping">A boolean indicating whether the animation should be looped or not.</param>
+ /// <param name="loopCount">The number of times to repeat the animation if it's set to loop.</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void PlayAnimation(MotionData motionData, int duration = 3000, bool isLooping = false, int loopCount = 1)
+ {
+
+ if (motionData == null)
+ {
+ Tizen.Log.Error(LogTag, "motionData is null");
+ return;
+ }
+ var motionAnimation = GenerateMotionDataAnimation(motionData);
+ if (motionAnimation != null)
+ {
+ motionAnimation.Duration = duration;
+ motionAnimation.Looping = isLooping;
+ motionAnimation.LoopCount = loopCount;
+ motionAnimation.BlendPoint = 0.2f;
+ motionPlayer.PlayAnimation(motionAnimation);
+ }
+ else
+ {
+ Tizen.Log.Error(LogTag, "motionAnimation is null");
+ }
+ }
+
+ /// <summary>
+ /// Plays the specified avatar animation based on its index within the available animations and an optional duration and loop count.
+ /// </summary>
+ /// <param name="index">The zero-based index of the desired avatar animation within the list of available animations.</param>
+ /// <param name="duration">The duration of the animation in milliseconds (default is 3000).</param>
+ /// <param name="isLooping">A boolean indicating whether the animation should be looped or not.</param>
+ /// <param name="loopCount">The number of times to repeat the animation if it's set to loop.</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void PlayAnimation(int index, int duration = 3000, bool isLooping = false, int loopCount = 1)
+ {
+ //TODO by index
+ //var motionAnimation = GenerateMotionDataAnimation(animationInfoList[index].MotionData);
+ /*motionAnimation.Duration = duration;
+ motionAnimation.Looping = isLooping;
+ motionAnimation.LoopCount = loopCount;
+ motionAnimation.BlendPoint = 0.2f;
+
+ motionPlayer.PlayAnimation(motionAnimation);*/
+ }
+
+ /// <summary>
+ /// Pauses the currently playing avatar animation.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void PauseMotionAnimation()
+ {
+ motionPlayer.PauseMotionAnimation();
+ }
+
+ /// <summary>
+ /// Stops the currently playing avatar animation.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void StopMotionAnimation()
+ {
+ motionPlayer?.StopMotionAnimation();
+ }
+
+ /// <summary>
+ /// Starts the eye blink animation for the current avatar.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void StartEyeBlink()
+ {
+ motionPlayer?.StartEyeBlink();
+ }
+
+ /// <summary>
+ /// Pauses the eye blink animation for the current avatar.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void PauseEyeBlink()
+ {
+ motionPlayer?.PauseEyeBlink();
+ }
+
+ /// <summary>
+ /// Stops the eye blink animation for the current avatar.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void StopEyeBlink()
+ {
+ motionPlayer?.StopEyeBlink();
+ }
+ #endregion
+
+ private void InitAvatar()
+ {
+ motionPlayer = new MotionPlayer();
+ var eyeMotionData = CreateEyeBlinkMotionData(200);
+ if (eyeMotionData == null)
+ {
+ Tizen.Log.Info(LogTag, "Failed Loading eyeAnimation");
+ }
+
+ ResourcesLoaded += (s, e) =>
+ {
+ var eyeAnimation = GenerateMotionDataAnimation(eyeMotionData);
+ if (eyeAnimation != null)
+ {
+ motionPlayer.SetBlinkAnimation(eyeAnimation);
+ }
+ };
+ }
+
+ private MotionData CreateEyeBlinkMotionData(int ms)
+ {
+ var keyFrames = new KeyFrames();
+ keyFrames.Add(0.1f, 0.0f);
+ keyFrames.Add(0.5f, 1.0f);
+ keyFrames.Add(0.9f, 0.0f);
+
+ var headBlendShapeEyeLeft = new AvatarBlendShapeIndex(avatarProperties.NodeMapper, NodeType.HeadGeo, avatarProperties.BlendShapeMapper, BlendShapeType.EyeBlinkLeft);
+ var headBlendShapeEyeRight = new AvatarBlendShapeIndex(avatarProperties.NodeMapper, NodeType.HeadGeo, avatarProperties.BlendShapeMapper, BlendShapeType.EyeBlinkRight);
+ var eyelashBlendShapeEyeLeft = new AvatarBlendShapeIndex(avatarProperties.NodeMapper, NodeType.EyelashGeo, avatarProperties.BlendShapeMapper, BlendShapeType.EyeBlinkLeft);
+ var eyelashBlendShapeEyeRight = new AvatarBlendShapeIndex(avatarProperties.NodeMapper, NodeType.EyelashGeo, avatarProperties.BlendShapeMapper, BlendShapeType.EyeBlinkRight);
+
+ var motionData = new MotionData(ms);
+ motionData.Add(headBlendShapeEyeLeft, new MotionValue(keyFrames));
+ motionData.Add(headBlendShapeEyeRight, new MotionValue(keyFrames));
+ motionData.Add(eyelashBlendShapeEyeLeft, new MotionValue(keyFrames));
+ motionData.Add(eyelashBlendShapeEyeRight, new MotionValue(keyFrames));
+
+ return motionData;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.ComponentModel;
+
+using static Tizen.AIAvatar.AIAvatar;
+
+namespace Tizen.AIAvatar
+{
+ /// <summary>
+ /// The AvatarInfo class describes the properties of an Avatar object.
+ /// It includes information such as the name of the avatar, its thumbnail image, and associated resources.
+ /// This class helps users manage and organize their Avatar assets more effectively.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class AvatarInfo
+ {
+ /// <summary>
+ /// The Name property gets the name of the Avatar.
+ /// This value is read-only and cannot be modified directly.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string Name { get; private set; }
+
+ /// <summary>
+ /// The ThumbnailPath property gets the path to the thumbnail image representing the Avatar.
+ /// This value is read-only and cannot be modified directly.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string ThumbnailPath { get; private set; }
+
+ /// <summary>
+ /// The ResourcePath property gets the path to the resource files associated with the Avatar.
+ /// This value is intended for internal use only and should not be accessed by users directly.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ internal string ResourcePath { get; private set; }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ internal AvatarInfoOption avatarInfoOption { get; private set; }
+
+ /// <summary>
+ /// Initializes a new instance of the AvatarInfo class with the specified file path, name, and option.
+ /// If no option is provided, the default is AvatarInfoOption.Thumbnail.
+ /// </summary>
+ /// <param name="name">The name of the Avatar.</param>
+ /// <param name="path">The full path to the Avatar file.</param>
+ /// <param name="info">The option specifying what type of information should be loaded from the file (thumbnail or resource).</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AvatarInfo(string name, string path, AvatarInfoOption info = AvatarInfoOption.Thumbnail)
+ {
+ this.Name = name;
+ this.avatarInfoOption = info;
+
+ if (info == AvatarInfoOption.Thumbnail)
+ {
+ ThumbnailPath = path;
+ }
+ else
+ {
+ ResourcePath = path;
+ }
+ }
+
+ internal AvatarInfo(string directoryPath)
+ {
+ string path = ApplicationResourcePath + EmojiAvatarResourcePath;
+ Name = directoryPath.Substring(path.Length, directoryPath.Length - path.Length);
+ ResourcePath = $"{directoryPath}/{AIAvatar.ExternalModel}";
+ }
+ }
+
+ /// <summary>
+ /// The AvatarInfoOption enumeration defines the options that determine how AvatarInfo instances are displayed or managed.
+ /// Currently it has two values: Thumbnail and Resource.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public enum AvatarInfoOption
+ {
+
+ /// <summary>
+ /// Thumbnail indicates that the AvatarInfo instance should display or manipulate the thumbnail image of the Avatar.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Thumbnail = 0,
+ /// <summary>
+ /// Resource indicates that the AvatarInfo instance should display or manipulate the resource files associated with the Avatar.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Resource = 1,
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.ComponentModel;
+using Tizen.NUI.Scene3D;
+
+namespace Tizen.AIAvatar
+{
+
+ /// <summary>
+ /// The Avatar class contains an inner AvatarProperties class.
+ /// This class manages AvatarProperty information using the AvatarPropertyMapper class.
+ /// By default, it includes jointMapper, blendShapeMapper, and nodeMapper, which automatically generate Properties based on model information.
+ /// This structure enables users to work with Avatar properties in a more convenient way.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class AvatarProperties
+ {
+ private AvatarPropertyMapper jointMapper;
+ private AvatarPropertyMapper blendShapeMapper;
+ private AvatarPropertyMapper nodeMapper;
+
+ internal event EventHandler<AvatarProperties> PropertiesChanged;
+
+ /// <summary>
+ /// The JointMapper property gets or sets the AvatarPropertyMapper responsible for mapping joint information in the Avatar model.
+ /// When setting this property, any changes made will trigger the AvatarPropertiesChanged event if it has been subscribed to.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AvatarPropertyMapper JointMapper
+ {
+ get
+ {
+ return jointMapper;
+ }
+ set
+ {
+ jointMapper = value;
+ PropertiesChanged?.Invoke(jointMapper, this);
+ }
+ }
+
+ /// <summary>
+ /// The BlendShapeMapper property gets or sets the AvatarPropertyMapper responsible for managing blend shape information in the Avatar model.
+ /// When setting this property, any changes made will trigger the AvatarPropertiesChanged event if it has been subscribed to.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AvatarPropertyMapper BlendShapeMapper
+ {
+ get
+ {
+ return blendShapeMapper;
+ }
+ set
+ {
+ blendShapeMapper = value;
+ PropertiesChanged?.Invoke(blendShapeMapper, this);
+ }
+ }
+
+ /// <summary>
+ /// The NodeMapper property gets or sets the AvatarPropertyMapper responsible for managing node information in the Avatar model.
+ /// When setting this property, any changes made will trigger the AvatarPropertiesChanged event if it has been subscribed to.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AvatarPropertyMapper NodeMapper
+ {
+ get
+ {
+ return nodeMapper;
+ }
+ set
+ {
+ nodeMapper = value;
+ PropertiesChanged?.Invoke(nodeMapper, this);
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the AvatarProperties class with the specified joint mapper, blend shape mapper, and node mapper.
+ /// These mappers are used to map between the Avatar's underlying model data and its corresponding properties.
+ /// </summary>
+ /// <param name="jointMapper">The AvatarPropertyMapper for joints.</param>
+ /// <param name="blendShapeMapper">The AvatarPropertyMapper for blend shapes.</param>
+ /// <param name="nodeMapper">The AvatarPropertyMapper for nodes.</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AvatarProperties(AvatarPropertyMapper jointMapper, AvatarPropertyMapper blendShapeMapper, AvatarPropertyMapper nodeMapper)
+ {
+ JointMapper = new AvatarPropertyMapper(jointMapper);
+ BlendShapeMapper = new AvatarPropertyMapper(blendShapeMapper);
+ NodeMapper = new AvatarPropertyMapper(nodeMapper);
+ }
+
+ /// <summary>
+ /// This method generates a MotionIndex to be used in animations based on the NodeType and BlendShapeType using the model information of an Avatar.
+ /// </summary>
+ /// <param name="nodeType">Node type</param>
+ /// <param name="blendShapeType">Blend shape type</param>
+ /// <returns>The generated MotionIndex</returns>
+ public MotionIndex CreateBlendShapeMotionIndex(NodeType nodeType, BlendShapeType blendShapeType)
+ {
+ var motionIndex = new AvatarBlendShapeIndex(NodeMapper, nodeType, BlendShapeMapper, blendShapeType);
+ return motionIndex;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace Tizen.AIAvatar
+{
+ /// <summary>
+ /// The AvatarPropertyMapper class manages property mapping information for specific attributes of an Avatar model.
+ /// It primarily maps the names of AvatarProperty to their actual storage locations.
+ /// By doing so, developers can directly read or write the values of these properties using their respective names.
+ /// This approach provides consistency and convenience when working with Avatar models.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class AvatarPropertyMapper
+ {
+ /// <summary>
+ /// Mapper between index and property name
+ /// </summary>
+ private Dictionary<uint, string> mapper = null;
+
+ /// <summary>
+ /// The counter of index. It will be increased one when we register custom index.
+ /// </summary>
+ private uint customIndexCounter = 0;
+
+ /// <summary>
+ /// Get current mapper information.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Dictionary<uint, string> Mapper
+ {
+ get
+ {
+ return mapper;
+ }
+ }
+
+ /// <summary>
+ /// Indexer method. Allows accessing and setting the property names using array notation.
+ /// </summary>
+ /// <param name="index">The index of property what we want to set</param>
+ /// <returns>The index of property, or uint.MaxValue if not exist</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string this[uint index]
+ {
+ set
+ {
+ SetPropertyName(index, value);
+ }
+ get
+ {
+ return GetPropertyName(index);
+ }
+ }
+
+ /// <summary>
+ /// Create an initialized AvatarPropertyNameMapper.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AvatarPropertyMapper()
+ {
+ mapper = new Dictionary<uint, string>();
+ customIndexCounter = 0u;
+ }
+
+ /// <summary>
+ /// Copy constructor.
+ /// Creates a new instance of the AvatarPropertyMapper class by copying the contents of another existing AvatarPropertyMapper instance.
+ /// </summary>
+ /// <param name="source">The source AvatarPropertyMapper instance to be copied.</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AvatarPropertyMapper(AvatarPropertyMapper source)
+ {
+ if (source != null)
+ {
+ mapper = new Dictionary<uint, string>(source.Mapper);
+ customIndexCounter = source.customIndexCounter;
+ }
+ else
+ {
+ mapper = new Dictionary<uint, string>();
+ customIndexCounter = 0u;
+ }
+ }
+
+ /// <summary>
+ /// Registers a custom property by name and returns its index.
+ /// </summary>
+ /// <param name="name">The name of custom property</param>
+ /// <returns>The index of property matched with name.</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public uint RegisterCustomProperty(string name)
+ {
+ uint ret = GetPropertyIndexByName(name);
+ if (ret >= customIndexCounter)
+ {
+ ret = customIndexCounter++;
+ SetPropertyName(ret, name);
+ }
+ return ret;
+ }
+
+ /// <summary>
+ /// Returns the index of a property by its name.
+ /// </summary>
+ /// <param name="name">The name of property what we want to get index</param>
+ /// <returns>The index of property, or uint.MaxValue if not exist</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public uint GetPropertyIndexByName(string name)
+ {
+ // TODO : Do this without iteration
+ foreach (var pair in mapper)
+ {
+ if (pair.Value == name)
+ {
+ return pair.Key;
+ }
+ }
+ return uint.MaxValue;
+ }
+
+ /// <summary>
+ /// Sets the property name at the given index.
+ /// </summary>
+ /// <param name="index">The index of property what we want to set</param>
+ /// <param name="name">The name of property what we want to set</param>
+ /// <remark>
+ /// New property will be added if we use index that not exist in mapper.
+ /// </remark>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void SetPropertyName(uint index, string name)
+ {
+ mapper.TryAdd(index, name);
+ }
+
+ /// <summary>
+ /// Gets the property name at the given index.
+ /// </summary>
+ /// <param name="index">The index of property what we want to set</param>
+ /// <returns>The name of property, or null if not exist</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string GetPropertyName(uint index)
+ {
+ string ret = null;
+ mapper.TryGetValue(index, out ret);
+ return ret;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.ComponentModel;
+
+namespace Tizen.AIAvatar
+{
+ /// <summary>
+ /// The type of predefined blendshape. We can customize each type name by "TODO_mapper"
+ /// The basic names provided by AIAvatar to control the default avatar of AREmoji.
+ /// Contains the BlendShape information of AIAvatar.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public enum BlendShapeType
+ {
+ #region Left Eyes
+ /// <summary>
+ /// EyeBlinkLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyeBlinkLeft = 0,
+
+ /// <summary>
+ /// EyeSquintLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyeSquintLeft,
+
+ /// <summary>
+ /// EyeDownLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyeDownLeft,
+
+ /// <summary>
+ /// EyeInLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyeInLeft,
+
+ /// <summary>
+ /// EyeOpenLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyeOpenLeft,
+
+ /// <summary>
+ /// EyeOutLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyeOutLeft,
+
+ /// <summary>
+ /// EyeUpLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyeUpLeft,
+ #endregion
+
+ #region Right Eyes
+ /// <summary>
+ /// EyeBlinkRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyeBlinkRight,
+
+ /// <summary>
+ /// EyeSquintRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyeSquintRight,
+
+ /// <summary>
+ /// EyeDownRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyeDownRight,
+
+ /// <summary>
+ /// EyeInRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyeInRight,
+
+ /// <summary>
+ /// EyeOpenRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyeOpenRight,
+
+ /// <summary>
+ /// EyeOutRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyeOutRight,
+
+ /// <summary>
+ /// EyeUpRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyeUpRight,
+ #endregion
+
+ #region Mouth and Jaw
+ /// <summary>
+ /// JawForward blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ JawForward,
+
+ /// <summary>
+ /// JawOpen blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ JawOpen,
+
+ /// <summary>
+ /// JawChew blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ JawChew,
+
+ /// <summary>
+ /// JawLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ JawLeft,
+
+ /// <summary>
+ /// JawRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ JawRight,
+
+ /// <summary>
+ /// MouthLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ MouthLeft,
+
+ /// <summary>
+ /// MouthFrownLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ MouthFrownLeft,
+
+ /// <summary>
+ /// MouthSmileLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ MouthSmileLeft,
+
+ /// <summary>
+ /// MouthDimpleLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ MouthDimpleLeft,
+
+ /// <summary>
+ /// MouthRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ MouthRight,
+
+ /// <summary>
+ /// MouthFrownRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ MouthFrownRight,
+
+ /// <summary>
+ /// MouthSmileRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ MouthSmileRight,
+
+ /// <summary>
+ /// MouthDimpleRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ MouthDimpleRight,
+ #endregion
+
+ #region Lips
+ /// <summary>
+ /// LipsStretchLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ LipsStretchLeft,
+
+ /// <summary>
+ /// LipsStretchRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ LipsStretchRight,
+
+ /// <summary>
+ /// LipsUpperClose blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ LipsUpperClose,
+
+ /// <summary>
+ /// LipsLowerClose blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ LipsLowerClose,
+
+ /// <summary>
+ /// LipsUpperUp blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ LipsUpperUp,
+
+ /// <summary>
+ /// LipsLowerDown blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ LipsLowerDown,
+
+ /// <summary>
+ /// LipsUpperOpen blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ LipsUpperOpen,
+
+ /// <summary>
+ /// LipsLowerOpen blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ LipsLowerOpen,
+
+ /// <summary>
+ /// LipsFunnel blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ LipsFunnel,
+
+ /// <summary>
+ /// LipsPucker blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ LipsPucker,
+ #endregion
+
+ #region Eyebrows, Cheeks, and Chin
+ /// <summary>
+ /// BrowDownLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ BrowDownLeft,
+
+ /// <summary>
+ /// BrowDownRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ BrowDownRight,
+
+ /// <summary>
+ /// BrowUpCenter blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ BrowUpCenter,
+
+ /// <summary>
+ /// BrowUpLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ BrowUpLeft,
+
+ /// <summary>
+ /// BrowUpRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ BrowUpRight,
+
+ /// <summary>
+ /// CheekSquintLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ CheekSquintLeft,
+
+ /// <summary>
+ /// CheekSquintRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ CheekSquintRight,
+
+ /// <summary>
+ /// ChinLowerRaise blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ ChinLowerRaise,
+
+ /// <summary>
+ /// ChinUpperRaise blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ ChinUpperRaise,
+ #endregion
+
+ #region Tongue
+ /// <summary>
+ /// TongueOut blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ TongueOut,
+
+ /// <summary>
+ /// TongueUp blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ TongueUp,
+
+ /// <summary>
+ /// TongueDown blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ TongueDown,
+
+ /// <summary>
+ /// TongueLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ TongueLeft,
+
+ /// <summary>
+ /// TongueRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ TongueRight,
+ #endregion
+
+ #region ETC
+ /// <summary>
+ /// Sneer blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Sneer,
+
+ /// <summary>
+ /// Puff blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Puff,
+
+ /// <summary>
+ /// PuffLeft blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ PuffLeft,
+
+ /// <summary>
+ /// PuffRight blendshape
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ PuffRight,
+ #endregion
+ /// <summary>
+ /// Max value of default blendshape. It will be used when we determine the motion index is default or custom.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ DefaultBlendShapeMax,
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.ComponentModel;
+
+namespace Tizen.AIAvatar
+{
+ /// <summary>
+ /// The type of predefined skeleton joint. We can customize each type name by "TODO_mapper"
+ /// The basic names provided by AIAvatar to control the default avatar of AREmoji.
+ /// Contains the joint information of AIAvatar.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public enum JointType
+ {
+ #region Head
+ /// <summary>
+ /// Head joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Head = 0,
+
+ /// <summary>
+ /// Neck joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Neck,
+
+ /// <summary>
+ /// EyeLeft joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyeLeft,
+
+ /// <summary>
+ /// EyeRight joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyeRight,
+ #endregion
+
+ #region Left Upper Body
+ /// <summary>
+ /// ShoulderLeft joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ ShoulderLeft,
+
+ /// <summary>
+ /// ElbowLeft joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ ElbowLeft,
+
+ /// <summary>
+ /// WristLeft joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ WristLeft,
+ #endregion
+
+ #region Right Upper Body
+ /// <summary>
+ /// ShoulderRight joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ ShoulderRight,
+
+ /// <summary>
+ /// ElbowRight joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ ElbowRight,
+
+ /// <summary>
+ /// WristRight joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ WristRight,
+ #endregion
+
+ #region Left Lower Body
+ /// <summary>
+ /// HipLeft joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ HipLeft,
+
+ /// <summary>
+ /// KneeLeft joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ KneeLeft,
+
+ /// <summary>
+ /// AnkleLeft joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ AnkleLeft,
+
+ /// <summary>
+ /// ForeFootLeft joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ ForeFootLeft,
+ #endregion
+
+ #region Right Lower Body
+ /// <summary>
+ /// HipRight joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ HipRight,
+
+ /// <summary>
+ /// KneeRight joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ KneeRight,
+
+ /// <summary>
+ /// AnkleRight joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ AnkleRight,
+
+ /// <summary>
+ /// ForeFootRight joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ ForeFootRight,
+ #endregion
+
+ #region Left Hand Finger
+ /// <summary>
+ /// FingerThumb1Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerThumb1Left,
+
+ /// <summary>
+ /// FingerThumb2Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerThumb2Left,
+
+ /// <summary>
+ /// FingerThumb3Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerThumb3Left,
+
+ /// <summary>
+ /// FingerThumb4Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerThumb4Left,
+
+ /// <summary>
+ /// FingerIndex1Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerIndex1Left,
+
+ /// <summary>
+ /// FingerIndex2Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerIndex2Left,
+
+ /// <summary>
+ /// FingerIndex3Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerIndex3Left,
+
+ /// <summary>
+ /// FingerIndex4Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerIndex4Left,
+
+ /// <summary>
+ /// FingerMiddle1Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerMiddle1Left,
+
+ /// <summary>
+ /// FingerMiddle2Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerMiddle2Left,
+
+ /// <summary>
+ /// FingerMiddle3Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerMiddle3Left,
+
+ /// <summary>
+ /// FingerMiddle4Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerMiddle4Left,
+
+ /// <summary>
+ /// FingerRing1Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerRing1Left,
+
+ /// <summary>
+ /// FingerRing2Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerRing2Left,
+
+ /// <summary>
+ /// FingerRing3Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerRing3Left,
+
+ /// <summary>
+ /// FingerRing4Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerRing4Left,
+
+ /// <summary>
+ /// FingerPinky1Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerPinky1Left,
+
+ /// <summary>
+ /// FingerPinky2Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerPinky2Left,
+
+ /// <summary>
+ /// FingerPinky3Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerPinky3Left,
+
+ /// <summary>
+ /// FingerPinky4Left joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerPinky4Left,
+ #endregion
+
+ #region Right Hand Finger
+ /// <summary>
+ /// FingerThumb1Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerThumb1Right,
+
+ /// <summary>
+ /// FingerThumb2Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerThumb2Right,
+
+ /// <summary>
+ /// FingerThumb3Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerThumb3Right,
+
+ /// <summary>
+ /// FingerThumb4Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerThumb4Right,
+
+ /// <summary>
+ /// FingerIndex1Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerIndex1Right,
+
+ /// <summary>
+ /// FingerIndex2Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerIndex2Right,
+
+ /// <summary>
+ /// FingerIndex3Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerIndex3Right,
+
+ /// <summary>
+ /// FingerIndex4Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerIndex4Right,
+
+ /// <summary>
+ /// FingerMiddle1Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerMiddle1Right,
+
+ /// <summary>
+ /// FingerMiddle2Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerMiddle2Right,
+
+ /// <summary>
+ /// FingerMiddle3Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerMiddle3Right,
+
+ /// <summary>
+ /// FingerMiddle4Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerMiddle4Right,
+
+ /// <summary>
+ /// FingerRing1Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerRing1Right,
+
+ /// <summary>
+ /// FingerRing2Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerRing2Right,
+
+ /// <summary>
+ /// FingerRing3Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerRing3Right,
+
+ /// <summary>
+ /// FingerRing4Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerRing4Right,
+
+ /// <summary>
+ /// FingerPinky1Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerPinky1Right,
+
+ /// <summary>
+ /// FingerPinky2Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerPinky2Right,
+
+ /// <summary>
+ /// FingerPinky3Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerPinky3Right,
+
+ /// <summary>
+ /// FingerPinky4Right joint
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ FingerPinky4Right,
+ #endregion
+
+ /// <summary>
+ /// Max value of default joint. It will be used when we determine the motion index is default or custom.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ DefaultJointMax,
+ }
+
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.ComponentModel;
+
+namespace Tizen.AIAvatar
+{
+ /// <summary>
+ /// The type of predefined node. We can customize each type name by "TODO_mapper"
+ /// The basic names provided by AIAvatar to control the default avatar of AREmoji.
+ /// Contains the node information of AIAvatar.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public enum NodeType
+ {
+ /// <summary>
+ /// head geometry
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ HeadGeo,
+ /// <summary>
+ /// mouth geometry
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ MouthGeo,
+ /// <summary>
+ /// eyelash geometry
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ EyelashGeo
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using Newtonsoft.Json;
+using System.Net.Http;
+using System;
+using System.Threading.Tasks;
+
+namespace Tizen.AIAvatar
+{
+ internal class EmotionAnalyzer
+ {
+ private LipSyncController avatarTTS;
+ private IRestClient restClient;
+ private const string playgroundURL = "https://playground-api.sec.samsung.net";
+
+ internal EmotionAnalyzer()
+ {
+ }
+
+ internal void InitAvatarLLM(LipSyncController avatarTTS)
+ {
+ this.avatarTTS = avatarTTS;
+ // Setup RestClinet
+ var restClientFactory = new RestClientFactory();
+ restClient = restClientFactory.CreateClient(playgroundURL);
+ }
+
+ internal async Task StartTTSWithLLMAsync(string text, string token)
+ {
+ var bearerToken = token;
+ var jsonData = "{\"messages\": [{\"role\": \"user\", \"content\": \"" + text + "\"}]}";
+
+ try
+ {
+ var postResponse = await restClient.SendRequestAsync(HttpMethod.Post, "/api/v1/chat/completions", bearerToken, jsonData);
+ var responseData = JsonConvert.DeserializeObject<dynamic>(postResponse);
+ string content = responseData["response"]["content"];
+ Log.Info("Tizen.AIAvatar", content);
+
+ //TTS 호출
+ var voiceInfo = new VoiceInfo()
+ {
+ Language = "en_US",
+ Type = VoiceType.Female,
+ };
+
+ avatarTTS.PlayTTSAsync(content, voiceInfo, (o, e) =>
+ {
+
+ });
+
+ }
+ catch (Exception ex)
+ {
+ Log.Error("Tizen.AIAvatar", "에러 발생: " + ex.Message);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.ComponentModel;
+
+namespace Tizen.AIAvatar
+{
+ /// <summary>
+ /// Manages facial expression control for avatars based on input text sentiment analysis results.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class EmotionController
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EmotionController"/> class without an avatar reference.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public EmotionController()
+ {
+
+ }
+
+ /// <summary>
+ /// Initializes the EmotionController by setting up the necessary components for managing facial expressions in the avatar.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void Initialize()
+ {
+
+ }
+ /// <summary>
+ /// This method analyzes emotion the given text.
+ /// </summary>
+ /// <param name="text">The text to analyze</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void AnalizeEmotion(string text)
+ {
+
+ }
+
+ /// <summary>
+ /// This method starts playing the emotion facial.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void PlayEmotionFacial()
+ {
+
+ }
+
+ /// <summary>
+ /// This method pauses the emotion facial.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void PauseEmotionFacial()
+ {
+
+ }
+
+ /// <summary>
+ /// This method stops the emotion facial.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void StopEmotionFacial()
+ {
+
+ }
+ }
+}
/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
using System.Collections.Generic;
using System.IO;
+using System.ComponentModel;
using static Tizen.AIAvatar.AIAvatar;
namespace Tizen.AIAvatar
{
- internal static class AvatarExtension
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static class AvatarExtension
{
- internal static List<AvatarInfo> GetDefaultAvatarList()
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static List<AvatarInfo> GetDefaultAvatarList()
{
var list = new List<AvatarInfo>();
var avatarModelFolders = Directory.GetDirectories(ApplicationResourcePath + EmojiAvatarResourcePath);
/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+namespace Tizen.AIAvatar
+{
+ internal class Interop
+ {
+ internal static partial class Libraries
+ {
+ public const string Scene3D = "libdali2-csharp-binder-scene3d.so";
+ }
+
+ [global::System.Runtime.InteropServices.DllImport(Libraries.Scene3D, EntryPoint = "CSharp_Dali_SceneView_Property_AlphaMaskURL_get")]
+ public static extern int AlphaMaskURLGet();
+
+ [global::System.Runtime.InteropServices.DllImport(Libraries.Scene3D, EntryPoint = "CSharp_Dali_SceneView_Property_MaskContentScale_get")]
+ public static extern int MaskContentScaleGet();
+
+ [global::System.Runtime.InteropServices.DllImport(Libraries.Scene3D, EntryPoint = "CSharp_Dali_SceneView_Property_CropToMask_get")]
+ public static extern int CropToMaskGet();
+
+ }
+}
/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
*/
-using System;
-using System.Collections.Generic;
-using System.Text;
using Tizen.NUI.Scene3D;
+using System.ComponentModel;
namespace Tizen.AIAvatar
{
- internal static class SceneViewExtension
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static class SceneViewExtension
{
- internal static void SetAlphaMaskUrl(this SceneView sceneView, string url)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void SetAlphaMaskUrl(this SceneView sceneView, string url)
{
var setValue = new Tizen.NUI.PropertyValue(url);
sceneView.SetProperty(Interop.AlphaMaskURLGet(), setValue);
setValue.Dispose();
}
- internal static string GetAlphaMaskUrl(this SceneView sceneView)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static string GetAlphaMaskUrl(this SceneView sceneView)
{
var returnValue = "";
var invertYAxis = sceneView.GetProperty(Interop.AlphaMaskURLGet());
return returnValue;
}
- internal static void SetMaskContentScaleFactor(this SceneView sceneView, float factor)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void SetMaskContentScaleFactor(this SceneView sceneView, float factor)
{
var setValue = new Tizen.NUI.PropertyValue(factor);
sceneView.SetProperty(Interop.MaskContentScaleGet(), setValue);
setValue.Dispose();
}
- internal static float GetMaskContentScaleFactor(this SceneView sceneView)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static float GetMaskContentScaleFactor(this SceneView sceneView)
{
var returnValue = 0.0f;
var invertYAxis = sceneView.GetProperty(Interop.MaskContentScaleGet());
return returnValue;
}
- internal static void EnableCropToMask(this SceneView sceneView, bool enableCropToMask)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void EnableCropToMask(this SceneView sceneView, bool enableCropToMask)
{
var setValue = new Tizen.NUI.PropertyValue(enableCropToMask);
sceneView.SetProperty(Interop.CropToMaskGet(), setValue);
setValue.Dispose();
}
- internal static bool IsEnabledCropToMask(this SceneView sceneView)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static bool IsEnabledCropToMask(this SceneView sceneView)
{
bool returnValue = false;
var invertYAxis = sceneView.GetProperty(Interop.CropToMaskGet());
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using Tizen.Multimedia;
+
+namespace Tizen.AIAvatar
+{
+ internal static class AIAvatar
+ {
+ internal const string LogTag = "Tizen.AIAvatar";
+ internal static readonly string ApplicationResourcePath = "/usr/apps/org.tizen.default-avatar-resource/shared/res/";
+ internal static readonly string EmojiAvatarResourcePath = "/models/EmojiAvatar/";
+ internal static readonly string DefaultModelResourcePath = "/models/DefaultAvatar/";
+ internal static readonly string DefaultMotionResourcePath = "/animation/motion/";
+
+ internal static readonly string VisemeInfo = $"{ApplicationResourcePath}/viseme/emoji_viseme_info.json";
+ internal static readonly string DefaultModel = "DefaultAvatar.gltf";
+
+ internal static readonly string AREmojiDefaultAvatarPath = $"{ApplicationResourcePath}{DefaultModelResourcePath}{DefaultModel}";
+
+ internal static readonly string DefaultLowModelResourcePath = "/models/DefaultAvatar_Low/";
+ internal static readonly string ExternalModel = "model_external.gltf";
+ internal static readonly string AREmojiDefaultLowAvatarPath = $"{ApplicationResourcePath}{DefaultLowModelResourcePath}{ExternalModel}";
+
+ internal static AudioOptions DefaultAudioOptions = new AudioOptions(24000, AudioChannel.Mono, AudioSampleType.S16Le);
+ internal static AudioOptions CurrentAudioOptions = DefaultAudioOptions;
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.ComponentModel;
+using Tizen.NUI;
+using Tizen.NUI.Scene3D;
+
+namespace Tizen.AIAvatar
+{
+ /// <summary>
+ /// Specialized <see cref="BlendShapeIndex"/> to control avatar blend shape.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// AvatarBlendShapeIndex leftEyeBlink = new AvatarBlendShapeIndex(avatar.BlendShapeMapper, BlendShapeType.EyeBlinkLeft);
+ ///
+ /// // We can change the property later.
+ /// AVatarBlendShapeIndex rightEyeBlink = new AvatarJointTransformIndex(avatar.BlendShapeMapper);
+ /// rightEyeBlink.AvatarBlendShapeType = (uint)BlendShapeType.EyeBlinkRight;
+ /// </code>
+ /// </example>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ internal class AvatarBlendShapeIndex : BlendShapeIndex
+ {
+ internal AvatarPropertyMapper nameMapper = null;
+ internal uint blendShapeType;
+
+ internal AvatarBlendShapeIndex(AvatarPropertyMapper mapper) : base()
+ {
+ nameMapper = mapper;
+ }
+
+ internal AvatarBlendShapeIndex(AvatarPropertyMapper mapper, PropertyKey blendShapeId) : base(new PropertyKey(GetPropertyNameFromMapper(mapper, blendShapeId)))
+ {
+ nameMapper = mapper;
+ }
+
+ internal AvatarBlendShapeIndex(AvatarPropertyMapper nodeMapper, NodeType nodeType, PropertyKey blendShapeId)
+ : base(new PropertyKey(GetPropertyNameFromMapper(nodeMapper, (uint)nodeType)), blendShapeId)
+ {
+ }
+
+ internal AvatarBlendShapeIndex(AvatarPropertyMapper blendShapeMapper, BlendShapeType blendShapeType) : this(blendShapeMapper, (uint)blendShapeType)
+ {
+ }
+
+ internal AvatarBlendShapeIndex(AvatarPropertyMapper mapper, uint blendShapeType) : base(new PropertyKey(GetPropertyNameFromMapper(mapper, blendShapeType)))
+ {
+ nameMapper = mapper;
+ this.blendShapeType = blendShapeType;
+ }
+
+ internal AvatarBlendShapeIndex(AvatarPropertyMapper nodeMapper, NodeType nodeType, AvatarPropertyMapper blendShapeMapper, BlendShapeType blendShapeType)
+ : base(new PropertyKey(GetPropertyNameFromMapper(nodeMapper, (uint)nodeType)), new PropertyKey(GetPropertyNameFromMapper(blendShapeMapper, (uint)blendShapeType)))
+ {
+ nameMapper = blendShapeMapper;
+ this.blendShapeType = (uint)blendShapeType;
+ }
+
+ /// <summary>
+ /// Get the name of given index.
+ /// </summary>
+ /// <param name="mapper">Name mapper for given index</param>
+ /// <param name="id">Target ID what we want to get name</param>
+ /// <returns>Name, or null if invalid</returns>
+ private static string GetPropertyNameFromMapper(AvatarPropertyMapper mapper, PropertyKey id)
+ {
+ if (id == null)
+ {
+ return "";
+ }
+ if (id.Type == PropertyKey.KeyType.String)
+ {
+ return id.StringKey;
+ }
+
+ var str = mapper?.GetPropertyName((uint)id.IndexKey) ?? "";
+ return str;
+ }
+
+ /// <summary>
+ /// Get the name of given BlendShape.
+ /// </summary>
+ /// <param name="mapper">Name mapper for given index</param>
+ /// <param name="type">Type of joint what we want to get name</param>
+ /// <returns>Name, or null if invalid</returns>
+ private static string GetPropertyNameFromMapper(AvatarPropertyMapper mapper, uint type)
+ {
+ var str = mapper?.GetPropertyName(type) ?? "";
+ return str;
+ }
+
+ /// <summary>
+ /// TODO : Explain me
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AvatarPropertyMapper NameMapper
+ {
+ get
+ {
+ return nameMapper;
+ }
+ set
+ {
+ nameMapper = value;
+
+ using PropertyKey blendShapeId = new(GetPropertyNameFromMapper(nameMapper, blendShapeType));
+ base.BlendShapeId = blendShapeId;
+ }
+ }
+
+ /// <summary>
+ /// TODO : Explain me
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public uint AvatarBlendShapeType
+ {
+ get
+ {
+ return blendShapeType;
+ }
+ set
+ {
+ blendShapeType = value;
+
+ using PropertyKey blendShapeId = new(GetPropertyNameFromMapper(nameMapper, blendShapeType));
+ base.BlendShapeId = blendShapeId;
+ }
+ }
+
+ /// <summary>
+ /// Hijack property to control Avatar specified logic.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new PropertyKey BlendShapeId
+ {
+ get
+ {
+ return base.BlendShapeId;
+ }
+ set
+ {
+ if (value != null)
+ {
+ if (value.Type == PropertyKey.KeyType.Index)
+ {
+ blendShapeType = (uint)value.IndexKey;
+ }
+ using PropertyKey blendShapeId = new(GetPropertyNameFromMapper(nameMapper, value));
+ base.BlendShapeId = blendShapeId;
+ }
+ else
+ {
+ base.BlendShapeId = value;
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.ComponentModel;
+using Tizen.NUI;
+using Tizen.NUI.Scene3D;
+
+namespace Tizen.AIAvatar
+{
+ /// <summary>
+ /// Specialized <see cref="MotionTransformIndex"/> to control avatar joint transform.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// AvatarJointTransformIndex position = new AvatarJointTransformIndex(avatar.JointMapper, JointType.Head, MotionTransformIndex.TransformTypes.Position);
+ ///
+ /// // We can change the property later.
+ /// AvatarJointTransformIndex orientation = new AvatarJointTransformIndex(avatar.JointMapper);
+ /// orientation.AvatarJointType = (uint)JointType.Neck;
+ /// orientation.TransformType = MotionTransformIndex.TransformTypes.Orientation;
+ /// </code>
+ /// </example>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ internal class AvatarJointTransformIndex : MotionTransformIndex
+ {
+ internal AvatarPropertyMapper nameMapper = null;
+ internal uint jointType;
+
+ /// <summary>
+ /// Create an initialized avatar joint transform index.
+ /// </summary>
+ /// <param name="mapper">Name mapper for this index</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AvatarJointTransformIndex(AvatarPropertyMapper mapper) : base()
+ {
+ nameMapper = mapper;
+ }
+
+ /// <summary>
+ /// Create an initialized avatar joint transform index with input node id, and transform type.
+ /// </summary>
+ /// <param name="mapper">Name mapper for this index</param>
+ /// <param name="modelNodeId">Node ID for this motion index</param>
+ /// <param name="transformType">Transform property type for this motion index</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AvatarJointTransformIndex(AvatarPropertyMapper mapper, PropertyKey modelNodeId, TransformTypes transformType) : base(new PropertyKey(GetPropertyNameFromMapper(mapper, modelNodeId)), transformType)
+ {
+ nameMapper = mapper;
+ }
+
+ /// <summary>
+ /// Create an initialized avatar joint transform index with input node id, and transform type.
+ /// </summary>
+ /// <param name="mapper">Name mapper for this index</param>
+ /// <param name="jointType">Type of joint for this motion index</param>
+ /// <param name="transformType">Transform property type for this motion index</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ internal AvatarJointTransformIndex(AvatarPropertyMapper mapper, JointType jointType, TransformTypes transformType) : this(mapper, (uint)jointType, transformType)
+ {
+ }
+
+ /// <summary>
+ /// Create an initialized avatar joint transform index with input node id, and transform type.
+ /// </summary>
+ /// <param name="mapper">Name mapper for this index</param>
+ /// <param name="jointType">Type of joint for this motion index</param>
+ /// <param name="transformType">Transform property type for this motion index</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AvatarJointTransformIndex(AvatarPropertyMapper mapper, uint jointType, TransformTypes transformType) : base(new PropertyKey(GetPropertyNameFromMapper(mapper, jointType)), transformType)
+ {
+ nameMapper = mapper;
+ this.jointType = jointType;
+ }
+
+ private static string GetPropertyNameFromMapper(AvatarPropertyMapper mapper, PropertyKey id)
+ {
+ if (id == null)
+ {
+ return "";
+ }
+ if (id.Type == PropertyKey.KeyType.String)
+ {
+ return id.StringKey;
+ }
+ return mapper?.GetPropertyName((uint)id.IndexKey) ?? "";
+ }
+
+ private static string GetPropertyNameFromMapper(AvatarPropertyMapper mapper, uint jointType)
+ {
+ return mapper?.GetPropertyName(jointType) ?? "";
+ }
+
+ /// <summary>
+ /// TODO : Explain me
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AvatarPropertyMapper NameMapper
+ {
+ get
+ {
+ return nameMapper;
+ }
+ set
+ {
+ nameMapper = value;
+
+ using PropertyKey nodeId = new(GetPropertyNameFromMapper(nameMapper, jointType));
+ base.ModelNodeId = nodeId;
+ }
+ }
+
+ /// <summary>
+ /// TODO : Explain me
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public uint AvatarJointType
+ {
+ get
+ {
+ return jointType;
+ }
+ set
+ {
+ jointType = value;
+
+ using PropertyKey nodeId = new(GetPropertyNameFromMapper(nameMapper, jointType));
+ base.ModelNodeId = nodeId;
+ }
+ }
+
+ /// <summary>
+ /// Hijack property to control Avatar specified logic.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new PropertyKey ModelNodeId
+ {
+ get
+ {
+ return base.ModelNodeId;
+ }
+ set
+ {
+ if (value != null)
+ {
+ if (value.Type == PropertyKey.KeyType.Index)
+ {
+ jointType = (uint)value.IndexKey;
+ }
+ using PropertyKey nodeId = new(GetPropertyNameFromMapper(nameMapper, value));
+ base.ModelNodeId = nodeId;
+ }
+ else
+ {
+ base.ModelNodeId = value;
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.Collections.Generic;
+
+namespace Tizen.AIAvatar
+{
+ internal class DefaultAvatarProperties : AvatarProperties
+ {
+ private static AvatarPropertyMapper defaultJointMapper = new AvatarPropertyMapper();
+ private static AvatarPropertyMapper defaultBlendShapeNameMapper = new AvatarPropertyMapper();
+ private static AvatarPropertyMapper defaultNodeMapper = new AvatarPropertyMapper();
+
+ internal DefaultAvatarProperties() : base(defaultJointMapper, defaultBlendShapeNameMapper, defaultNodeMapper)
+ {
+ Initialize();
+ }
+
+ private void Initialize()
+ {
+ foreach (var indexNamePair in blendShapeList)
+ {
+ BlendShapeMapper.SetPropertyName((uint)indexNamePair.Item1, indexNamePair.Item2);
+ }
+
+ foreach (var indexNamePair in jointList)
+ {
+ JointMapper.SetPropertyName((uint)indexNamePair.Item1, indexNamePair.Item2);
+ }
+
+ foreach (var indexNamePair in nodeList)
+ {
+ NodeMapper.SetPropertyName((uint)indexNamePair.Item1, indexNamePair.Item2);
+ }
+
+ }
+
+ #region AR Emoji BlendShape name list
+ private static readonly List<(BlendShapeType, string)> blendShapeList = new List<(BlendShapeType, string)>()
+ {
+ (BlendShapeType.EyeBlinkLeft, "EyeBlink_Left"),
+ (BlendShapeType.EyeSquintLeft, "EyeSquint_Left"),
+ (BlendShapeType.EyeDownLeft, "EyeDown_Left"),
+ (BlendShapeType.EyeInLeft, "EyeIn_Left"),
+ (BlendShapeType.EyeOpenLeft, "EyeOpen_Left"),
+ (BlendShapeType.EyeOutLeft, "EyeOut_Left"),
+ (BlendShapeType.EyeUpLeft, "EyeUp_Left"),
+
+ (BlendShapeType.EyeBlinkRight, "EyeBlink_Right"),
+ (BlendShapeType.EyeSquintRight, "EyeSquint_Right"),
+ (BlendShapeType.EyeDownRight, "EyeDown_Right"),
+ (BlendShapeType.EyeInRight, "EyeIn_Right"),
+ (BlendShapeType.EyeOpenRight, "EyeOpen_Right"),
+ (BlendShapeType.EyeOutRight, "EyeOut_Right"),
+ (BlendShapeType.EyeUpRight, "EyeUp_Right"),
+
+ (BlendShapeType.JawForward, "JawFwd"),
+ (BlendShapeType.JawOpen, "JawOpen"),
+ (BlendShapeType.JawChew, "JawChew"),
+ (BlendShapeType.JawLeft, "JawLeft"),
+ (BlendShapeType.JawRight, "JawRight"),
+
+ (BlendShapeType.MouthLeft, "MouthLeft"),
+ (BlendShapeType.MouthFrownLeft, "MouthFrown_Left"),
+ (BlendShapeType.MouthSmileLeft, "MouthSmile_Left"),
+ (BlendShapeType.MouthDimpleLeft, "MouthDimple_Left"),
+
+ (BlendShapeType.MouthRight, "MouthRight"),
+ (BlendShapeType.MouthFrownRight, "MouthFrown_Right"),
+ (BlendShapeType.MouthSmileRight, "MouthSmile_Right"),
+ (BlendShapeType.MouthDimpleRight, "MouthDimple_Right"),
+
+ (BlendShapeType.LipsStretchLeft, "LipsStretch_Left"),
+ (BlendShapeType.LipsStretchRight, "LipsStretch_Right"),
+ (BlendShapeType.LipsUpperClose, "LipsUpperClose"),
+ (BlendShapeType.LipsLowerClose, "LipsLowerClose"),
+ (BlendShapeType.LipsUpperUp, "LipsUpperUp"),
+ (BlendShapeType.LipsLowerDown, "LipsLowerDown"),
+ (BlendShapeType.LipsUpperOpen, "LipsUpperOpen"),
+ (BlendShapeType.LipsLowerOpen, "LipsLowerOpen"),
+ (BlendShapeType.LipsFunnel, "LipsFunnel"),
+ (BlendShapeType.LipsPucker, "LipsPucker"),
+
+ (BlendShapeType.BrowDownLeft, "BrowsDown_Left"),
+ (BlendShapeType.BrowDownRight, "BrowsDown_Right"),
+ (BlendShapeType.BrowUpCenter, "BrowsUp_Center"),
+ (BlendShapeType.BrowUpLeft, "BrowsUp_Left"),
+ (BlendShapeType.BrowUpRight, "BrowsUp_Right"),
+ (BlendShapeType.CheekSquintLeft, "CheekSquint_Left"),
+ (BlendShapeType.CheekSquintRight, "CheekSquint_Right"),
+ (BlendShapeType.ChinLowerRaise, "ChinLowerRaise"),
+ (BlendShapeType.ChinUpperRaise, "ChinUpperRaise"),
+
+ (BlendShapeType.TongueOut, "Tongue_Out"),
+ (BlendShapeType.TongueUp, "Tongue_Up"),
+ (BlendShapeType.TongueDown, "Tongue_Down"),
+ (BlendShapeType.TongueLeft, "Tongue_Left"),
+ (BlendShapeType.TongueRight, "Tongue_Right"),
+
+ (BlendShapeType.Sneer, "Sneer"),
+ (BlendShapeType.Puff, "Puff"),
+ (BlendShapeType.PuffLeft, "Puff_Left"),
+ (BlendShapeType.PuffRight, "Puff_Right"),
+ };
+ #endregion
+
+ #region AR Emoji Joint name list
+ private static readonly List<(JointType, string)> jointList = new List<(JointType, string)>
+ {
+ (JointType.Head, "head_JNT"),
+ (JointType.Neck, "neck_JNT"),
+ (JointType.EyeLeft, "l_eye_JNT"),
+ (JointType.EyeRight, "r_eye_JNT"),
+
+ (JointType.ShoulderLeft, "l_arm_JNT"),
+ (JointType.ElbowLeft, "l_forearm_JNT"),
+ (JointType.WristLeft, "l_hand_JNT"),
+
+ (JointType.ShoulderRight, "r_arm_JNT"),
+ (JointType.ElbowRight, "r_forearm_JNT"),
+ (JointType.WristRight, "r_hand_JNT"),
+
+ (JointType.HipLeft, "l_upleg_JNT"),
+ (JointType.KneeLeft, "l_leg_JNT"),
+ (JointType.AnkleLeft, "l_foot_JNT"),
+ (JointType.ForeFootLeft, "l_toebase_JNT"),
+
+ (JointType.HipRight, "r_upleg_JNT"),
+ (JointType.KneeRight, "r_leg_JNT"),
+ (JointType.AnkleRight, "r_foot_JNT"),
+ (JointType.ForeFootRight, "r_toebase_JNT"),
+
+ (JointType.FingerThumb1Left, "l_handThumb1_JNT"),
+ (JointType.FingerThumb2Left, "l_handThumb2_JNT"),
+ (JointType.FingerThumb3Left, "l_handThumb3_JNT"),
+ (JointType.FingerThumb4Left, "l_handThumb4_JNT"),
+ (JointType.FingerIndex1Left, "l_handIndex1_JNT"),
+ (JointType.FingerIndex2Left, "l_handIndex2_JNT"),
+ (JointType.FingerIndex3Left, "l_handIndex3_JNT"),
+ (JointType.FingerIndex4Left, "l_handIndex4_JNT"),
+ (JointType.FingerMiddle1Left, "l_handMiddle1_JNT"),
+ (JointType.FingerMiddle2Left, "l_handMiddle2_JNT"),
+ (JointType.FingerMiddle3Left, "l_handMiddle3_JNT"),
+ (JointType.FingerMiddle4Left, "l_handMiddle4_JNT"),
+ (JointType.FingerRing1Left, "l_handRing1_JNT"),
+ (JointType.FingerRing2Left, "l_handRing2_JNT"),
+ (JointType.FingerRing3Left, "l_handRing3_JNT"),
+ (JointType.FingerRing4Left, "l_handRing4_JNT"),
+ (JointType.FingerPinky1Left, "l_handPinky1_JNT"),
+ (JointType.FingerPinky2Left, "l_handPinky2_JNT"),
+ (JointType.FingerPinky3Left, "l_handPinky3_JNT"),
+ (JointType.FingerPinky4Left, "l_handPinky4_JNT"),
+
+ (JointType.FingerThumb1Right, "r_handThumb1_JNT"),
+ (JointType.FingerThumb2Right, "r_handThumb2_JNT"),
+ (JointType.FingerThumb3Right, "r_handThumb3_JNT"),
+ (JointType.FingerThumb4Right, "r_handThumb4_JNT"),
+ (JointType.FingerIndex1Right, "r_handIndex1_JNT"),
+ (JointType.FingerIndex2Right, "r_handIndex2_JNT"),
+ (JointType.FingerIndex3Right, "r_handIndex3_JNT"),
+ (JointType.FingerIndex4Right, "r_handIndex4_JNT"),
+ (JointType.FingerMiddle1Right, "r_handMiddle1_JNT"),
+ (JointType.FingerMiddle2Right, "r_handMiddle2_JNT"),
+ (JointType.FingerMiddle3Right, "r_handMiddle3_JNT"),
+ (JointType.FingerMiddle4Right, "r_handMiddle4_JNT"),
+ (JointType.FingerRing1Right, "r_handRing1_JNT"),
+ (JointType.FingerRing2Right, "r_handRing2_JNT"),
+ (JointType.FingerRing3Right, "r_handRing3_JNT"),
+ (JointType.FingerRing4Right, "r_handRing4_JNT"),
+ (JointType.FingerPinky1Right, "r_handPinky1_JNT"),
+ (JointType.FingerPinky2Right, "r_handPinky2_JNT"),
+ (JointType.FingerPinky3Right, "r_handPinky3_JNT"),
+ (JointType.FingerPinky4Right, "r_handPinky4_JNT"),
+ };
+ #endregion
+
+ #region AR Emoji Joint name list
+ private static readonly List<(NodeType, string)> nodeList = new List<(NodeType, string)>
+ {
+ (NodeType.HeadGeo, "head_GEO"),
+ (NodeType.MouthGeo, "mouth_GEO"),
+ (NodeType.EyelashGeo, "eyelash_GEO"),
+ };
+ #endregion
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.IO;
+using Tizen.Security;
+using Newtonsoft.Json;
+using System.Collections.Generic;
+using static Tizen.AIAvatar.AIAvatar;
+
+namespace Tizen.AIAvatar
+{
+ internal class Utils
+ {
+ internal static void ConvertAudioToFloat(in byte[] audioBytes, out float[] audioFloat)
+ {
+ audioFloat = new float[audioBytes.Length / 2];
+
+ for (int i = 0, j = 0; i < audioBytes.Length; i += 2, j++)
+ {
+ short sample = BitConverter.ToInt16(audioBytes, i);
+ audioFloat[j] = sample / 32768.0f;
+ }
+ }
+
+ internal static byte[] ReadAllBytes(string path)
+ {
+ try
+ {
+ var bytes = File.ReadAllBytes(path);
+ return bytes;
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+
+ internal static void SaveFile(string path, string filename, byte[] array)
+ {
+ try
+ {
+ var file = new FileStream($"{path}/{filename}", FileMode.Create);
+ file.Write(array, 0, array.Length);
+ file.Close();
+ }
+ catch (Exception)
+ {
+ return;
+ }
+ }
+
+ internal static void CheckPrivilege(string privilege)
+ {
+ var result = PrivacyPrivilegeManager.CheckPermission(privilege);
+
+ switch (result)
+ {
+ case CheckResult.Allow:
+ Log.Info(LogTag, $"Privilege \"{privilege}\" : allowed.");
+ break;
+ case CheckResult.Deny:
+ Log.Info(LogTag, $"Privilege \"{privilege}\" : denied.");
+ /// Privilege can't be used
+ break;
+ case CheckResult.Ask:
+ /// Request permission to user
+ PrivacyPrivilegeManager.RequestPermission(privilege);
+ break;
+ }
+ }
+
+ internal static T ConvertJson<T>(string jsonString)
+ {
+ return JsonConvert.DeserializeObject<T>(jsonString);
+ }
+
+ internal int FindMaxValue<T>(List<T> list, Converter<T, int> projection)
+ {
+ if (list.Count == 0)
+ {
+ throw new InvalidOperationException("Empty list");
+ }
+ int maxValue = int.MinValue;
+ foreach (T item in list)
+ {
+ int value = projection(item);
+ if (value > maxValue)
+ {
+ maxValue = value;
+ }
+ }
+ return maxValue;
+ }
+ }
+}
+
+internal class SystemUtils
+{
+ internal static string GetFileName(string path)
+ {
+ return System.IO.Path.GetFileName(path);
+ }
+ internal static string GetFileNameWithoutExtension(string path)
+ {
+ return System.IO.Path.GetFileNameWithoutExtension(path);
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using Tizen.NUI;
+
+using static Tizen.AIAvatar.AIAvatar;
+
+namespace Tizen.AIAvatar
+{
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ internal class AsyncLipSyncer
+ {
+ private readonly uint AsyncPlayTime = 160;
+ private Queue<Animation> lipAnimations;
+ private Queue<byte[]> lipAudios;
+ private Timer asyncVowelTimer;
+
+ private bool isAsyncInit = false;
+ private bool isFinishAsyncLip = false;
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ internal AsyncLipSyncer()
+ {
+
+ }
+
+ internal int SampleRate { get; set; }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ internal bool IsAsyncInit { get=>isAsyncInit; set=>isAsyncInit = value; }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ internal void SetFinishAsyncLip(bool finished)
+ {
+ isFinishAsyncLip = finished;
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected new void OnLipAnimationFinished(object sender, EventArgs e)
+ {
+ if (!isAsyncInit)
+ {
+ //TODO : State is Stop
+ }
+ else
+ {
+ Tizen.Log.Error(LogTag, "OnLipAnimationFinished---------------c");
+ //Async State
+ if (isFinishAsyncLip && lipAnimations.Count == 0)
+ {
+ Tizen.Log.Error(LogTag, "Finish vowel lip sync");
+
+
+ //Audio Player is Stop, AudioPlayer.Stop();
+ //TODO : State is Stop
+ DestroyVowelTimer();
+ isAsyncInit = false;
+ }
+ }
+ }
+
+ internal void InitAsyncLipsync()
+ {
+ if (lipAnimations == null)
+ {
+ lipAnimations = new Queue<Animation>();
+ }
+ else
+ {
+ lipAnimations.Clear();
+ }
+
+ if (lipAudios == null)
+ {
+ lipAudios = new Queue<byte[]>();
+ }
+ else
+ {
+ lipAudios.Clear();
+ }
+ }
+
+ private void EnqueueVowels(byte[] buffer, bool deleteLast = false)
+ {
+ /*
+ var vowels = PredictVowels(buffer);
+ if (vowels != null)
+ {
+ vowelPools.Enqueue(vowels);
+ if (deleteLast)
+ {
+ var vowelList = new List<string>(vowels);
+ vowelList.RemoveAt(vowelList.Count - 1);
+ vowels = vowelList.ToArray();
+ }
+ }*/
+ }
+
+ internal Animation CreateAsyncLipAnimation(byte[] buffer, int sampleRate)
+ {
+ EnqueueVowels(buffer, false);
+ return null;
+ //return CreateLipAnimationByVowelsQueue(sampleRate);
+ }
+
+ internal void EnqueueAnimation(byte[] recordBuffer, int sampleRate, int audioLength)
+ {
+ var createdAni = CreateAsyncLipAnimation(recordBuffer, sampleRate);
+ if (createdAni != null)
+ {
+ lipAnimations.Enqueue(createdAni);
+ }
+
+ //Use Audio Full File
+ ///var createdAni = avatarLipSyncer.CreateLipAnimation(recordBuffer, sampleRate);
+ //lipAnimations.Enqueue(createdAni);
+
+ var currentAudioBuffer = new byte[audioLength];
+ Buffer.BlockCopy(recordBuffer, 0, currentAudioBuffer, 0, audioLength);
+
+ lipAudios.Enqueue(currentAudioBuffer);
+ }
+
+ internal bool PlayAsyncLip(int sampleRate, bool isFinishAsyncLip)
+ {
+ try
+ {
+ if (lipAudios.Count <= 0 && lipAnimations.Count <= 0)
+ {
+ Tizen.Log.Info(LogTag, "Return lipaudio 0");
+ if (isFinishAsyncLip)
+ {
+ Tizen.Log.Info(LogTag, "Finish Async lipsync");
+ return false;
+ }
+ return true;
+ }
+ Tizen.Log.Info(LogTag, "Async timer tick lipAudios : " + lipAudios.Count);
+ Tizen.Log.Info(LogTag, "Async timer tick lipAnimations : " + lipAnimations.Count);
+
+ var lipAnimation = lipAnimations.Dequeue();
+ lipAnimation.Finished += OnLipAnimationFinished;
+
+ //ResetLipAnimation(lipAnimation);
+ //PlayLipAnimation();
+ var audioBuffer = lipAudios.Dequeue();
+
+ //Audio Playe rAsync Play
+ //AudioPlayer.PlayAsync(audioBuffer, sampleRate);
+ return true;
+
+ }
+ catch (Exception ex)
+ {
+ Log.Error(LogTag, $"---Log Tick : {ex.StackTrace}");
+
+ return false;
+ }
+ }
+
+ internal void StartAsyncLipPlayTimer()
+ {
+ if (asyncVowelTimer == null)
+ {
+ Tizen.Log.Info(LogTag, "Start Async");
+ asyncVowelTimer = new Timer(AsyncPlayTime);
+ asyncVowelTimer.Tick += OnAsyncVowelTick;
+ asyncVowelTimer.Start();
+ }
+ return;
+ }
+
+ private void DestroyVowelTimer()
+ {
+ if (asyncVowelTimer != null)
+ {
+
+ asyncVowelTimer.Tick -= OnAsyncVowelTick;
+ asyncVowelTimer.Stop();
+ asyncVowelTimer.Dispose();
+ asyncVowelTimer = null;
+ }
+ }
+
+ private bool OnAsyncVowelTick(object source, Tizen.NUI.Timer.TickEventArgs e)
+ {
+ return PlayAsyncLip(SampleRate, isFinishAsyncLip);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.Collections.Generic;
+using System.ComponentModel;
+using System;
+using Tizen.Uix.Tts;
+
+using static Tizen.AIAvatar.AIAvatar;
+using Tizen.Multimedia;
+using Tizen.NUI.Scene3D;
+using Tizen.NUI;
+
+namespace Tizen.AIAvatar
+{
+ /// <summary>
+ /// A controller class used to manage lip sync functionality for avatars.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class LipSyncController : IDisposable
+ {
+ private TTSController ttsController;
+ private AudioRecorder audioRecorder;
+ private LipSyncer lipSyncer;
+
+ private Avatar avatar;
+
+ private event EventHandler ttsReadyFinished;
+ private readonly Object ttsReadyFinishedLock = new Object();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LipSyncController"/> class.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public LipSyncController(Avatar avatar)
+ {
+ this.avatar = avatar;
+
+ audioRecorder = new AudioRecorder();
+ lipSyncer = new LipSyncer();
+
+ lipSyncer.CreatedKeyFrameAnimation += OnCreatedKeyFrameAnimation;
+ }
+
+ private Animation CreateLipAnimation(AnimationKeyFrame animationKeyFrames, bool isMic = false)
+ {
+ var animationTime = (int)(animationKeyFrames.AnimationTime * 1000f);
+ var lipAnimation = new Animation(animationTime);
+
+ var motionData = new MotionData(animationTime);
+ for (var i = 0; i < animationKeyFrames.NodeNames.Length; i++)
+ {
+ var nodeName = animationKeyFrames.NodeNames[i];
+ for (var j = 0; j < animationKeyFrames.BlendShapeCounts[i]; j++)
+ {
+ var blendShapeIndex = new BlendShapeIndex(new PropertyKey(nodeName), new PropertyKey(j));
+ var keyFrameList = animationKeyFrames.GetKeyFrames(nodeName, j);
+ if (keyFrameList.Count == 0)
+ {
+ continue;
+ }
+
+ var keyFrames = new KeyFrames();
+ CreateKeyTimeFrames(ref keyFrames, keyFrameList, isMic);
+
+ motionData.Add(blendShapeIndex, new MotionValue(keyFrames));
+ lipAnimation = avatar.GenerateMotionDataAnimation(motionData);
+ }
+ }
+ return lipAnimation;
+ }
+
+ private KeyFrames CreateKeyTimeFrames(ref KeyFrames keyFrames, List<KeyFrame> keyFrameList, bool isMic = false)
+ {
+ foreach (var key in keyFrameList)
+ {
+ keyFrames.Add(key.time, key.value);
+ }
+ if (!isMic) keyFrames.Add(1.0f, 0.0f);
+
+ return keyFrames;
+ }
+
+ private Animation OnCreatedKeyFrameAnimation(AnimationKeyFrame animationKeyFrame, bool isMic)
+ {
+ if (animationKeyFrame == null)
+ {
+ Tizen.Log.Error(LogTag, "animtionKeyFrame is null");
+ }
+ var lipAnimation = CreateLipAnimation(animationKeyFrame, isMic);
+ return lipAnimation;
+ }
+
+ /// <summary>
+ /// Occurs when text-to-speech synthesis has finished and the audio data is ready to play.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler TTSReadyFinished
+ {
+ add
+ {
+ lock (ttsReadyFinishedLock)
+ {
+ ttsReadyFinished += value;
+ }
+
+ }
+
+ remove
+ {
+ lock (ttsReadyFinishedLock)
+ {
+ if (ttsReadyFinished == null)
+ {
+ Log.Error(LogTag, "ttsReadyFinished is null");
+ return;
+ }
+ ttsReadyFinished -= value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the current Text-To-Speech client associated with the Lip Sync Controller.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public TtsClient CurrentTTSClient => ttsController?.TtsHandle;
+
+ /// <summary>
+ /// Initializes the Text-To-Speech system for use with the Lip Sync Controller.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void InitializeTTS()
+ {
+ if (ttsController == null)
+ {
+ try
+ {
+ ttsController = new TTSController();
+ //TODO : LipSync Event Connect
+ ttsController.PreparedSyncText += OnPreparedSyncText;
+ ttsController.StoppedTTS += OnStoppedTTS;
+ ttsController.UpdatedBuffer += OnUpdatedBuffer;
+ }
+ catch (Exception e)
+ {
+ Log.Error(LogTag, $"error :{e.Message}");
+ Log.Error(LogTag, $"{e.StackTrace}");
+ }
+ }
+ }
+
+ private void OnUpdatedBuffer(object sender, TTSControllerEventArgs e)
+ {
+ throw new NotImplementedException();
+ if (lipSyncer != null)
+ {
+ Log.Error(LogTag, "OnTTSBufferChanged");
+ /*
+ lipSyncer.EnqueueAnimation(recordBuffer, sampleRate, audioLength);
+ if (!isAsyncLipStarting)
+ {
+ lipSyncer.StartAsyncLipPlayTimer();
+ isAsyncLipStarting = true;
+ }*/
+ }
+ else
+ {
+ Log.Error(LogTag, "avatarLipSyncer is null");
+ }
+ }
+
+ private void OnStoppedTTS(object sender, TTSControllerEventArgs e)
+ {
+ lipSyncer.Stop();
+ }
+
+ private void OnPreparedSyncText(object sender, TTSControllerEventArgs e)
+ {
+ var data = e.AudioData;
+ var sampleRate = e.SampleRate;
+
+ // Play Lipsync Animation by Audio
+ lipSyncer.PlayAudio(data, sampleRate);
+ }
+
+ /// <summary>
+ /// Deinitializes the Text-To-Speech system for use with the Lip Sync Controller.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void DeInitializeTTS()
+ {
+ if (ttsController != null)
+ {
+ try
+ {
+ ttsController.DeinitTts();
+ ttsController = null;
+ }
+ catch (Exception e)
+ {
+ Log.Error(LogTag, $"error :{e.Message}");
+ Log.Error(LogTag, $"{e.StackTrace}");
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns a list of supported voices for the Text-To-Speech system.
+ /// </summary>
+ /// <returns></returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public List<VoiceInfo> GetSupportedVoices()
+ {
+ return ttsController.GetSupportedVoices();
+ }
+
+ /// <summary>
+ /// Plays the given audio with LipSync and returns whether it was successful or not.
+ /// If the lip syncer is null, logs an error and returns false.
+ /// The lipSyncer plays the audio using the provided byte array and sample rate.
+ /// </summary>
+ /// <param name="audio">The audio data to be played in a byte array format.</param>
+ /// <param name="sampleRate">The sampling rate of the audio data, default value is 24000 Hz.</param>
+ public bool PlayLipSync(byte[] audio, int sampleRate = 24000)
+ {
+ if (lipSyncer == null)
+ {
+ Log.Error(LogTag, "lipSyncer is null");
+ return false;
+ }
+ lipSyncer.PlayAudio(audio, sampleRate);
+
+ return true;
+ }
+
+ /// <summary>
+ /// Plays the given audio with LipSync and returns whether it was successful or not.
+ /// If the lip syncer is null, logs an error and returns false.
+ /// The lipSyncer plays the audio using the provided byte array and sample rate.
+ /// </summary>
+ /// <param name="audio">The audio data to be played in a byte array format.</param>
+ /// <param name="sampleRate">The sampling rate of the audio data, default value is 16000 Hz.</param>
+ public bool PlayLipSync(string path, int sampleRate = 24000)
+ {
+ var audio = Utils.ReadAllBytes(path);
+ if (audio == null)
+ {
+ Log.Error(LogTag, "audio data is null");
+ return false;
+ }
+ lipSyncer.PlayAudio(audio, sampleRate);
+
+ return true;
+ }
+
+ /// <summary>
+ /// Prepares the Text-To-Speech engine to synthesize speech from the provided text and voice info.
+ /// </summary>
+ /// <param name="text">The text to convert into speech.</param>
+ /// <param name="voiceInfo">The voice info to use for converting the text into speech.</param>
+ /// <param name="ttsReadyFinishedCallback">An optional callback that will be invoked when the TTS process is complete.</param>
+ /// <returns><c>True</c> if preparation was successful, otherwise <c>False</c>.</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool PrepareTTS(string text, VoiceInfo voiceInfo, EventHandler ttsReadyFinishedCallback = null)
+ {
+ if (ttsController == null || ttsController.TtsHandle == null)
+ {
+ Log.Error(LogTag, "tts is null");
+ return false;
+ }
+
+
+ if (!ttsController.IsSupportedVoice(voiceInfo))
+ {
+ Log.Info(LogTag, $"{voiceInfo.Language} & {voiceInfo.Type} is not supported");
+ return false;
+ }
+
+ Log.Info(LogTag, "Current TTS State :" + ttsController.TtsHandle.CurrentState);
+ if (ttsController.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
+ {
+ Log.Info(LogTag, "TTS is not ready");
+ return false;
+ }
+
+ try
+ {
+ ttsController.AddText(text, voiceInfo);
+ ttsController.Prepare(ttsReadyFinishedCallback);
+ }
+ catch (Exception e)
+ {
+ Log.Error(LogTag, $"error :{e.Message}");
+ Log.Error(LogTag, $"{e.StackTrace}");
+ return false;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Prepares the Text-To-Speech engine to synthesize speech from the provided text and language code.
+ /// </summary>
+ /// <param name="text">The text to convert into speech.</param>
+ /// <param name="lang">The two-letter ISO 639-1 language code to use for converting the text into speech (default is Korean - ko_KR).</param>
+ /// <param name="ttsReadyFinishedCallback">An optional callback that will be invoked when the TTS process is complete.</param>
+ /// <returns><c>True</c> if preparation was successful, otherwise <c>False</c>.</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool PrepareTTS(string text, string lang = "ko_KR", EventHandler ttsReadyFinishedCallback = null)
+ {
+ if (ttsController == null || ttsController.TtsHandle == null)
+ {
+ Log.Error(LogTag, "tts is null");
+ return false;
+ }
+
+ if (!ttsController.IsSupportedVoice(lang))
+ {
+ Log.Error(LogTag, $"{lang} is not supported");
+ return false;
+ }
+
+ Log.Info(LogTag, "Current TTS State :" + ttsController.TtsHandle.CurrentState);
+ if (ttsController.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
+ {
+ Log.Error(LogTag, "TTS is not ready");
+ return false;
+ }
+ try
+ {
+ ttsController.AddText(text, lang);
+ ttsController.Prepare(ttsReadyFinishedCallback);
+ }
+ catch (Exception e)
+ {
+ Log.Error(LogTag, $"error :{e.Message}");
+ Log.Error(LogTag, $"{e.StackTrace}");
+ return false;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Plays the previously prepared Text-To-Speech audio data.
+ /// </summary>
+ /// <returns><c>True</c> if playback started successfully, otherwise <c>False</c>.</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool PlayPreparedTTS()
+ {
+ if (ttsController == null || ttsController.TtsHandle == null)
+ {
+ Log.Error(LogTag, "tts is null");
+ return false;
+ }
+
+ return ttsController.PlayPreparedText();
+ }
+
+ /// <summary>
+ /// Converts the given text into speech using the specified voice info and plays the resulting audio immediately.
+ /// </summary>
+ /// <param name="text">The text to convert into speech.</param>
+ /// <param name="voiceInfo">The voice info to use for converting the text into speech.</param>
+ /// <returns><c>True</c> if playback started successfully, otherwise <c>False</c>.</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool PlayTTS(string text, VoiceInfo voiceInfo)
+ {
+ if (ttsController == null || ttsController.TtsHandle == null)
+ {
+ Log.Error(LogTag, "tts is null");
+ return false;
+ }
+
+ if (!ttsController.IsSupportedVoice(voiceInfo))
+ {
+ Log.Info(LogTag, $"{voiceInfo.Language} & {voiceInfo.Type} is not supported");
+ return false;
+ }
+
+ Log.Info(LogTag, "Current TTS State :" + ttsController.TtsHandle.CurrentState);
+ if (ttsController.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
+ {
+ Log.Info(LogTag, "TTS is not ready");
+ return false;
+ }
+
+ try
+ {
+ ttsController.AddText(text, voiceInfo);
+ ttsController.Play();
+ }
+ catch (Exception e)
+ {
+ Log.Error(LogTag, $"error :{e.Message}");
+ Log.Error(LogTag, $"{e.StackTrace}");
+ return false;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Converts the given text into speech using the specified language code and plays the resulting audio immediately.
+ /// </summary>
+ /// <param name="text">The text to convert into speech.</param>
+ /// <param name="lang">The two-letter ISO 639-1 language code to use for converting the text into speech (default is Korean - ko_KR).</param>
+ /// <returns><c>True</c> if playback started successfully, otherwise <c>False</c>.</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool PlayTTS(string text, string lang = "ko_KR")
+ {
+ if (ttsController == null || ttsController.TtsHandle == null)
+ {
+ Log.Error(LogTag, "tts is null");
+ return false;
+ }
+
+ if (!ttsController.IsSupportedVoice(lang))
+ {
+ Log.Error(LogTag, $"{lang} is not supported");
+ return false;
+ }
+
+ Log.Info(LogTag, "Current TTS State :" + ttsController.TtsHandle.CurrentState);
+ if (ttsController.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
+ {
+ Log.Error(LogTag, "TTS is not ready");
+ return false;
+ }
+ try
+ {
+ ttsController.AddText(text, lang);
+ ttsController.Play();
+ }
+ catch (Exception e)
+ {
+ Log.Error(LogTag, $"error :{e.Message}");
+ Log.Error(LogTag, $"{e.StackTrace}");
+ return false;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Asynchronously converts the given text into speech using the specified voice info and plays the resulting audio once it's ready.
+ /// </summary>
+ /// <param name="text">The text to convert into speech.</param>
+ /// <param name="voiceInfo">The voice info to use for converting the text into speech.</param>
+ /// <param name="ttsReadyFinishedCallback">An optional callback that will be invoked when the TTS process is complete.</param>
+ /// <returns><c>True</c> if asynchronous conversion was initiated successfully, otherwise <c>False</c>.</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool PlayTTSAsync(string text, VoiceInfo voiceInfo, EventHandler ttsReadyFinishedCallback = null)
+ {
+ if (ttsController == null || ttsController.TtsHandle == null)
+ {
+ Log.Error(LogTag, "tts is null");
+ return false;
+ }
+
+ if (!ttsController.IsSupportedVoice(voiceInfo))
+ {
+ Log.Info(LogTag, $"{voiceInfo.Language} & {voiceInfo.Type} is not supported");
+ return false;
+ }
+
+ Log.Info(LogTag, "Current TTS State :" + ttsController.TtsHandle.CurrentState);
+ if (ttsController.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
+ {
+ Log.Info(LogTag, "TTS is not ready");
+ return false;
+ }
+
+ try
+ {
+ ttsController.AddText(text, voiceInfo);
+ ttsController.PlayAsync(ttsReadyFinishedCallback);
+ }
+ catch (Exception e)
+ {
+ Log.Error(LogTag, $"error :{e.Message}");
+ Log.Error(LogTag, $"{e.StackTrace}");
+ return false;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Asynchronously converts the given text into speech using the specified language code and plays the resulting audio once it's ready.
+ /// </summary>
+ /// <param name="text">The text to convert into speech.</param>
+ /// <param name="lang">The two-letter ISO 639-1 language code to use for converting the text into speech (default is Korean - ko_KR).</param>
+ /// <param name="ttsReadyFinishedCallback">An optional callback that will be invoked when the TTS process is complete.</param>
+ /// <returns><c>True</c> if asynchronous conversion was initiated successfully, otherwise <c>False</c>.</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool PlayTTS(string text, string lang = "ko_KR", EventHandler ttsReadyFinishedCallback = null)
+ {
+ if (ttsController == null || ttsController.TtsHandle == null)
+ {
+ Log.Error(LogTag, "tts is null");
+ return false;
+ }
+
+ if (!ttsController.IsSupportedVoice(lang))
+ {
+ Log.Error(LogTag, $"{lang} is not supported");
+ return false;
+ }
+
+ Log.Info(LogTag, "Current TTS State :" + ttsController.TtsHandle.CurrentState);
+ if (ttsController.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
+ {
+ Log.Error(LogTag, "TTS is not ready");
+ return false;
+ }
+ try
+ {
+ ttsController.AddText(text, lang);
+ ttsController.PlayAsync(ttsReadyFinishedCallback);
+ }
+ catch (Exception e)
+ {
+ Log.Error(LogTag, $"error :{e.Message}");
+ Log.Error(LogTag, $"{e.StackTrace}");
+ return false;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Pauses the currently playing Text-To-Speech audio.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void PauseTTS()
+ {
+ if (ttsController == null || ttsController.TtsHandle == null)
+ {
+ Log.Error(LogTag, "tts is null");
+ return;
+ }
+
+ try
+ {
+ Log.Info(LogTag, "Current TTS State :" + ttsController.TtsHandle.CurrentState);
+ ttsController?.Pause();
+ }
+ catch (Exception e)
+ {
+ Log.Error(LogTag, $"error :{e.Message}");
+ Log.Error(LogTag, $"{e.StackTrace}");
+ }
+ }
+
+ /// <summary>
+ /// Stops the currently playing Text-To-Speech audio.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void StopTTS()
+ {
+ if (ttsController == null || ttsController.TtsHandle == null)
+ {
+ Log.Error(LogTag, "tts is null");
+ return;
+ }
+
+ try
+ {
+ Log.Info(LogTag, "Current TTS State :" + ttsController.TtsHandle.CurrentState);
+ ttsController?.Stop();
+ }
+ catch (Exception e)
+ {
+ Log.Error(LogTag, $"error :{e.Message}");
+ Log.Error(LogTag, $"{e.StackTrace}");
+ }
+ }
+
+ public void InitializeMic()
+ {
+ if (audioRecorder == null)
+ {
+ Tizen.Log.Error(LogTag, "audio record is null");
+ return;
+ }
+ audioRecorder.InitializeMic(lipSyncer, 160);
+ }
+
+ public void DeinitializeMic()
+ {
+ if (audioRecorder == null)
+ {
+ Tizen.Log.Error(LogTag, "audio record is null");
+ return;
+ }
+ audioRecorder.StartRecording();
+ }
+
+ public void StartMic()
+ {
+ if (audioRecorder == null)
+ {
+ Tizen.Log.Error(LogTag, "audio record is null");
+ return;
+ }
+ audioRecorder.StartRecording();
+ }
+
+ public void StopMic()
+ {
+ if (audioRecorder == null)
+ {
+ Tizen.Log.Error(LogTag, "audio record is null");
+ return;
+ }
+ audioRecorder.StopRecording();
+ }
+
+ public void PauseMic()
+ {
+ if (audioRecorder == null)
+ {
+ Tizen.Log.Error(LogTag, "audio record is null");
+ return;
+ }
+ audioRecorder.PauseRecording();
+ }
+
+ public void ResumeMic()
+ {
+ if(audioRecorder == null)
+ {
+ Tizen.Log.Error(LogTag, "audio record is null");
+ return;
+ }
+ audioRecorder.ResumeRecording();
+ }
+
+ /// <summary>
+ /// Releases unmanaged resources used by this instance.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void Dispose()
+ {
+ if (ttsController != null)
+ {
+ ttsController.Dispose();
+ ttsController = null;
+ }
+
+ if (audioRecorder != null)
+ {
+ audioRecorder.Dispose();
+ audioRecorder = null;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Collections.Generic;
+using Tizen.NUI;
+using Tizen.NUI.Scene3D;
+using static Tizen.AIAvatar.AIAvatar;
+
+namespace Tizen.AIAvatar
+{
+ internal class LipSyncer
+ {
+ private Animation lipAnimation = null;
+
+ private VowelConverter vowelConverter;
+ private AudioPlayer audioPlayer;
+ private event Func<AnimationKeyFrame, bool, Animation> TheEvent;
+
+ internal Func<AnimationKeyFrame, bool, Animation> CreatedKeyFrameAnimation;
+
+ //Mic
+ private Queue<string[]> vowelPools = new Queue<string[]>();
+ private string prevVowel = "sil";
+
+ internal LipSyncer()
+ {
+ vowelConverter = new VowelConverter();
+ audioPlayer = new AudioPlayer();
+ }
+
+ internal AudioPlayer AudioPlayer { get { return audioPlayer; } }
+
+ public void SetLipAnimation(Animation lipAnimation)
+ {
+ this.lipAnimation = lipAnimation;
+ }
+
+ public void PlayAudio(byte[] audio, int sampleRate)
+ {
+ if (audio != null)
+ {
+ PlayLipSync(audio, sampleRate);
+ }
+ }
+
+ public void Stop()
+ {
+ StopLipSync();
+ }
+
+ public void Pause()
+ {
+ PauseLipSync();
+ }
+
+ public void Destroy()
+ {
+ DestroyLipAnimation();
+ }
+
+ //TODO : lipAnimation 자체를 Animator안에서 생성하고 관리될 수 있게 수정.
+ protected void ResetLipAnimation(Animation lipAnimation)
+ {
+ DestroyLipAnimation();
+ this.lipAnimation = lipAnimation;
+ if (this.lipAnimation != null)
+ {
+ this.lipAnimation.Finished += OnLipAnimationFinished;
+ }
+ }
+
+ protected void PlayLipAnimation()
+ {
+ if (lipAnimation == null)
+ {
+ Log.Error(LogTag, "Current Lip Animation is null");
+ }
+ lipAnimation?.Play();
+ }
+
+ protected void OnLipAnimationFinished(object sender, EventArgs e)
+ {
+ }
+
+ private void PlayLipSync(byte[] audio)
+ {
+ if (audio == null)
+ {
+ Tizen.Log.Error(LogTag, "audi data is null");
+ return;
+ }
+ DestroyLipAnimation();
+
+ var lipKeyframes = CreateKeyFrame(audio, CurrentAudioOptions.SampleRate);
+ if (lipKeyframes != null)
+ {
+ var lipAnimation = CreatedKeyFrameAnimation?.Invoke(lipKeyframes, false);
+
+ if (lipAnimation != null)
+ {
+ ResetLipAnimation(lipAnimation);
+ PlayLipAnimation();
+ audioPlayer.Play(audio);
+ }
+ else
+ {
+ Tizen.Log.Error(LogTag, "lipAnimation is null");
+ }
+ }
+ else
+ {
+ Tizen.Log.Error(LogTag, "lipKeyframes is null");
+ }
+ }
+
+ private void PlayLipSync(byte[] audio, int sampleRate)
+ {
+ DestroyLipAnimation();
+ var lipKeyFrames = CreateKeyFrame(audio, sampleRate);
+ var lipAnimation = CreatedKeyFrameAnimation?.Invoke(lipKeyFrames, false);
+ if (lipAnimation != null)
+ {
+ ResetLipAnimation(lipAnimation);
+ PlayLipAnimation();
+ }
+ audioPlayer.Play(audio, sampleRate);
+ }
+
+ private void PlayLipSync(string path)
+ {
+ var bytes = Utils.ReadAllBytes(path);
+ if (bytes != null)
+ {
+ PlayLipSync(bytes);
+ }
+ else
+ {
+ Log.Error(LogTag, "Failed to load audio file");
+ }
+ }
+
+ private void PauseLipSync()
+ {
+ PauseLipAnimation();
+ audioPlayer.Pause();
+ }
+
+ private void StopLipSync()
+ {
+ if (lipAnimation != null)
+ {
+ DestroyLipAnimation();
+
+ var lipAnimation = ResetLipAnimation();
+ if (lipAnimation != null)
+ {
+ ResetLipAnimation(lipAnimation);
+ PlayLipAnimation();
+ }
+ else
+ {
+ Log.Error(LogTag, "Current Lip Animation is null");
+ }
+ }
+ else
+ {
+ Log.Error(LogTag, "Current Lip Animation is null");
+ }
+ audioPlayer.Stop();
+ }
+
+ private void PauseLipAnimation()
+ {
+ if (lipAnimation != null)
+ {
+ lipAnimation?.Pause();
+ }
+ else
+ {
+ Log.Error(LogTag, "Current Lip Animation is null");
+ }
+ }
+
+ private void DestroyLipAnimation()
+ {
+ if (lipAnimation != null)
+ {
+ lipAnimation.Stop();
+ lipAnimation.Dispose();
+ lipAnimation = null;
+ }
+ }
+
+ private void EnqueueVowels(byte[] buffer, bool deleteLast = false)
+ {
+ var vowels = PredictVowels(buffer);
+ if (vowels != null)
+ {
+ vowelPools.Enqueue(vowels);
+ if (deleteLast)
+ {
+ var vowelList = new List<string>(vowels);
+ vowelList.RemoveAt(vowelList.Count - 1);
+ vowels = vowelList.ToArray();
+ }
+ }
+ }
+
+ private Animation CreateLipAnimationByVowelsQueue(int sampleRate = 0)
+ {
+ if (sampleRate == 0)
+ {
+ sampleRate = CurrentAudioOptions.SampleRate;
+ }
+ if (vowelPools.Count > 0)
+ {
+ AttachPreviousVowel(vowelPools.Dequeue(), out var newVowels);
+ Log.Info(LogTag, $"vowelRecognition: {String.Join(", ", newVowels)}");
+
+ var lipKeyFrames = vowelConverter?.CreateKeyFrames(newVowels, sampleRate, true);
+ var lipAnimation = CreatedKeyFrameAnimation?.Invoke(lipKeyFrames, true);
+
+ return lipAnimation;
+ }
+ return null;
+ }
+
+ private Animation ResetLipAnimation()
+ {
+ vowelPools.Clear();
+ var newVowels = new string[1];
+ newVowels[0] = prevVowel = "sil";
+ vowelPools.Enqueue(newVowels);
+ return CreateLipAnimationByVowelsQueue();
+ }
+
+ private string[] PredictVowels(byte[] audioData)
+ {
+ string[] vowels = vowelConverter?.PredictVowels(audioData);
+ return vowels;
+ }
+
+ private void AttachPreviousVowel(in string[] vowels, out string[] newVowels)
+ {
+ newVowels = new string[vowels.Length + 1];
+ newVowels[0] = prevVowel;
+ Array.Copy(vowels, 0, newVowels, 1, vowels.Length);
+ prevVowel = vowels[vowels.Length - 1];
+ }
+
+ private AnimationKeyFrame CreateKeyFrame(byte[] audio, int sampleRate)
+ {
+ var keyFrames = vowelConverter?.CreateKeyFrames(audio, sampleRate);
+ if (keyFrames == null)
+ {
+ Log.Error(LogTag, $"Failed to initialize KeyFrames");
+ }
+
+ return keyFrames;
+ }
+
+
+ internal void OnRecordBufferChanged(byte[] recordBuffer, int sampleRate)
+ {
+ EnqueueVowels(recordBuffer);
+ }
+
+ internal void OnRecodingTick()
+ {
+ var lipAnimation = CreateLipAnimationByVowelsQueue();
+ if (lipAnimation != null)
+ {
+ ResetLipAnimation(lipAnimation);
+ PlayLipAnimation();
+ }
+ else
+ {
+ Log.Error(LogTag, "Current Lip Animation is null");
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Collections.Generic;
+
+namespace Tizen.AIAvatar
+{
+ internal class CustomMfccExtractor : IMfccExtractor
+ {
+ private readonly int FilterBankSize = 24;
+
+ private int featureCount = 12;
+
+ private int blockSize;
+ private int frameSize;
+ private int hopSize;
+
+ private Window window;
+ private FFT fft;
+ private FilterBank filterBank;
+ private DCT dct;
+
+ private float[] block;
+ private float[] spectrum;
+ private float[] melSpectrum;
+
+ public CustomMfccExtractor(int samplingRate, float frameDuration, float hopDuration, int featureCount)
+ {
+ this.featureCount = featureCount;
+
+ frameSize = (int)Math.Round(samplingRate * frameDuration, 1);
+ hopSize = (int)Math.Round(samplingRate * hopDuration, 1);
+ blockSize = (int)Math.Pow(2, Math.Ceiling(Math.Log(frameSize, 2)));
+
+ window = new Window(frameSize);
+ fft = new FFT(blockSize);
+ filterBank = new FilterBank(FilterBankSize, samplingRate, blockSize);
+ dct = new DCT(FilterBankSize, this.featureCount);
+
+ block = new float[blockSize];
+ spectrum = new float[blockSize / 2 + 1];
+ melSpectrum = new float[FilterBankSize];
+ }
+
+ public List<float[]> ComputeFrom(float[] audio, int sampleRate)
+ {
+ int totalCount = (audio.Length - frameSize) / hopSize + 1;
+ List<float[]> mfccs = new List<float[]>(totalCount);
+
+ for (var i = 0; i < totalCount; i++)
+ {
+ mfccs.Add(new float[featureCount]);
+ }
+
+ var lastSample = audio.Length - frameSize;
+ for (int sample = 0, i = 0; sample <= lastSample; sample += hopSize, i++)
+ {
+ // 1. Framing
+ Buffer.BlockCopy(audio, sample * sizeof(float), block, 0, frameSize * sizeof(float));
+
+ for (var k = frameSize; k < blockSize; block[k++] = 0) { }
+
+ // 2. PreEmphasis
+ // PreEmphasis.PreEmphasize(ref block, 0.97f);
+
+ // 3. HammingWindow
+ window.Apply(block);
+
+ // 4. FFT
+ fft.Direct(block, spectrum);
+
+ // 5. MelFilterBank
+ filterBank.Apply(spectrum, melSpectrum);
+
+ // 6. DCT
+ dct.DirectNorm(melSpectrum, mfccs[i]);
+ }
+
+ return mfccs;
+ }
+ }
+
+ //DLL Interface
+ internal class Extractor
+ {
+ public static float[][] ComputeFrom(float[] audio, int samplingRate, float frameDuration, float hopDuration, int featureCount)
+ {
+ CustomMfccExtractor cme = new CustomMfccExtractor(samplingRate, frameDuration, hopDuration, featureCount);
+ List<float[]> mfccFeatures = cme.ComputeFrom(audio, samplingRate);
+
+ return mfccFeatures.ToArray();
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+
+namespace Tizen.AIAvatar
+{
+ internal class DCT
+ {
+ private int dctSize;
+ private int featureCount;
+
+ private float[][] dctMatrix;
+
+ public DCT(int filterBankSize, int featureCount = 12)
+ {
+ dctSize = filterBankSize;
+ this.featureCount = featureCount;
+
+ dctMatrix = new float[dctSize][];
+
+ float m = MathF.PI / (dctSize << 1);
+
+ for (var k = 0; k < dctSize; k++)
+ {
+ dctMatrix[k] = new float[dctSize];
+
+ for (var n = 0; n < dctSize; n++)
+ {
+ dctMatrix[k][n] = (float)(2 * Math.Cos(((n << 1) + 1) * k * m));
+ }
+ }
+ }
+
+ public void DirectNorm(float[] input, float[] output)
+ {
+
+ var norm0 = (float)Math.Sqrt(0.5f);
+ var norm = (float)Math.Sqrt(0.5f / dctSize);
+
+ for (var k = 0; k < featureCount; k++)
+ {
+ output[k] = 0.0f;
+ for (var n = 0; n < input.Length; n++)
+ {
+ output[k] += input[n] * dctMatrix[k][n];
+ }
+ output[k] *= norm;
+ }
+ output[0] *= norm0;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+
+namespace Tizen.AIAvatar
+{
+ internal class FFT
+ {
+ private int fftSize;
+
+ private float[] re, im;
+ private float[] realSpectrum, imagSpectrum;
+ private float[] cosTbl, sinTbl;
+ private float[] ar, br, ai, bi;
+
+ public FFT(int size)
+ {
+ fftSize = size / 2;
+
+ // Precalculate FFT Parameter
+ re = new float[fftSize];
+ im = new float[fftSize];
+ realSpectrum = new float[fftSize + 1];
+ imagSpectrum = new float[fftSize + 1];
+
+ var tblSize = (int)Math.Log(fftSize, 2);
+
+ cosTbl = new float[tblSize];
+ sinTbl = new float[tblSize];
+
+ for (int i = 1, pos = 0; i < fftSize; i *= 2, pos++)
+ {
+ cosTbl[pos] = (float)Math.Cos(2 * Math.PI * i / fftSize);
+ sinTbl[pos] = (float)Math.Sin(2 * Math.PI * i / fftSize);
+ }
+
+ ar = new float[fftSize];
+ br = new float[fftSize];
+ ai = new float[fftSize];
+ bi = new float[fftSize];
+
+ var f = MathF.PI / fftSize;
+
+ for (var i = 0; i < fftSize; i++)
+ {
+ ar[i] = (float)(0.5 * (1 - Math.Sin(f * i)));
+ ai[i] = (float)(-0.5 * Math.Cos(f * i));
+ br[i] = (float)(0.5 * (1 + Math.Sin(f * i)));
+ bi[i] = (float)(0.5 * Math.Cos(f * i));
+ }
+ }
+
+ public void Direct(float[] input, float[] output)
+ {
+ for (int i = 0, k = 0; i < fftSize; i++)
+ {
+ re[i] = input[k++];
+ im[i] = input[k++];
+ }
+
+ var L = fftSize;
+ var M = fftSize >> 1;
+ var S = fftSize - 1;
+ var ti = 0;
+ while (L >= 2)
+ {
+ var l = L >> 1;
+ var u1 = 1.0f;
+ var u2 = 0.0f;
+ var c = cosTbl[ti];
+ var s = -sinTbl[ti];
+ ti++;
+ for (var j = 0; j < l; j++)
+ {
+ for (var i = j; i < fftSize; i += L)
+ {
+ var p = i + l;
+ var t1 = re[i] + re[p];
+ var t2 = im[i] + im[p];
+ var t3 = re[i] - re[p];
+ var t4 = im[i] - im[p];
+ re[p] = t3 * u1 - t4 * u2;
+ im[p] = t4 * u1 + t3 * u2;
+ re[i] = t1;
+ im[i] = t2;
+ }
+ var u3 = u1 * c - u2 * s;
+ u2 = u2 * c + u1 * s;
+ u1 = u3;
+ }
+ L >>= 1;
+ }
+ for (int i = 0, j = 0; i < S; i++)
+ {
+ if (i > j)
+ {
+ var t1 = re[j];
+ var t2 = im[j];
+ re[j] = re[i];
+ im[j] = im[i];
+ re[i] = t1;
+ im[i] = t2;
+ }
+ var k = M;
+ while (j >= k)
+ {
+ j -= k;
+ k >>= 1;
+ }
+ j += k;
+ }
+
+ realSpectrum[0] = re[0] * ar[0] - im[0] * ai[0] + re[0] * br[0] + im[0] * bi[0];
+ imagSpectrum[0] = im[0] * ar[0] + re[0] * ai[0] + re[0] * bi[0] - im[0] * br[0];
+
+ for (var k = 1; k < fftSize; k++)
+ {
+ realSpectrum[k] = re[k] * ar[k] - im[k] * ai[k] + re[fftSize - k] * br[k] + im[fftSize - k] * bi[k];
+ imagSpectrum[k] = im[k] * ar[k] + re[k] * ai[k] + re[fftSize - k] * bi[k] - im[fftSize - k] * br[k];
+ }
+
+ realSpectrum[fftSize] = re[0] - im[0];
+ imagSpectrum[fftSize] = 0;
+
+ for (int i = 0; i < fftSize; i++)
+ {
+ output[i] = realSpectrum[i] * realSpectrum[i] + imagSpectrum[i] * imagSpectrum[i];
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Linq;
+
+namespace Tizen.AIAvatar
+{
+ internal struct bangDouble
+ {
+ public double left;
+ public double center;
+ public double right;
+
+ public bangDouble(double l, double c, double r)
+ {
+ left = l;
+ center = c;
+ right = r;
+ }
+
+ }
+
+ internal class FilterBank
+ {
+ private float[][] _filterBank;
+ private int _filterBankSize;
+
+ public FilterBank(int filterBankSize, int samplingRate, int blockSize)
+ {
+ _filterBankSize = filterBankSize;
+ var melBands = MelBands(filterBankSize, samplingRate);
+ _filterBank = Triangular(blockSize, samplingRate, melBands, HerzToMel);
+ }
+
+ public void Apply(float[] input, float[] output)
+ {
+ for (var i = 0; i < _filterBankSize; i++)
+ {
+ var sum = 0.0f;
+
+ for (var j = 0; j < input.Length; j++)
+ {
+ sum += _filterBank[i][j] * input[j];
+ }
+
+ output[i] = (float)Math.Log10(Math.Max(sum, float.Epsilon));
+ }
+ }
+
+ public static void Apply(float[][] filterBank, float[] spectrum, float[] filtered)
+ {
+ for (var i = 0; i < filterBank.Length; i++)
+ {
+ var en = 0.0f;
+
+ for (var j = 0; j < spectrum.Length; j++)
+ {
+ en += filterBank[i][j] * spectrum[j];
+ }
+
+ filtered[i] = en;
+ }
+ }
+
+ public static bangDouble[] MelBands(
+ int melFilterCount, int samplingRate, float lowFreq = 0, float highFreq = 0, bool overlap = true)
+ {
+ return UniformBands(HerzToMel, MelToHerz, melFilterCount, samplingRate, lowFreq, highFreq, overlap);
+ }
+
+ public static float[][] Triangular(int fftSize,
+ int samplingRate,
+ bangDouble[] frequencies,
+ Func<double, double> mapper = null)
+ {
+ if (mapper is null) mapper = x => x;
+
+ var herzResolution = (float)samplingRate / fftSize;
+ var herzFrequencies = Enumerable.Range(0, fftSize / 2 + 1)
+ .Select(f => f * herzResolution)
+ .ToArray();
+
+ var filterCount = frequencies.Length;
+ var filterBank = new float[filterCount][];
+
+ for (var i = 0; i < filterCount; i++)
+ {
+ filterBank[i] = new float[fftSize / 2 + 1];
+
+ var left = frequencies[i].left;
+ var center = frequencies[i].center;
+ var right = frequencies[i].right;
+
+ left = mapper(left);
+ center = mapper(center);
+ right = mapper(right);
+
+ var j = 0;
+ for (; mapper(herzFrequencies[j]) <= left; j++) ;
+ for (; mapper(herzFrequencies[j]) <= center; j++)
+ {
+ filterBank[i][j] = (float)((mapper(herzFrequencies[j]) - left) / (center - left));
+ }
+ for (; j < herzFrequencies.Length && mapper(herzFrequencies[j]) < right; j++)
+ {
+ filterBank[i][j] = (float)((right - mapper(herzFrequencies[j])) / (right - center));
+ }
+ }
+
+ return filterBank;
+ }
+
+ private static bangDouble[] UniformBands(
+ Func<double, double> scaleMapper,
+ Func<double, double> inverseMapper,
+ int filterCount,
+ int samplingRate,
+ float lowFreq = 0,
+ float highFreq = 0,
+ bool overlap = true)
+ {
+ if (lowFreq < 0) lowFreq = 0;
+ if (highFreq <= lowFreq) highFreq = samplingRate * 0.5f;
+
+ var startingFrequency = scaleMapper(lowFreq);
+ var frequencyTuples = new bangDouble[filterCount];
+
+ if (overlap)
+ {
+ var newResolution = (scaleMapper(highFreq) - scaleMapper(lowFreq)) / (filterCount + 1);
+ var frequencies = Enumerable.Range(0, filterCount + 2)
+ .Select(i => inverseMapper(startingFrequency + i * newResolution))
+ .ToArray();
+ for (var i = 0; i < filterCount; i++)
+ {
+ frequencyTuples[i].left = frequencies[i];
+ frequencyTuples[i].center = frequencies[i + 1];
+ frequencyTuples[i].right = frequencies[i + 2];
+ }
+ }
+ else
+ {
+ var newResolution = (scaleMapper(highFreq) - scaleMapper(lowFreq)) / filterCount;
+ var frequencies = Enumerable.Range(0, filterCount + 1)
+ .Select(i => inverseMapper(startingFrequency + i * newResolution))
+ .ToArray();
+
+ for (var i = 0; i < filterCount; i++)
+ {
+ frequencyTuples[i].left = frequencies[i];
+ frequencyTuples[i].center = (frequencies[i] + frequencies[i + 1]) / 2;
+ frequencyTuples[i].right = frequencies[i + 2];
+ }
+ }
+
+ return frequencyTuples;
+ }
+
+ public static double HerzToMel(double herz)
+ {
+ return 1127 * Math.Log(herz / 700 + 1);
+ }
+
+ public static double MelToHerz(double mel)
+ {
+ return (Math.Exp(mel / 1127) - 1) * 700;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.Collections.Generic;
+
+namespace Tizen.AIAvatar
+{
+ internal interface IMfccExtractor
+ {
+ List<float[]> ComputeFrom(float[] samples, int samplingRate);
+ }
+}
+
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+namespace Tizen.AIAvatar
+{
+ internal class PreEmphasis
+ {
+ internal static void PreEmphasize(ref float[] block, float value)
+ {
+ float prevSample = block[0];
+
+ for (int i = 0; i < block.Length; i++)
+ {
+ var result = block[i] - prevSample * value;
+ prevSample = block[i];
+ block[i] = result;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Linq;
+
+namespace Tizen.AIAvatar
+{
+ internal class Window
+ {
+ private float[] _window;
+
+ public Window(int size)
+ {
+ _window = Hamming(size);
+ }
+
+ public void Apply(float[] block)
+ {
+ if (block.Length < _window.Length) return;
+
+ for (int i = 0; i < _window.Length; i++)
+ {
+ block[i] *= _window[i];
+ }
+ }
+
+ public static float[] Hamming(int length)
+ {
+ var n = 2 * Math.PI / (length - 1);
+ return Enumerable.Range(0, length)
+ .Select(i => 0.54f - 0.46f * Math.Cos(i * n))
+ .Select(v => (float)v)
+ .ToArray();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.ComponentModel;
+
+namespace Tizen.AIAvatar
+{
+ internal interface ISingleShotModel
+ {
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void SetTensorData(int index, byte[] buffer);
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public byte[] GetTensorData(int index);
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void Invoke();
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Tizen.AIAvatar
+{
+ internal static class SoftmaxLinqExtension
+ {
+ internal static IEnumerable<float> SoftMax(this IEnumerable<float> source)
+ {
+ var exp = source.Select(x => MathF.Exp(x)).ToArray();
+ var sum = exp.Sum();
+ return exp.Select(x => x / sum);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.ComponentModel;
+using Tizen.MachineLearning.Inference;
+using static Tizen.AIAvatar.AIAvatar;
+
+namespace Tizen.AIAvatar
+{
+ internal class TFVowel6 : ISingleShotModel
+ {
+ private SingleShot singleShot;
+ private TensorsInfo inputInfo;
+ private TensorsInfo outputInfo;
+ private TensorsData inputData;
+ private TensorsData outputData;
+
+ private readonly string modelPath = ApplicationResourcePath + "audio2vowel_7.tflite";
+
+ internal TFVowel6(int[] inputDimension, int[] outputDimension)
+ {
+ try
+ {
+ inputInfo = new TensorsInfo();
+ inputInfo.AddTensorInfo(TensorType.Float32, inputDimension);
+
+ outputInfo = new TensorsInfo();
+ outputInfo.AddTensorInfo(TensorType.Float32, outputDimension);
+
+ singleShot = new SingleShot(modelPath, inputInfo, outputInfo);
+ }
+ catch (Exception e)
+ {
+ if (e is NotSupportedException)
+ {
+ Log.Info(LogTag, "NotSupportedException occurs");
+ }
+ else
+ {
+ Log.Info(LogTag, e.Message);
+ }
+ }
+ }
+
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void SetTensorData(int index, byte[] buffer)
+ {
+ try
+ {
+ inputData = inputInfo.GetTensorsData();
+ inputData.SetTensorData(index, buffer);
+ }
+ catch (Exception e)
+ {
+ Log.Info(LogTag, e.Message);
+ }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void Invoke()
+ {
+ try
+ {
+ outputData = singleShot.Invoke(inputData);
+ }
+ catch (Exception e)
+ {
+ Log.Info(LogTag, e.Message);
+ }
+
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public byte[] GetTensorData(int index)
+ {
+ return outputData.GetTensorData(index);
+ }
+
+
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using Newtonsoft.Json;
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Tizen.AIAvatar
+{
+ internal enum Viseme
+ {
+ sil, AE, Ah, B_M_P, Ch_J, EE, Er, IH, Oh, W_OO, S_Z, F_V, TH,
+ T_L_D_N, K_G_H_NG, R
+ };
+
+ internal class AnimationConverter
+ {
+ private Dictionary<string, string> VowelToViseme
+ = new Dictionary<string, string> {
+ { "A", "Ah" },
+ { "E", "AE" },
+ { "I", "EE" },
+ { "O", "R" },
+ { "ER", "R" },
+ { "U", "W_OO" },
+ { "HM", "B_M_P" },
+ { "sil", "sil" }
+ };
+
+ private AnimationKeyFrame _animationKeyFrame;
+ private BlendShapeInfo _visemBlendShapeInfo;
+ private Dictionary<string, BlendShapeValue[]> _visemeMap;
+
+ private bool _isInitialized;
+
+ public AnimationConverter() { }
+
+ public void InitializeVisemeInfo(string info_path)
+ {
+ try
+ {
+ StreamReader v = new StreamReader(info_path);
+ var jsonString = v.ReadToEnd();
+
+ _visemBlendShapeInfo = JsonConvert.DeserializeObject<BlendShapeInfo>(jsonString);
+ _visemeMap = _visemBlendShapeInfo.GetVisemeMap();
+
+ var nodeNames = _visemBlendShapeInfo.GetNodeNames();
+ var blendShapeCounts = _visemBlendShapeInfo.GetBlendShapeCounts();
+ var blendShapeKeyFormat = _visemBlendShapeInfo.blendShape.keyFormat;
+
+ _animationKeyFrame = new AnimationKeyFrame(nodeNames,
+ blendShapeCounts,
+ blendShapeKeyFormat);
+ _isInitialized = true;
+ }
+ catch (Exception)
+ {
+ throw new FailedPersonalizeException(info_path);
+ }
+ }
+
+ public AnimationKeyFrame ConvertVowelsToAnimation(string[] vowels,
+ float stepDuration)
+ {
+ if (!_isInitialized) throw new NotInitializedException();
+
+ _animationKeyFrame.ClearAnimation();
+ ConvertVowelsToVisemes(vowels, out var visemes);
+ ConvertVisemesToAnimationKeyFrame(visemes, stepDuration);
+
+ return _animationKeyFrame;
+ }
+
+ public AnimationKeyFrame ConvertVowelsToAnimationMic(string[] vowels,
+ float stepDuration)
+ {
+ if (!_isInitialized) throw new NotInitializedException();
+
+ _animationKeyFrame.ClearAnimation();
+ ConvertVowelsToVisemes(vowels, out var visemes);
+ ConvertVisemesToAnimationKeyFrameMic(visemes, stepDuration);
+
+ return _animationKeyFrame;
+ }
+
+ private void ConvertVowelsToVisemes(in string[] vowels,
+ out string[] visemes)
+ {
+ visemes = new string[vowels.Length];
+
+ for (var i = 0; i < vowels.Length; i++)
+ {
+ if (!VowelToViseme.TryGetValue(vowels[i], out visemes[i]))
+ {
+ throw new InvalidVowelTypeException(vowels[i]);
+ }
+ }
+ }
+
+ private void ConvertVisemesToAnimationKeyFrame(in string[] visemes,
+ float stepDuration)
+ {
+ float animationTime = visemes.Length * stepDuration;
+ _animationKeyFrame.SetAnimationTime(animationTime);
+
+ for (int i = 0; i < visemes.Length; i++)
+ {
+ float timeStamp = GetTimeStamp(i, stepDuration) / animationTime;
+
+ foreach (var info in _visemeMap[visemes[i]])
+ {
+ _animationKeyFrame.AddKeyFrame(info.nodeName,
+ info.blendIndex,
+ timeStamp,
+ info.blendValue);
+ }
+ }
+ }
+
+ private void ConvertVisemesToAnimationKeyFrameMic(in string[] visemes,
+ float stepDuration)
+ {
+ float animationTime = (visemes.Length - 1) * stepDuration;
+ _animationKeyFrame.SetAnimationTime(animationTime);
+
+ for (int i = 0; i < visemes.Length; i++)
+ {
+ float timeStamp = GetTimeStamp(i, stepDuration) / animationTime;
+
+ foreach (var info in _visemeMap[visemes[i]])
+ {
+ _animationKeyFrame.AddKeyFrame(info.nodeName,
+ info.blendIndex,
+ timeStamp,
+ info.blendValue);
+ }
+ }
+ }
+
+ private float GetTimeStamp(int idx, float stepDuration)
+ {
+ if (idx > 0)
+ return (idx * stepDuration) - (stepDuration / 2.0f);
+ else
+ return 0;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.Collections.Generic;
+
+namespace Tizen.AIAvatar
+{
+ internal class KeyFrame
+ {
+ public KeyFrame(float t, float v)
+ {
+ time = t;
+ value = v;
+ }
+ public float time;
+ public float value;
+ }
+
+ internal class AnimationKeyFrame
+ {
+ private Dictionary<string, List<KeyFrame>[]> _animationKeyFrames;
+
+ public string[] NodeNames { get; private set; }
+ public int[] BlendShapeCounts { get; private set; }
+ public string BlendShapeKeyFormat { get; private set; }
+ public float AnimationTime { get; private set; }
+
+ public AnimationKeyFrame(string[] nodeNames,
+ int[] blendShapeCounts,
+ string blendShapeKeyFormat)
+ {
+ _animationKeyFrames = new Dictionary<string, List<KeyFrame>[]>();
+ NodeNames = nodeNames;
+ BlendShapeCounts = blendShapeCounts;
+ BlendShapeKeyFormat = blendShapeKeyFormat;
+
+ InitializeAnimation(nodeNames, blendShapeCounts);
+ }
+
+ public void SetAnimationTime(float animationTime)
+ {
+ AnimationTime = animationTime;
+ }
+
+ public List<KeyFrame> GetKeyFrames(string nodeName, int blendIndex)
+ {
+ return _animationKeyFrames[nodeName][blendIndex];
+ }
+
+ public void AddKeyFrame(string nodeName, int blendIndex, float time, float value)
+ {
+ _animationKeyFrames[nodeName][blendIndex].Add(new KeyFrame(time, value));
+ }
+
+ public void AddKeyFrame(string nodeName, int blendIndex, KeyFrame value)
+ {
+ _animationKeyFrames[nodeName][blendIndex].Add(value);
+ }
+
+ public void InitializeAnimation(string[] nodeNames, int[] blendShapeCounts)
+ {
+ ClearAnimation();
+
+ for (int i = 0; i < nodeNames.Length; i++)
+ {
+ var nodeName = nodeNames[i];
+ var blendShapeCount = blendShapeCounts[i];
+
+ _animationKeyFrames.Add(nodeName, new List<KeyFrame>[blendShapeCount]);
+
+ for (int j = 0; j < blendShapeCount; j++)
+ {
+ _animationKeyFrames[nodeName][j] = new List<KeyFrame>();
+ }
+ }
+ }
+
+ public void ClearAnimation()
+ {
+ foreach (KeyValuePair<string, List<KeyFrame>[]> kvp in _animationKeyFrames)
+ {
+ foreach (List<KeyFrame> kf in kvp.Value)
+ {
+ kf.Clear();
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.Collections.Generic;
+
+namespace Tizen.AIAvatar
+{
+ internal class BlendShapeInfo
+ {
+ public BlendShapeModelInfo blendShape;
+ public BlendShapeVisemeInfo[] visemes;
+
+ public BlendShapeInfo() { }
+
+ public string[] GetNodeNames()
+ {
+ return blendShape.nodeNames;
+ }
+
+ public int[] GetBlendShapeCounts()
+ {
+ return blendShape.blendShapeCount;
+ }
+
+ public Dictionary<string, BlendShapeValue[]> GetVisemeMap()
+ {
+ Dictionary<string, BlendShapeValue[]> visemeMap
+ = new Dictionary<string, BlendShapeValue[]>();
+
+ foreach (var visemeInfo in this.visemes)
+ {
+ visemeMap.Add(visemeInfo.name, visemeInfo.values);
+ }
+
+ return visemeMap;
+ }
+ }
+
+ internal class BlendShapeModelInfo
+ {
+ public string keyFormat;
+ public string[] nodeNames;
+ public int[] blendShapeCount;
+
+ public BlendShapeModelInfo(string keyFormat, string[] nodeNames, int[] blendShapeCount)
+ {
+ this.keyFormat = keyFormat;
+ this.nodeNames = nodeNames;
+ this.blendShapeCount = blendShapeCount;
+ }
+ }
+
+ internal class BlendShapeVisemeInfo
+ {
+ public string name;
+ public BlendShapeValue[] values;
+ }
+
+ internal class BlendShapeValue
+ {
+ public string nodeName;
+ public int blendIndex;
+ public float blendValue;
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+
+namespace Tizen.AIAvatar
+{
+ internal class InvalidVowelTypeException : Exception
+ {
+ public InvalidVowelTypeException() { }
+
+ public InvalidVowelTypeException(string name)
+ : base($"Not supported vowel type, {name}") { }
+ }
+
+ internal class FailedPersonalizeException : Exception
+ {
+ public FailedPersonalizeException() { }
+
+ public FailedPersonalizeException(string name)
+ : base($"Failed to personalize, file_path : {name}") { }
+ }
+
+ internal class NotInitializedException : Exception
+ {
+ public NotInitializedException()
+ : base($"Animation Converter should be initialized with viseme_info") { }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Tizen.AIAvatar
+{
+ internal class VowelClassifier
+ {
+ private readonly int vowelForecastRange = 6;
+ private readonly string[] vowelName = { "A", "E", "I", "O", "U", "HM", "ER" };
+
+ private readonly int sampleRate;
+ private readonly int featureCount;
+
+ private float stepTime = 0.0f;
+ private const int DefaultFeatureMeanCount = 8;
+
+ private CustomMfccExtractor mfccExtractor;
+
+
+ internal VowelClassifier(int sampleRate, float frameDuration = 0.025f, float hopDuration = 0.01f, int featureCount = 12)
+ {
+ this.sampleRate = sampleRate;
+ this.featureCount = featureCount;
+ stepTime = (float)(hopDuration * DefaultFeatureMeanCount);
+ mfccExtractor = new CustomMfccExtractor(sampleRate, frameDuration, hopDuration, featureCount);
+ }
+
+ public float GetStepTime()
+ {
+ return stepTime;
+ }
+
+
+ public string[] Inference(byte[] audio, ISingleShotModel model)
+ {
+ int length = PreprocessInput(audio, out float[] features, out List<float> audioEnergy);
+ var outputData = InferenceModel(features, length, model);
+ var result = ProcessOutput(outputData, audioEnergy);
+
+ return result.ToArray();
+ }
+
+ public int PreprocessInput(byte[] audioByte, out float[] flattenedArray, out List<float> audioEnergy)
+ {
+ Utils.ConvertAudioToFloat(audioByte, out var audio);
+
+ var mfccFeatures = mfccExtractor.ComputeFrom(audio, sampleRate);
+
+ int featureLength = (int)Math.Ceiling((double)mfccFeatures.Count / DefaultFeatureMeanCount);
+ flattenedArray = new float[featureLength * featureCount];
+ audioEnergy = new List<float>();
+
+ for (int i = 0; i < featureLength; i++)
+ {
+ CalculateFeatureMean(mfccFeatures, out var featureMean, out var energy, i * DefaultFeatureMeanCount);
+ Array.Copy(featureMean, 0, flattenedArray, i * featureCount, featureCount);
+ audioEnergy.Add(energy);
+ }
+
+ return featureLength;
+ }
+
+ public List<float[]> InferenceModel(float[] input, int length, ISingleShotModel model)
+ {
+ byte[] inputBuffer = new byte[4 * 12 * 1 * 1];
+ byte[] outputBuffer;
+
+ int srcOffset = 0;
+ List<float[]> fullOutput = new List<float[]>();
+
+ for (int i = 0; i < length; i++)
+ {
+ float[] output = new float[7];
+
+ Buffer.BlockCopy(input, srcOffset, inputBuffer, 0, inputBuffer.Length);
+ srcOffset += inputBuffer.Length;
+
+ model.SetTensorData(0, inputBuffer);
+
+ model.Invoke();
+
+ outputBuffer = model.GetTensorData(0);
+
+ Buffer.BlockCopy(outputBuffer, 0, output, 0, outputBuffer.Length);
+
+ fullOutput.Add(output);
+
+ }
+
+ return fullOutput;
+ }
+
+ private List<string> ProcessOutput(List<float[]> outputData, List<float> audioEnergy)
+ {
+ var result = new List<string>();
+
+ for (int i = 0; i < outputData.Count; i++)
+ {
+ // Softmax 연산 수행
+ if (audioEnergy[i] > -1.0f)
+ {
+ var scores = Enumerable.Range(0, vowelForecastRange).
+ Select(j => outputData[i][j]).SoftMax().ToArray();
+ int maxIndex = Array.IndexOf(scores, scores.Max());
+ result.Add(vowelName[maxIndex]);
+ }
+ else
+ {
+ result.Add("sil");
+ }
+
+ }
+
+ return result;
+ }
+
+ private void CalculateFeatureMean(in List<float[]> features,
+ out float[] featureMean,
+ out float energy,
+ int offset)
+ {
+ featureMean = new float[featureCount];
+ int featureLength = (DefaultFeatureMeanCount < features.Count - offset
+ ? DefaultFeatureMeanCount
+ : features.Count - offset);
+
+ float sum = 0f;
+ for (int fc = 0; fc < featureCount; fc++)
+ {
+ for (int dc = offset; dc < offset + DefaultFeatureMeanCount && dc < features.Count; dc++)
+ {
+ featureMean[fc] += features[dc][fc];
+ }
+ featureMean[fc] /= featureLength;
+ sum += featureMean[fc];
+ }
+ energy = sum / featureCount;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.Collections.Generic;
+
+using static Tizen.AIAvatar.AIAvatar;
+
+namespace Tizen.AIAvatar
+{
+ internal class VowelConverter
+ {
+ private TFVowel6 vowelClassfierModel = null;
+ private VowelClassifier vowelClassifier = null;
+ private Dictionary<int, VowelClassifier> vowelClassifiers;
+ private AnimationConverter animationConverter = new AnimationConverter();
+
+ internal VowelConverter()
+ {
+ vowelClassifiers = new Dictionary<int, VowelClassifier>();
+ vowelClassifier = GetVowelClassifier(CurrentAudioOptions.SampleRate);
+ vowelClassfierModel = new TFVowel6(new int[4] { 12, 1, 1, 1 }, new int[4] { 7, 1, 1, 1 });
+ animationConverter.InitializeVisemeInfo(VisemeInfo);
+ }
+
+ internal AnimationKeyFrame CreateKeyFrames(string[] vowels, int sampleRate, bool isMic = false)
+ {
+ vowelClassifier = GetVowelClassifier(sampleRate);
+
+ if (isMic)
+ {
+ return animationConverter.ConvertVowelsToAnimationMic(vowels, vowelClassifier.GetStepTime());
+ }
+ else
+ {
+ return animationConverter.ConvertVowelsToAnimation(vowels, vowelClassifier.GetStepTime());
+ }
+ }
+
+ internal AnimationKeyFrame CreateKeyFrames(byte[] audioData, int sampleRate, bool isMic = false)
+ {
+ vowelClassifier = GetVowelClassifier(sampleRate);
+
+ if (vowelClassifier == null)
+ {
+ Log.Error(LogTag, "Failed to play Buffer");
+ return null;
+ }
+ var vowels = PredictVowels(audioData);
+ Log.Info(LogTag, $"vowelRecognition: {string.Join(", ", vowels)}");
+
+ if (isMic) return animationConverter.ConvertVowelsToAnimationMic(vowels, vowelClassifier.GetStepTime());
+ else return animationConverter.ConvertVowelsToAnimation(vowels, vowelClassifier.GetStepTime());
+ }
+
+ internal string[] PredictVowels(byte[] audioData)
+ {
+ var vowels = vowelClassifier.Inference(audioData, vowelClassfierModel);
+ return vowels;
+ }
+
+ internal VowelClassifier GetVowelClassifier(int sampleRate)
+ {
+ if (vowelClassifiers.ContainsKey(sampleRate))
+ {
+ return vowelClassifiers[sampleRate];
+ }
+ else
+ {
+ vowelClassifiers[sampleRate] = new VowelClassifier(sampleRate);
+ return vowelClassifiers[sampleRate];
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.ComponentModel;
+using Tizen.Multimedia;
+
+namespace Tizen.AIAvatar
+{
+ /// <summary>
+ /// Provides the ability to audio
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ internal class AudioOptions
+ {
+ private int sampleRate;
+ private AudioChannel channel;
+ private AudioSampleType sampleType;
+
+ /// <summary>
+ /// Initializes a new instance of the AudioOptions class with the specified sample rate, channel, and sampleType.
+ /// </summary>
+ /// <param name="sampleRate">the audio sample rate (8000 ~ 192000Hz)</param>
+ /// <param name="channel">the audio channel type.</param>
+ /// <param name="sampleType">the audio sample type.</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AudioOptions(int sampleRate, AudioChannel channel, AudioSampleType sampleType)
+ {
+ this.sampleRate = sampleRate;
+ this.channel = channel;
+ this.sampleType = sampleType;
+ }
+
+ /// <summary>
+ /// The audio sample rate (8000 ~ 192000Hz)
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public int SampleRate { get => sampleRate; set => sampleRate = value; }
+
+ /// <summary>
+ /// The audio channel type
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AudioChannel Channel { get => channel; set => channel = value; }
+
+ /// <summary>
+ /// The audio sample type
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public AudioSampleType SampleType { get => sampleType; set => sampleType = value; }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using Tizen.Multimedia;
+using System.IO;
+using System;
+
+using static Tizen.AIAvatar.AIAvatar;
+
+namespace Tizen.AIAvatar
+{
+ internal class AudioPlayer : IDisposable
+ {
+ private AudioPlayback audioPlayback;
+ private MemoryStream audioStream;
+
+ internal AudioPlayer()
+ {
+ }
+
+ internal void PlayAsync(byte[] buffer, int sampleRate = 0)
+ {
+ if (audioPlayback == null)
+ {
+ Play(buffer, sampleRate);
+ }
+ else
+ {
+ audioPlayback.Write(buffer);
+ }
+ }
+
+ internal void Play(byte[] audioBytes, int sampleRate = 0)
+ {
+ if (audioBytes == null)
+ {
+ return;
+ }
+
+ try
+ {
+ if (audioPlayback != null)
+ {
+ Destroy();
+ }
+ if (sampleRate == 0)
+ {
+ sampleRate = CurrentAudioOptions.SampleRate;
+ }
+ audioPlayback = new AudioPlayback(sampleRate, CurrentAudioOptions.Channel, CurrentAudioOptions.SampleType);
+ }
+ catch (Exception e)
+ {
+ Log.Error(LogTag, $"Failed to create AudioPlayback. {e.Message}");
+ }
+
+ if (audioPlayback != null)
+ {
+ audioPlayback.Prepare();
+ audioPlayback.BufferAvailable += (sender, args) =>
+ {
+ if (audioStream.Position == audioStream.Length)
+ {
+ return;
+ }
+
+ try
+ {
+ var buffer = new byte[args.Length];
+ audioStream.Read(buffer, 0, args.Length);
+ audioPlayback.Write(buffer);
+ }
+ catch (Exception e)
+ {
+ Log.Error(LogTag, $"Failed to write. {e.Message}");
+ }
+ };
+
+ audioStream = new MemoryStream(audioBytes);
+ }
+ }
+
+ internal void Pause()
+ {
+ if (audioPlayback != null)
+ {
+ audioPlayback.Pause();
+ }
+ else
+ {
+ Log.Error(LogTag, $"audioPlayBack is null");
+ }
+ }
+
+ internal void Stop()
+ {
+ if (audioPlayback != null)
+ {
+ audioPlayback.Pause();
+ Destroy();
+ }
+ else
+ {
+ Log.Error(LogTag, $"audioPlayBack is null");
+ }
+ }
+
+ public void Dispose()
+ {
+ Destroy();
+
+ audioStream?.Flush();
+ audioStream?.Dispose();
+ audioStream = null;
+ }
+
+ private void Destroy()
+ {
+ audioPlayback?.Unprepare();
+ audioPlayback?.Dispose();
+ audioPlayback = null;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Linq;
+using Tizen.Multimedia;
+using Tizen.NUI;
+using static Tizen.AIAvatar.AIAvatar;
+
+namespace Tizen.AIAvatar
+{
+ internal class AudioRecorder : IDisposable
+ {
+ private const string privilegeForRecording = "http://tizen.org/privilege/recorder";
+
+ private AsyncAudioCapture asyncAudioCapture;
+
+ private byte[] recordedBuffer;
+ private float desiredBufferDuration = 0.16f;
+ private int desiredBufferLength;
+
+ private Timer audioRecordingTimer;
+
+ private Action audioRecdingAction;
+ private Action<byte[], int> bufferAction;
+
+ private static AudioRecorder instance;
+
+ internal static AudioRecorder Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ instance = new AudioRecorder();
+ }
+ return instance;
+ }
+ }
+
+ internal event EventHandler<RecordBufferChangedEventArgs> BufferChanged;
+
+ internal AudioRecorder()
+ {
+ Utils.CheckPrivilege(privilegeForRecording);
+ desiredBufferLength = (int)(CurrentAudioOptions.SampleRate * desiredBufferDuration * 2);
+ }
+
+ internal void InitializeMic(LipSyncer lipSyncer, uint recordingTime = 160)
+ {
+ audioRecordingTimer = new Timer(recordingTime);
+ if (lipSyncer != null)
+ {
+ Tizen.Log.Error(LogTag, "LipSyncer of animator is null");
+ return;
+ }
+ this.audioRecdingAction = lipSyncer.OnRecodingTick;
+ this.bufferAction = lipSyncer.OnRecordBufferChanged;
+
+ BufferChanged += OnRecordBufferChanged;
+ audioRecordingTimer.Tick += AudioRecordingTimerTick;
+ }
+
+
+ internal void DeinitializeMic()
+ {
+ StopRecording();
+ BufferChanged -= OnRecordBufferChanged;
+
+ if (audioRecordingTimer != null)
+ {
+ audioRecordingTimer.Stop();
+ audioRecordingTimer.Tick -= AudioRecordingTimerTick;
+
+ audioRecordingTimer.Dispose();
+ audioRecordingTimer = null;
+ }
+ audioRecdingAction = null;
+ }
+
+ internal void StartRecording()
+ {
+ audioRecordingTimer?.Start();
+ asyncAudioCapture = new AsyncAudioCapture(CurrentAudioOptions.SampleRate, CurrentAudioOptions.Channel, CurrentAudioOptions.SampleType);
+
+ recordedBuffer = new byte[0];
+ asyncAudioCapture.DataAvailable += (s, e) =>
+ {
+ recordedBuffer = recordedBuffer.Concat(e.Data).ToArray();
+ if (recordedBuffer.Length >= desiredBufferLength)
+ {
+ var recordedBuffer = this.recordedBuffer;
+ this.recordedBuffer = Array.Empty<byte>();
+
+ BufferChanged?.Invoke(this, new RecordBufferChangedEventArgs(recordedBuffer, CurrentAudioOptions.SampleRate));
+ }
+ };
+ asyncAudioCapture.Prepare();
+ Log.Info(LogTag, "Start Recording - Preapre AsyncAudioCapture");
+ }
+
+ internal void StopRecording()
+ {
+ audioRecordingTimer?.Stop();
+ asyncAudioCapture.Dispose();
+ }
+
+ internal void PauseRecording()
+ {
+ asyncAudioCapture.Pause();
+ }
+
+ internal void ResumeRecording()
+ {
+ asyncAudioCapture.Resume();
+ }
+
+ private void OnRecordBufferChanged(object sender, RecordBufferChangedEventArgs e)
+ {
+ bufferAction?.Invoke(e.RecordedBuffer, CurrentAudioOptions.SampleRate);
+ }
+
+ private bool AudioRecordingTimerTick(object source, Timer.TickEventArgs e)
+ {
+ Log.Info(LogTag, "TickTimer");
+ audioRecdingAction?.Invoke();
+ return true;
+ }
+
+ public void Dispose()
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+
+namespace Tizen.AIAvatar
+{
+ internal class RecordBufferChangedEventArgs : EventArgs
+ {
+ public byte[] RecordedBuffer { get; set; }
+ public int SampleRate { get; set; }
+
+ public RecordBufferChangedEventArgs(byte[] recordedBuffer, int SampleRate)
+ {
+ this.RecordedBuffer = recordedBuffer;
+ this.SampleRate = SampleRate;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Tizen.Uix.Tts;
+
+using static Tizen.AIAvatar.AIAvatar;
+
+namespace Tizen.AIAvatar
+{
+ internal class TTSControllerEventArgs : EventArgs
+ {
+ public TTSControllerEventArgs()
+ {
+ }
+
+ public TTSControllerEventArgs(byte[] audioData, int sampleRate)
+ {
+ //AudioData = audioData;
+ AudioData = new byte[audioData.Length];
+ Buffer.BlockCopy(audioData, 0, AudioData, 0, audioData.Length);
+ SampleRate = sampleRate;
+ }
+
+ public byte[] AudioData
+ {
+ get;
+ internal set;
+ }
+
+ public int SampleRate
+ {
+ get;
+ internal set;
+ }
+ }
+
+ internal class TTSController : IDisposable
+ {
+ private List<UtteranceText> textList;
+ private TtsClient ttsHandle;
+ private VoiceInfo voiceInfo;
+ private List<Byte> byteList;
+
+ private byte[] recordedBuffer;
+ private byte[] audioTailBuffer;
+
+ private int sampleRate;
+ private float desiredBufferDuration = 0.175f;
+ private float audioTailLengthFactor = 0.015f;
+ private float audioBufferMultiflier = 2f;
+
+ private int desiredBufferLength;
+ private int audioTailLength;
+
+ private bool isPrepared = false;
+ private bool isAsync = false;
+
+ private Action<byte[], int> bufferChangedAction;
+
+ private int audioLength;
+ private bool isAsyncLipStarting;
+
+ internal event EventHandler PlayReadyCallback;
+
+ internal event EventHandler<TTSControllerEventArgs> PreparedSyncText;
+ internal event EventHandler<TTSControllerEventArgs> StoppedTTS;
+
+
+ internal event EventHandler<TTSControllerEventArgs> UpdatedBuffer;
+
+ internal TTSController()
+ {
+ InitTts();
+ }
+
+ ~TTSController()
+ {
+ DeinitTts();
+ }
+
+ internal TtsClient TtsHandle
+ {
+ get { return ttsHandle; }
+ }
+
+ internal VoiceInfo VoiceInfo
+ {
+ get { return voiceInfo; }
+ set
+ {
+ voiceInfo = value;
+ }
+ }
+
+ internal List<VoiceInfo> GetSupportedVoices()
+ {
+ var voiceInfoList = new List<VoiceInfo>();
+
+ if (ttsHandle == null)
+ {
+ Log.Error(LogTag, $"ttsHandle is null");
+ return voiceInfoList;
+ }
+
+ var supportedVoices = ttsHandle.GetSupportedVoices();
+ foreach (var supportedVoice in supportedVoices)
+ {
+ Log.Info(LogTag, $"{supportedVoice.Language} & {supportedVoice.VoiceType} is supported");
+ voiceInfoList.Add(new VoiceInfo() { Language = supportedVoice.Language, Type = (VoiceType)supportedVoice.VoiceType });
+ }
+ return voiceInfoList;
+ }
+
+ internal bool IsSupportedVoice(string lang)
+ {
+ if (ttsHandle == null)
+ {
+ Log.Error(LogTag, $"ttsHandle is null");
+ return false;
+ }
+ var supportedVoices = ttsHandle.GetSupportedVoices();
+
+ foreach (var supportedVoice in supportedVoices)
+ {
+ if (supportedVoice.Language.Equals(lang))
+ {
+ Log.Info(LogTag, $"{lang} is supported");
+ return true;
+ }
+ }
+ return false;
+ }
+
+ internal bool IsSupportedVoice(VoiceInfo voiceInfo)
+ {
+ if (ttsHandle == null)
+ {
+ Log.Error(LogTag, $"ttsHandle is null");
+ return false;
+ }
+ var supportedVoices = ttsHandle.GetSupportedVoices();
+ foreach (var supportedVoice in supportedVoices)
+ {
+ if (supportedVoice.Language.Equals(voiceInfo.Language) && ((VoiceType)supportedVoice.VoiceType == voiceInfo.Type))
+ {
+ Log.Info(LogTag, $"{voiceInfo.Language} & {voiceInfo.Type} is supported");
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ internal void AddText(string txt, VoiceInfo voiceInfo)
+ {
+ if (voiceInfo.Language == null)
+ {
+ Log.Error(LogTag, "VoiceInfo's value is null");
+ }
+ if (ttsHandle == null)
+ {
+ Log.Error(LogTag, $"ttsHandle is null");
+ return;
+ }
+ var temp = new UtteranceText();
+ temp.Text = txt;
+ temp.UttID = ttsHandle.AddText(txt, voiceInfo.Language, (int)voiceInfo.Type, 0);
+ try
+ {
+ textList.Add(temp);
+ }
+ catch (Exception e)
+ {
+ Log.Error(LogTag, $"Error AddText" + e.Message);
+ }
+ }
+
+ internal void AddText(string txt, string lang)
+ {
+ if (ttsHandle == null)
+ {
+ Log.Error(LogTag, $"ttsHandle is null");
+ return;
+ }
+ var temp = new UtteranceText();
+ temp.Text = txt;
+ temp.UttID = ttsHandle.AddText(txt, lang, (int)voiceInfo.Type, 0);
+ try
+ {
+ textList.Add(temp);
+ }
+ catch (Exception e)
+ {
+ Log.Error(LogTag, $"Error AddText" + e.Message);
+ }
+ }
+
+ internal void Prepare(EventHandler playReadyCallback)
+ {
+ if (ttsHandle == null)
+ {
+ Log.Error(LogTag, $"ttsHandle is null");
+ return;
+ }
+ Log.Info(LogTag, "Prepare TTS");
+ isPrepared = true;
+ isAsync = false;
+ PlayReadyCallback = playReadyCallback;
+ Play(true);
+ }
+
+
+ internal bool PlayPreparedText()
+ {
+ if (byteList != null && byteList.Count != 0)
+ {
+ Log.Info(LogTag, "PlayPreparedText TTS");
+
+ PreparedSyncText?.Invoke(this, new TTSControllerEventArgs(byteList.ToArray(), sampleRate));
+ return true;
+ }
+ return false;
+ }
+
+ internal void Play(bool isPrepared = false)
+ {
+ if (ttsHandle == null)
+ {
+ Log.Error(LogTag, $"ttsHandle is null");
+ return;
+ }
+
+ this.isPrepared = isPrepared;
+ isAsync = false;
+ ttsHandle.Play();
+ }
+
+ internal void PlayAsync(EventHandler playReadyCallback)
+ {
+ if (ttsHandle == null)
+ {
+ Log.Error(LogTag, $"ttsHandle is null");
+ return;
+ }
+
+ isPrepared = false;
+ isAsync = true;
+ PlayReadyCallback = playReadyCallback;
+ ttsHandle.Play();
+ }
+
+ public void Pause()
+ {
+ if (ttsHandle == null)
+ {
+ Log.Error(LogTag, $"ttsHandle is null");
+ return;
+ }
+ ttsHandle.Pause();
+ }
+
+ internal void Stop()
+ {
+ if (ttsHandle == null)
+ {
+ Log.Error(LogTag, $"ttsHandle is null");
+ return;
+ }
+ ttsHandle.Stop();
+ StoppedTTS?.Invoke(this, new TTSControllerEventArgs());
+ }
+
+ internal void DeinitTts()
+ {
+ try
+ {
+ if (ttsHandle != null)
+ {
+ ttsHandle.Unprepare();
+
+ // Unregister Callbacks
+ ttsHandle.DefaultVoiceChanged -= TtsDefaultVoiceChangedCallback;
+ ttsHandle.EngineChanged -= TtsEngineChangedCallback;
+ ttsHandle.ErrorOccurred -= TtsErrorOccuredCallback;
+ ttsHandle.StateChanged -= TtsStateChangedCallback;
+ ttsHandle.UtteranceCompleted -= TtsUtteranceCompletedCallback;
+ ttsHandle.UtteranceStarted -= TtsUtteranceStartedCallback;
+
+ ttsHandle.Dispose();
+ ttsHandle = null;
+ }
+
+ if (textList != null)
+ {
+ textList.Clear();
+ textList = null;
+ }
+
+ if (byteList != null)
+ {
+ byteList.Clear();
+ byteList = null;
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Error(LogTag, "[ERROR] Fail to unprepare Tts");
+ Log.Error(LogTag, e.Message);
+ }
+ }
+
+ private void InitTts()
+ {
+ try
+ {
+ ttsHandle = new TtsClient();
+
+ // Register Callbacks
+ ttsHandle.DefaultVoiceChanged += TtsDefaultVoiceChangedCallback;
+ ttsHandle.EngineChanged += TtsEngineChangedCallback;
+ ttsHandle.ErrorOccurred += TtsErrorOccuredCallback;
+ ttsHandle.StateChanged += TtsStateChangedCallback;
+ ttsHandle.UtteranceCompleted += TtsUtteranceCompletedCallback;
+ ttsHandle.UtteranceStarted += TtsUtteranceStartedCallback;
+
+ ttsHandle.SynthesizedPcm += TtsSyntheiszedPCM;
+ ttsHandle.PlayingMode = PlayingMode.ByClient;
+
+ ttsHandle.Prepare();
+
+ voiceInfo = new VoiceInfo
+ {
+ Language = ttsHandle.DefaultVoice.Language,
+ Type = (VoiceType)ttsHandle.DefaultVoice.VoiceType
+ };
+
+ textList = new List<UtteranceText>();
+ Log.Info(LogTag, voiceInfo.Language + ", " + voiceInfo.Type.ToString());
+
+ }
+ catch (Exception e)
+ {
+ Log.Error(LogTag, "[ERROR] Fail to prepare Tts");
+ Log.Error(LogTag, e.Message);
+ }
+ }
+
+ private void TtsSyntheiszedPCM(object sender, SynthesizedPcmEventArgs e)
+ {
+ var dataSize = e.Data.Length;
+ var audio = new byte[dataSize];
+ sampleRate = e.SampleRate;
+
+ //Marshal.Copy(e.Data, audio, 0, dataSize);
+ switch (e.EventType) //START
+ {
+ case SynthesizedPcmEvent.Start://start
+ Tizen.Log.Info(LogTag, "------------------Start : " + e.UtteranceId);
+ Tizen.Log.Info(LogTag, "Output audio Size : " + dataSize);
+ Tizen.Log.Info(LogTag, "SampleRate" + e.SampleRate);
+ if (byteList == null)
+ {
+ byteList = new List<byte>();
+ }
+ if (recordedBuffer == null)
+ {
+ recordedBuffer = new byte[0];
+ }
+ byteList.Clear();
+
+ if (isAsync)
+ {
+ recordedBuffer = Array.Empty<byte>();
+
+ desiredBufferLength = (int)(e.SampleRate * desiredBufferDuration * audioBufferMultiflier);
+ audioTailLength = (int)(sampleRate * audioTailLengthFactor * audioBufferMultiflier);
+ audioTailBuffer = new byte[audioTailLength];
+ PlayReadyCallback?.Invoke(null, EventArgs.Empty);
+ //InitAsyncBuffer();
+ }
+ break;
+ case SynthesizedPcmEvent.Continue://continue
+ if (isAsync)
+ {
+ recordedBuffer = recordedBuffer.Concat(e.Data).ToArray();
+ //PlayAsync
+ if (recordedBuffer.Length >= desiredBufferLength)
+ {
+ Tizen.Log.Error(LogTag, "Current recordbuffer length :" + recordedBuffer.Length);
+ //UpdateBuffer(recordedBuffer, sampleRate);
+
+ Buffer.BlockCopy(recordedBuffer, recordedBuffer.Length - audioTailLength, audioTailBuffer, 0, audioTailLength);
+
+ recordedBuffer = Array.Empty<byte>();
+ recordedBuffer = recordedBuffer.Concat(audioTailBuffer).ToArray();
+ Array.Clear(audioTailBuffer, 0, audioTailLength);
+ }
+ }
+ else
+ {
+ byteList.AddRange(e.Data);
+ }
+ break;
+ case SynthesizedPcmEvent.Finish://finish
+ Tizen.Log.Info(LogTag, "------------------Finish : " + e.UtteranceId);
+ if (!isAsync)
+ {
+ if (!isPrepared)
+ {
+ //Play voice immediately
+ PlayPreparedText();
+ }
+ else
+ {
+ //Notify finished state
+ Log.Info(LogTag, "Notify finished state");
+ PlayReadyCallback?.Invoke(null, EventArgs.Empty);
+ }
+ }
+ else
+ {//async
+ //FinishedSynthesizedPcm?.Invoke(null, EventArgs.Empty);
+ //lipSyncer.SetFinishAsyncLip(true);
+ }
+ break;
+ case SynthesizedPcmEvent.Fail: //fail
+ break;
+
+ }
+ }
+
+ private void TtsUtteranceStartedCallback(object sender, UtteranceEventArgs e)
+ {
+ Log.Debug(LogTag, "Utterance start now (" + e.UtteranceId + ")");
+ }
+
+ private void TtsUtteranceCompletedCallback(object sender, UtteranceEventArgs e)
+ {
+ Log.Debug(LogTag, "Utterance complete (" + e.UtteranceId + ")");
+
+ foreach (UtteranceText item in textList)
+ {
+ if (item.UttID == e.UtteranceId)
+ {
+ textList.Remove(item);
+ Log.Debug(LogTag, "TextList Count (" + textList.Count.ToString() + ")");
+ break;
+ }
+ }
+ }
+
+ private void TtsStateChangedCallback(object sender, StateChangedEventArgs e)
+ {
+ Log.Debug(LogTag, "Current state is changed from (" + e.Previous + ") to (" + e.Current + ")");
+ }
+
+ private void TtsErrorOccuredCallback(object sender, ErrorOccurredEventArgs e)
+ {
+ Log.Error(LogTag, "Error is occured (" + e.ErrorMessage + ")");
+ }
+
+ private void TtsEngineChangedCallback(object sender, EngineChangedEventArgs e)
+ {
+ Log.Debug(LogTag, "Prefered engine is changed (" + e.EngineId + ") (" + e.VoiceType.Language + ")");
+ }
+
+ private void TtsDefaultVoiceChangedCallback(object sender, DefaultVoiceChangedEventArgs e)
+ {
+ Log.Debug(LogTag, "Default voice is changed from (" + e.Previous + ") to (" + e.Current + ")");
+ }
+
+ private void InitAsyncBuffer()
+ {
+ /*
+ InitedAsyncBuffer?.Invoke(null, EventArgs.Empty);
+ if (!lipSyncer.IsAsyncInit)
+ {
+ audioLength = (int)(sampleRate * 0.16f * 2f);
+
+ lipSyncer.InitAsyncLipsync();
+ lipSyncer.IsAsyncInit = true;
+
+ lipSyncer.SetFinishAsyncLip(false);
+ isAsyncLipStarting = false;
+ }*/
+ }
+
+ private void UpdateBuffer(byte[] recordBuffer, int sampleRate)
+ {
+ UpdatedBuffer?.Invoke(this, new TTSControllerEventArgs(recordBuffer, sampleRate));
+ /*
+ if (lipSyncer != null)
+ {
+ Log.Error(LogTag, "OnTTSBufferChanged");
+ lipSyncer.EnqueueAnimation(recordBuffer, sampleRate, audioLength);
+ if (!isAsyncLipStarting)
+ {
+ lipSyncer.StartAsyncLipPlayTimer();
+ isAsyncLipStarting = true;
+ }
+ }
+ else
+ {
+ Log.Error(LogTag, "avatarLipSyncer is null");
+ }*/
+ }
+
+ public void Dispose()
+ {
+ ttsHandle.Stop();
+ ttsHandle.Dispose();
+ ttsHandle = null;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+
+namespace Tizen.AIAvatar
+{
+ internal struct UtteranceText : IEquatable<UtteranceText>
+ {
+ private string text;
+ private int uttID;
+
+ public string Text { get => text; set => text = value; }
+ public int UttID { get => uttID; set => uttID = value; }
+
+ public override bool Equals(object obj) => obj is VoiceInfo other && this.Equals(other);
+
+ public bool Equals(UtteranceText other) => Text == other.Text && UttID == other.UttID;
+
+ public static bool operator ==(UtteranceText lhsUtternaceText, UtteranceText rhsUtternaceText) => lhsUtternaceText.Equals(rhsUtternaceText);
+
+ public static bool operator !=(UtteranceText lhsUtternaceText, UtteranceText rhsVoiceInfo) => !lhsUtternaceText.Equals(rhsVoiceInfo);
+
+ public override int GetHashCode() => (Text, UttID).GetHashCode();
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.ComponentModel;
+
+namespace Tizen.AIAvatar
+{
+ /// <summary>
+ /// VoiceInfo stores information about a voice.
+ /// The 'Language' field stores the language of the voice
+ /// The 'Type' field stores the type of the voice.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public struct VoiceInfo : IEquatable<VoiceInfo>
+ {
+ private string language;
+ private VoiceType type;
+
+ /// <summary>
+ /// Gets or sets the language of the voice.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string Language { get => language; set => language = value; }
+
+ /// <summary>
+ /// Gets or sets the type of the voice.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public VoiceType Type { get => type; set => type = value; }
+
+ /// <summary>
+ /// Determines whether the specified object is equal to the current object.
+ /// </summary>
+ /// <param name="obj">The object to compare with the current object.</param>
+ /// <returns>true if equal VoiceInfo, else false.</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override bool Equals(object obj) => obj is VoiceInfo other && this.Equals(other);
+
+ /// <summary>
+ /// Determines whether the specified object is equal to the current object.
+ /// </summary>
+ /// <param name="other">The VoiceInfo to compare with the current VoiceInfo.</param>
+ /// <returns>true if equal VoiceInfo, else false.</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool Equals(VoiceInfo other) => Language == other.Language && Type == other.Type;
+
+ /// <summary>
+ /// The == operator.
+ /// </summary>
+ /// <param name="lhsVoiceInfo">VoiceInfo to compare</param>
+ /// <param name="rhsVoiceInfo">VoiceInfo to be compared</param>
+ /// <returns>true if VoiceInfo are equal</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static bool operator ==(VoiceInfo lhsVoiceInfo, VoiceInfo rhsVoiceInfo) => lhsVoiceInfo.Equals(rhsVoiceInfo);
+
+ /// <summary>
+ /// The != operator.
+ /// </summary>
+ /// <param name="lhsVoiceInfo">VoiceInfo to compare</param>
+ /// <param name="rhsVoiceInfo">VoiceInfo to be compared</param>
+ /// <returns>true if VoiceInfo are not equal</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static bool operator !=(VoiceInfo lhsVoiceInfo, VoiceInfo rhsVoiceInfo) => !lhsVoiceInfo.Equals(rhsVoiceInfo);
+
+ /// <summary>
+ /// Gets the hash code of this VoiceInfo.
+ /// </summary>
+ /// <returns>The hash code.</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override int GetHashCode() => (Language, Type).GetHashCode();
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.ComponentModel;
+
+namespace Tizen.AIAvatar
+{
+ /// <summary>
+ /// VoiceType is an enum that represents various types of voices.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public enum VoiceType
+ {
+ /// <summary>
+ /// Automatically determines the best voice to use based on available options.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Auto = 0,
+ /// <summary>
+ /// Selects a male voice for speech synthesis.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Male = 1,
+ /// <summary>
+ /// Selects a female voice for speech synthesis.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Female = 2,
+ /// <summary>
+ /// Selects a child's voice for speech synthesis.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Child = 3
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.Net.Http;
+using System.Threading.Tasks;
+
+namespace Tizen.AIAvatar
+{
+
+ internal interface IRestClient
+ {
+ Task<string> SendRequestAsync(HttpMethod method, string endpoint, string bearerToken = null, string jsonData = null);
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Net.Http.Headers;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using System.ComponentModel;
+
+namespace Tizen.AIAvatar
+{
+ internal class RestClient : IRestClient, IDisposable
+ {
+ private readonly HttpClient client;
+
+ internal RestClient(HttpClient httpClient)
+ {
+ client = httpClient;
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public async Task<string> SendRequestAsync(HttpMethod method, string endpoint, string bearerToken = null, string jsonData = null)
+ {
+ AddBearerToken(bearerToken);
+
+ HttpRequestMessage request = new HttpRequestMessage(method, endpoint);
+
+ if (jsonData != null)
+ {
+ request.Content = new StringContent(jsonData, Encoding.UTF8, "application/json");
+ }
+
+ HttpResponseMessage response = await client.SendAsync(request);
+ request?.Dispose();
+ return await HandleResponse(response);
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void Dispose()
+ {
+ client.Dispose();
+ }
+
+ private void AddBearerToken(string bearerToken)
+ {
+ if (!string.IsNullOrEmpty(bearerToken))
+ {
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);
+ }
+ }
+
+ private async Task<string> HandleResponse(HttpResponseMessage response)
+ {
+ if (response.IsSuccessStatusCode)
+ {
+ return await response.Content.ReadAsStringAsync();
+ }
+ else
+ {
+ throw new HttpRequestException($"HTTP request failed with status code {response.StatusCode}");
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System.Net.Http;
+using System;
+
+namespace Tizen.AIAvatar
+{
+ internal class RestClientFactory
+ {
+ internal IRestClient CreateClient(string baseUrl)
+ {
+ return new RestClient(new HttpClient { BaseAddress = new Uri(baseUrl) });
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+namespace Tizen.AIAvatar
+{
+ internal class TrackingController
+ {
+ internal TrackingController()
+ {
+ }
+
+ internal void Initialize(TrackingOptions options)
+ {
+ }
+
+ internal void StartMotionTracking()
+ {
+
+ }
+
+ internal void StopMotionTracking()
+ {
+
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+namespace Tizen.AIAvatar
+{
+ internal class TrackingOptions
+ {
+ }
+}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using Tizen.Multimedia;
-
-namespace Tizen.AIAvatar
-{
- internal static class AIAvatar
- {
- internal const string LogTag = "Tizen.AIAvatar";
- internal static readonly string ApplicationResourcePath = "/usr/apps/org.tizen.default-avatar-resource/shared/res/";
- internal static readonly string EmojiAvatarResourcePath = "/models/EmojiAvatar/";
- internal static readonly string DefaultModelResourcePath = "/models/DefaultAvatar/";
- internal static readonly string DefaultMotionResourcePath = "/animation/motion/";
-
- internal static readonly string VisemeInfo = $"{ApplicationResourcePath}/viseme/emoji_viseme_info.json";
- internal static readonly string DefaultModel = "DefaultAvatar.gltf";
-
- internal static readonly string AREmojiDefaultAvatarPath = $"{ApplicationResourcePath}{DefaultModelResourcePath}{DefaultModel}";
-
- internal static readonly string DefaultLowModelResourcePath = "/models/DefaultAvatar_Low/";
- internal static readonly string ExternalModel = "model_external.gltf";
- internal static readonly string AREmojiDefaultLowAvatarPath = $"{ApplicationResourcePath}{DefaultLowModelResourcePath}{ExternalModel}";
-
- internal static AudioOptions DefaultAudioOptions = new AudioOptions(24000, AudioChannel.Mono, AudioSampleType.S16Le);
- internal static AudioOptions CurrentAudioOptions = DefaultAudioOptions;
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-namespace Tizen.AIAvatar
-{
- internal class Interop
- {
- internal static partial class Libraries
- {
- public const string Scene3D = "libdali2-csharp-binder-scene3d.so";
- }
-
- [global::System.Runtime.InteropServices.DllImport(Libraries.Scene3D, EntryPoint = "CSharp_Dali_SceneView_Property_AlphaMaskURL_get")]
- public static extern int AlphaMaskURLGet();
-
- [global::System.Runtime.InteropServices.DllImport(Libraries.Scene3D, EntryPoint = "CSharp_Dali_SceneView_Property_MaskContentScale_get")]
- public static extern int MaskContentScaleGet();
-
- [global::System.Runtime.InteropServices.DllImport(Libraries.Scene3D, EntryPoint = "CSharp_Dali_SceneView_Property_CropToMask_get")]
- public static extern int CropToMaskGet();
-
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.IO;
-using Tizen.Security;
-using Newtonsoft.Json;
-using System.Collections.Generic;
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
- internal class Utils
- {
- internal static void ConvertAudioToFloat(in byte[] audioBytes, out float[] audioFloat)
- {
- audioFloat = new float[audioBytes.Length / 2];
-
- for (int i = 0, j = 0; i < audioBytes.Length; i += 2, j++)
- {
- short sample = BitConverter.ToInt16(audioBytes, i);
- audioFloat[j] = sample / 32768.0f;
- }
- }
-
- internal static byte[] ReadAllBytes(string path)
- {
- try
- {
- var bytes = File.ReadAllBytes(path);
- return bytes;
- }
- catch (Exception)
- {
- return null;
- }
- }
-
- internal static void SaveFile(string path, string filename, byte[] array)
- {
- try
- {
- var file = new FileStream($"{path}/{filename}", FileMode.Create);
- file.Write(array, 0, array.Length);
- file.Close();
- }
- catch (Exception)
- {
- return;
- }
- }
-
- internal static void CheckPrivilege(string privilege)
- {
- var result = PrivacyPrivilegeManager.CheckPermission(privilege);
-
- switch (result)
- {
- case CheckResult.Allow:
- Log.Info(LogTag, $"Privilege \"{privilege}\" : allowed.");
- break;
- case CheckResult.Deny:
- Log.Info(LogTag, $"Privilege \"{privilege}\" : denied.");
- /// Privilege can't be used
- break;
- case CheckResult.Ask:
- /// Request permission to user
- PrivacyPrivilegeManager.RequestPermission(privilege);
- break;
- }
- }
-
- internal static T ConvertJson<T>(string jsonString)
- {
- return JsonConvert.DeserializeObject<T>(jsonString);
- }
-
- internal int FindMaxValue<T>(List<T> list, Converter<T, int> projection)
- {
- if (list.Count == 0)
- {
- throw new InvalidOperationException("Empty list");
- }
- int maxValue = int.MinValue;
- foreach (T item in list)
- {
- int value = projection(item);
- if (value > maxValue)
- {
- maxValue = value;
- }
- }
- return maxValue;
- }
- }
-}
-
-internal class SystemUtils
-{
- internal static void ST()
- {
- Tizen.Log.Error("MYLOG", System.Environment.StackTrace);
- }
-
- internal static string GetFileName(string path)
- {
- return System.IO.Path.GetFileName(path);
- }
- internal static string GetFileNameWithoutExtension(string path)
- {
- return System.IO.Path.GetFileNameWithoutExtension(path);
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.Collections.Generic;
-using System.ComponentModel;
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
- internal class BlendShapeInfo
- {
- internal BlendShapeModelInfo blendShape;
- internal BlendShapeVisemeInfo[] visemes;
-
- internal BlendShapeInfo() { }
-
- internal string[] GetNodeNames()
- {
- if (blendShape == null)
- {
- Log.Error(LogTag, "blendShape is null");
- return null;
- }
- return blendShape.nodeNames;
- }
-
- internal int[] GetBlendShapeCounts()
- {
- return blendShape.blendShapeCount;
- }
-
- internal Dictionary<Viseme, BlendShapeValue[]> GetVisemeMap()
- {
- Dictionary<Viseme, BlendShapeValue[]> visemeMap = new Dictionary<Viseme, BlendShapeValue[]>();
-
- foreach (var visemeInfo in visemes)
- {
- visemeMap.Add(visemeInfo.name, visemeInfo.values);
- }
-
- return visemeMap;
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
- internal class BlendShapeModelInfo
- {
- internal string keyFormat;
- internal string[] nodeNames;
- internal int[] blendShapeCount;
-
- internal BlendShapeModelInfo(string keyFormat, string[] nodeNames, int[] blendShapeCount)
- {
- this.keyFormat = keyFormat;
- this.nodeNames = nodeNames;
- this.blendShapeCount = blendShapeCount;
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
- internal class BlendShapeValue
- {
- internal string nodeName;
- internal BlendShapeType blendIndex;
- internal float blendValue;
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
- internal class BlendShapeVisemeInfo
- {
- internal Viseme name;
- internal BlendShapeValue[] values;
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2024 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.Net.Http;
-using System.Threading.Tasks;
-
-namespace Tizen.AIAvatar
-{
-
- internal interface IRestClient
- {
- Task<string> SendRequestAsync(HttpMethod method, string endpoint, string bearerToken = null, string jsonData = null);
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2024 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.Net.Http.Headers;
-using System.Net.Http;
-using System.Text;
-using System.Threading.Tasks;
-using System.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
- internal class RestClient : IRestClient, IDisposable
- {
- private readonly HttpClient client;
-
- internal RestClient(HttpClient httpClient)
- {
- client = httpClient;
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public async Task<string> SendRequestAsync(HttpMethod method, string endpoint, string bearerToken = null, string jsonData = null)
- {
- AddBearerToken(bearerToken);
-
- HttpRequestMessage request = new HttpRequestMessage(method, endpoint);
-
- if (jsonData != null)
- {
- request.Content = new StringContent(jsonData, Encoding.UTF8, "application/json");
- }
-
- HttpResponseMessage response = await client.SendAsync(request);
- request?.Dispose();
- return await HandleResponse(response);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Dispose()
- {
- client.Dispose();
- }
-
- private void AddBearerToken(string bearerToken)
- {
- if (!string.IsNullOrEmpty(bearerToken))
- {
- client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);
- }
- }
-
- private async Task<string> HandleResponse(HttpResponseMessage response)
- {
- if (response.IsSuccessStatusCode)
- {
- return await response.Content.ReadAsStringAsync();
- }
- else
- {
- throw new HttpRequestException($"HTTP request failed with status code {response.StatusCode}");
- }
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2024 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.Net.Http;
-using System;
-
-namespace Tizen.AIAvatar
-{
- internal class RestClientFactory
- {
- internal IRestClient CreateClient(string baseUrl)
- {
- return new RestClient(new HttpClient { BaseAddress = new Uri(baseUrl) });
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Tizen.NUI;
-using Tizen.NUI.Scene3D;
-
-namespace Tizen.AIAvatar
-{
- internal class DefaultAvatarProperties : AvatarProperties
- {
- private static AvatarPropertyMapper defaultJointMapper = new AvatarPropertyMapper();
- private static AvatarPropertyMapper defaultBlendShapeNameMapper = new AvatarPropertyMapper();
- private static AvatarPropertyMapper defaultNodeMapper = new AvatarPropertyMapper();
-
-
- internal DefaultAvatarProperties() : base(defaultJointMapper, defaultBlendShapeNameMapper, defaultNodeMapper)
- {
- foreach (var indexNamePair in blendShapeList)
- {
- defaultBlendShapeNameMapper.SetPropertyName((uint)indexNamePair.Item1, indexNamePair.Item2);
- }
-
- foreach (var indexNamePair in jointList)
- {
- defaultJointMapper.SetPropertyName((uint)indexNamePair.Item1, indexNamePair.Item2);
- }
-
- foreach (var indexNamePair in nodeList)
- {
- defaultNodeMapper.SetPropertyName((uint)indexNamePair.Item1, indexNamePair.Item2);
- }
- }
-
- #region AR Emoji BlendShape name list
- private static readonly List<(BlendShapeType, string)> blendShapeList = new List<(BlendShapeType, string)>()
- {
- (BlendShapeType.EyeBlinkLeft, "EyeBlink_Left"),
- (BlendShapeType.EyeSquintLeft, "EyeSquint_Left"),
- (BlendShapeType.EyeDownLeft, "EyeDown_Left"),
- (BlendShapeType.EyeInLeft, "EyeIn_Left"),
- (BlendShapeType.EyeOpenLeft, "EyeOpen_Left"),
- (BlendShapeType.EyeOutLeft, "EyeOut_Left"),
- (BlendShapeType.EyeUpLeft, "EyeUp_Left"),
-
- (BlendShapeType.EyeBlinkRight, "EyeBlink_Right"),
- (BlendShapeType.EyeSquintRight, "EyeSquint_Right"),
- (BlendShapeType.EyeDownRight, "EyeDown_Right"),
- (BlendShapeType.EyeInRight, "EyeIn_Right"),
- (BlendShapeType.EyeOpenRight, "EyeOpen_Right"),
- (BlendShapeType.EyeOutRight, "EyeOut_Right"),
- (BlendShapeType.EyeUpRight, "EyeUp_Right"),
-
- (BlendShapeType.JawForward, "JawFwd"),
- (BlendShapeType.JawOpen, "JawOpen"),
- (BlendShapeType.JawChew, "JawChew"),
- (BlendShapeType.JawLeft, "JawLeft"),
- (BlendShapeType.JawRight, "JawRight"),
-
- (BlendShapeType.MouthLeft, "MouthLeft"),
- (BlendShapeType.MouthFrownLeft, "MouthFrown_Left"),
- (BlendShapeType.MouthSmileLeft, "MouthSmile_Left"),
- (BlendShapeType.MouthDimpleLeft, "MouthDimple_Left"),
-
- (BlendShapeType.MouthRight, "MouthRight"),
- (BlendShapeType.MouthFrownRight, "MouthFrown_Right"),
- (BlendShapeType.MouthSmileRight, "MouthSmile_Right"),
- (BlendShapeType.MouthDimpleRight, "MouthDimple_Right"),
-
- (BlendShapeType.LipsStretchLeft, "LipsStretch_Left"),
- (BlendShapeType.LipsStretchRight, "LipsStretch_Right"),
- (BlendShapeType.LipsUpperClose, "LipsUpperClose"),
- (BlendShapeType.LipsLowerClose, "LipsLowerClose"),
- (BlendShapeType.LipsUpperUp, "LipsUpperUp"),
- (BlendShapeType.LipsLowerDown, "LipsLowerDown"),
- (BlendShapeType.LipsUpperOpen, "LipsUpperOpen"),
- (BlendShapeType.LipsLowerOpen, "LipsLowerOpen"),
- (BlendShapeType.LipsFunnel, "LipsFunnel"),
- (BlendShapeType.LipsPucker, "LipsPucker"),
-
- (BlendShapeType.BrowDownLeft, "BrowsDown_Left"),
- (BlendShapeType.BrowDownRight, "BrowsDown_Right"),
- (BlendShapeType.BrowUpCenter, "BrowsUp_Center"),
- (BlendShapeType.BrowUpLeft, "BrowsUp_Left"),
- (BlendShapeType.BrowUpRight, "BrowsUp_Right"),
- (BlendShapeType.CheekSquintLeft, "CheekSquint_Left"),
- (BlendShapeType.CheekSquintRight, "CheekSquint_Right"),
- (BlendShapeType.ChinLowerRaise, "ChinLowerRaise"),
- (BlendShapeType.ChinUpperRaise, "ChinUpperRaise"),
-
- (BlendShapeType.TongueOut, "Tongue_Out"),
- (BlendShapeType.TongueUp, "Tongue_Up"),
- (BlendShapeType.TongueDown, "Tongue_Down"),
- (BlendShapeType.TongueLeft, "Tongue_Left"),
- (BlendShapeType.TongueRight, "Tongue_Right"),
-
- (BlendShapeType.Sneer, "Sneer"),
- (BlendShapeType.Puff, "Puff"),
- (BlendShapeType.PuffLeft, "Puff_Left"),
- (BlendShapeType.PuffRight, "Puff_Right"),
- };
- #endregion
-
- #region AR Emoji Joint name list
- private static readonly List<(JointType, string)> jointList = new List<(JointType, string)>
- {
- (JointType.Head, "head_JNT"),
- (JointType.Neck, "neck_JNT"),
- (JointType.EyeLeft, "l_eye_JNT"),
- (JointType.EyeRight, "r_eye_JNT"),
-
- (JointType.ShoulderLeft, "l_arm_JNT"),
- (JointType.ElbowLeft, "l_forearm_JNT"),
- (JointType.WristLeft, "l_hand_JNT"),
-
- (JointType.ShoulderRight, "r_arm_JNT"),
- (JointType.ElbowRight, "r_forearm_JNT"),
- (JointType.WristRight, "r_hand_JNT"),
-
- (JointType.HipLeft, "l_upleg_JNT"),
- (JointType.KneeLeft, "l_leg_JNT"),
- (JointType.AnkleLeft, "l_foot_JNT"),
- (JointType.ForeFootLeft, "l_toebase_JNT"),
-
- (JointType.HipRight, "r_upleg_JNT"),
- (JointType.KneeRight, "r_leg_JNT"),
- (JointType.AnkleRight, "r_foot_JNT"),
- (JointType.ForeFootRight, "r_toebase_JNT"),
-
- (JointType.FingerThumb1Left, "l_handThumb1_JNT"),
- (JointType.FingerThumb2Left, "l_handThumb2_JNT"),
- (JointType.FingerThumb3Left, "l_handThumb3_JNT"),
- (JointType.FingerThumb4Left, "l_handThumb4_JNT"),
- (JointType.FingerIndex1Left, "l_handIndex1_JNT"),
- (JointType.FingerIndex2Left, "l_handIndex2_JNT"),
- (JointType.FingerIndex3Left, "l_handIndex3_JNT"),
- (JointType.FingerIndex4Left, "l_handIndex4_JNT"),
- (JointType.FingerMiddle1Left, "l_handMiddle1_JNT"),
- (JointType.FingerMiddle2Left, "l_handMiddle2_JNT"),
- (JointType.FingerMiddle3Left, "l_handMiddle3_JNT"),
- (JointType.FingerMiddle4Left, "l_handMiddle4_JNT"),
- (JointType.FingerRing1Left, "l_handRing1_JNT"),
- (JointType.FingerRing2Left, "l_handRing2_JNT"),
- (JointType.FingerRing3Left, "l_handRing3_JNT"),
- (JointType.FingerRing4Left, "l_handRing4_JNT"),
- (JointType.FingerPinky1Left, "l_handPinky1_JNT"),
- (JointType.FingerPinky2Left, "l_handPinky2_JNT"),
- (JointType.FingerPinky3Left, "l_handPinky3_JNT"),
- (JointType.FingerPinky4Left, "l_handPinky4_JNT"),
-
- (JointType.FingerThumb1Right, "r_handThumb1_JNT"),
- (JointType.FingerThumb2Right, "r_handThumb2_JNT"),
- (JointType.FingerThumb3Right, "r_handThumb3_JNT"),
- (JointType.FingerThumb4Right, "r_handThumb4_JNT"),
- (JointType.FingerIndex1Right, "r_handIndex1_JNT"),
- (JointType.FingerIndex2Right, "r_handIndex2_JNT"),
- (JointType.FingerIndex3Right, "r_handIndex3_JNT"),
- (JointType.FingerIndex4Right, "r_handIndex4_JNT"),
- (JointType.FingerMiddle1Right, "r_handMiddle1_JNT"),
- (JointType.FingerMiddle2Right, "r_handMiddle2_JNT"),
- (JointType.FingerMiddle3Right, "r_handMiddle3_JNT"),
- (JointType.FingerMiddle4Right, "r_handMiddle4_JNT"),
- (JointType.FingerRing1Right, "r_handRing1_JNT"),
- (JointType.FingerRing2Right, "r_handRing2_JNT"),
- (JointType.FingerRing3Right, "r_handRing3_JNT"),
- (JointType.FingerRing4Right, "r_handRing4_JNT"),
- (JointType.FingerPinky1Right, "r_handPinky1_JNT"),
- (JointType.FingerPinky2Right, "r_handPinky2_JNT"),
- (JointType.FingerPinky3Right, "r_handPinky3_JNT"),
- (JointType.FingerPinky4Right, "r_handPinky4_JNT"),
- };
- #endregion
-
- #region AR Emoji Joint name list
- private static readonly List<(NodeType, string)> nodeList = new List<(NodeType, string)>
- {
- (NodeType.HeadGeo, "head_GEO"),
- (NodeType.MouthGeo, "mouth_GEO"),
- (NodeType.EyelashGeo, "eyelash_GEO"),
- };
- #endregion
-
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using Tizen.NUI;
-
-namespace Tizen.AIAvatar
-{
- internal class LipInfo
- {
- private Animatable animatable;
- private string nodeName;
- private int blendShapeCount;
-
- internal Animatable Animatable { get => animatable; set => animatable = value; }
- internal string NodeName { get => nodeName; set => nodeName = value; }
- internal int BlendShapeCount { get => blendShapeCount; set => blendShapeCount = value; }
- }
-}
+++ /dev/null
-
-using System.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
- internal interface ISingleShotModel
- {
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void SetTensorData(int index, byte[] buffer);
- [EditorBrowsable(EditorBrowsableState.Never)]
- public byte[] GetTensorData(int index);
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Invoke();
- }
-}
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Tizen.AIAvatar
-{
- internal static class SoftmaxLinqExtension
- {
- internal static IEnumerable<float> SoftMax(this IEnumerable<float> source)
- {
- var exp = source.Select(x => MathF.Exp(x)).ToArray();
- var sum = exp.Sum();
- return exp.Select(x => x / sum);
- }
- }
-}
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Text;
-using Tizen.MachineLearning.Inference;
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
- internal class TFVowel6 : ISingleShotModel
- {
- private SingleShot singleShot;
- private TensorsInfo inputInfo;
- private TensorsInfo outputInfo;
- private TensorsData inputData;
- private TensorsData outputData;
-
- private readonly string modelPath = ApplicationResourcePath + "audio2vowel_7.tflite";
-
- internal TFVowel6(int[] inputDimension, int[] outputDimension)
- {
- try
- {
- inputInfo = new TensorsInfo();
- inputInfo.AddTensorInfo(TensorType.Float32, inputDimension);
-
- outputInfo = new TensorsInfo();
- outputInfo.AddTensorInfo(TensorType.Float32, outputDimension);
-
- singleShot = new SingleShot(modelPath, inputInfo, outputInfo);
- }
- catch (Exception e)
- {
- if (e is NotSupportedException)
- {
- Log.Info(LogTag, "NotSupportedException occurs");
- }
- else
- {
- Log.Info(LogTag, e.Message);
- }
- }
- }
-
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void SetTensorData(int index, byte[] buffer)
- {
- try
- {
- inputData = inputInfo.GetTensorsData();
- inputData.SetTensorData(index, buffer);
- }
- catch (Exception e)
- {
- Log.Info(LogTag, e.Message);
- }
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Invoke()
- {
- try
- {
- outputData = singleShot.Invoke(inputData);
- }
- catch (Exception e)
- {
- Log.Info(LogTag, e.Message);
- }
-
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public byte[] GetTensorData(int index)
- {
- return outputData.GetTensorData(index);
- }
-
-
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-namespace Tizen.AIAvatar
-{
- // Various visemes
- internal enum Viseme
- {
- sil,
- AE,
- Ah,
- B_M_P,
- Ch_J,
- EE,
- Er,
- IH,
- Oh,
- W_OO,
- R,
- };
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using Tizen.Multimedia;
-using System.IO;
-using System;
-
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
- internal class AudioPlayer
- {
- private AudioPlayback audioPlayback;
- private MemoryStream audioStream;
-
- internal AudioPlayer()
- {
- }
-
- internal void PlayAsync(byte[] buffer, int sampleRate = 0)
- {
- if (audioPlayback == null)
- {
- Play(buffer, sampleRate);
- }
- else
- {
- audioPlayback.Write(buffer);
- }
- }
-
- internal void Play(byte[] audioBytes, int sampleRate = 0)
- {
- if (audioBytes == null)
- {
- return;
- }
-
- try
- {
- if (audioPlayback != null)
- {
- DestroyAudioPlayback();
- }
- if (sampleRate == 0)
- {
- sampleRate = CurrentAudioOptions.SampleRate;
- }
- audioPlayback = new AudioPlayback(sampleRate, CurrentAudioOptions.Channel, CurrentAudioOptions.SampleType);
- }
- catch (Exception e)
- {
- Log.Error(LogTag, $"Failed to create AudioPlayback. {e.Message}");
- return;
- }
-
- if (audioPlayback != null)
- {
- audioPlayback.Prepare();
- audioPlayback.BufferAvailable += (sender, args) =>
- {
- if (audioStream.Position == audioStream.Length)
- {
- return;
- }
-
- try
- {
- var buffer = new byte[args.Length];
- audioStream.Read(buffer, 0, args.Length);
- audioPlayback.Write(buffer);
- }
- catch (Exception e)
- {
- Log.Error(LogTag, $"Failed to write. {e.Message}");
- }
- };
-
- audioStream = new MemoryStream(audioBytes);
- }
- }
-
- internal void Pause()
- {
- if (audioPlayback != null)
- {
- audioPlayback.Pause();
- }
- else
- {
- Log.Error(LogTag, $"audioPlayBack is null");
- }
- }
-
- internal void Stop()
- {
- if (audioPlayback != null)
- {
- audioPlayback.Pause();
- DestroyAudioPlayback();
- }
- else
- {
- Log.Error(LogTag, $"audioPlayBack is null");
- }
- }
-
- internal void Destroy()
- {
- DestroyAudioPlayback();
- }
-
- private void DestroyAudioPlayback()
- {
- audioPlayback?.Unprepare();
- audioPlayback?.Dispose();
- audioPlayback = null;
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.Linq;
-using System.Text;
-using Tizen.Multimedia;
-using Tizen.NUI;
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
- internal class AudioRecorder
- {
- private const string privilegeForRecording = "http://tizen.org/privilege/recorder";
-
- private AsyncAudioCapture asyncAudioCapture;
-
- private byte[] recordedBuffer;
- private float desiredBufferDuration = 0.16f;
- private int desiredBufferLength;
-
- private Timer audioRecordingTimer;
-
- private Action audioRecdingAction;
- private Action<byte[],int > bufferAction;
-
- private static AudioRecorder instance;
-
- internal static AudioRecorder Instance
- {
- get
- {
- if (instance == null)
- {
- instance = new AudioRecorder();
- }
- return instance;
- }
- }
-
- internal event EventHandler<RecordBufferChangedEventArgs> BufferChanged;
-
- internal AudioRecorder()
- {
- Utils.CheckPrivilege(privilegeForRecording);
- desiredBufferLength = (int)(CurrentAudioOptions.SampleRate * desiredBufferDuration * 2);
- }
-
-
- internal void InitMic(BlendShapePlayer animator, uint recordingTime = 160)
- {
- audioRecordingTimer = new Timer(recordingTime);
- if (animator != null)
- {
- //TODO : Connection MIC
- var lipSyncer = (animator.GetAnimationModule(AnimationModuleType.LipSyncer) as LipSyncer);
- if(lipSyncer != null)
- {
- Tizen.Log.Error(LogTag, "LipSyncer of animator is null");
- return;
- }
- this.audioRecdingAction = lipSyncer.OnRecodingTick;
- this.bufferAction = lipSyncer.OnRecordBufferChanged;
-
- BufferChanged += OnRecordBufferChanged;
- audioRecordingTimer.Tick += AudioRecordingTimerTick;
- }
- }
-
-
- internal void DeinitMic()
- {
- StopRecording();
- BufferChanged -= OnRecordBufferChanged;
-
- if (audioRecordingTimer != null)
- {
- audioRecordingTimer.Stop();
- audioRecordingTimer.Tick -= AudioRecordingTimerTick;
-
- audioRecordingTimer.Dispose();
- audioRecordingTimer = null;
- }
- audioRecdingAction = null;
- }
-
- internal void StartRecording()
- {
- audioRecordingTimer?.Start();
- asyncAudioCapture = new AsyncAudioCapture(CurrentAudioOptions.SampleRate, CurrentAudioOptions.Channel, CurrentAudioOptions.SampleType);
-
- recordedBuffer = new byte[0];
- asyncAudioCapture.DataAvailable += (s, e) =>
- {
- recordedBuffer = recordedBuffer.Concat(e.Data).ToArray();
- if (recordedBuffer.Length >= desiredBufferLength)
- {
- var recordedBuffer = this.recordedBuffer;
- this.recordedBuffer = Array.Empty<byte>();
-
- BufferChanged?.Invoke(this, new RecordBufferChangedEventArgs(recordedBuffer, CurrentAudioOptions.SampleRate));
- }
- };
- asyncAudioCapture.Prepare();
- Log.Info(LogTag, "Start Recording - Preapre AsyncAudioCapture");
- }
-
- internal void StopRecording()
- {
- audioRecordingTimer?.Stop();
- asyncAudioCapture.Dispose();
- }
-
- internal void PauseRecording()
- {
- asyncAudioCapture.Pause();
- }
-
- internal void ResumeRecording()
- {
- asyncAudioCapture.Resume();
- }
-
- private void OnRecordBufferChanged(object sender, RecordBufferChangedEventArgs e)
- {
- bufferAction?.Invoke(e.RecordedBuffer, CurrentAudioOptions.SampleRate);
- }
-
- private bool AudioRecordingTimerTick(object source, Timer.TickEventArgs e)
- {
- Log.Info(LogTag, "TickTimer");
- audioRecdingAction?.Invoke();
- return true;
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-
-namespace Tizen.AIAvatar
-{
- internal class RecordBufferChangedEventArgs : EventArgs
- {
- public byte[] RecordedBuffer { get; set; }
- public int SampleRate { get; set; }
-
- public RecordBufferChangedEventArgs(byte[] recordedBuffer, int SampleRate)
- {
- this.RecordedBuffer = recordedBuffer;
- this.SampleRate = SampleRate;
- }
- }
-
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.Collections.Generic;
-using Tizen.Uix.Tts;
-using System.Runtime.InteropServices;
-
-using static Tizen.AIAvatar.AIAvatar;
-using System.Linq;
-using Tizen.NUI;
-
-namespace Tizen.AIAvatar
-{
- internal class TTSLipSyncer
- {
-
- private Avatar currentAvatar;
-
- private List<UtteranceText> textList;
- private TtsClient ttsHandle;
- private VoiceInfo voiceInfo;
- private List<Byte> byteList;
-
- private byte[] recordedBuffer;
- private byte[] audioTailBuffer;
-
- private int sampleRate;
- private float desiredBufferDuration = 0.175f;
- private float audioTailLengthFactor = 0.015f;
- private float audioBufferMultiflier = 2f;
-
- private int desiredBufferLength;
- private int audioTailLength;
-
- private bool isPrepared = false;
- private bool isAsync = false;
-
- private Action<byte[], int> bufferChangedAction;
-
- private int audioLength;
- private bool isAsyncLipStarting;
-
- private AsyncLipSyncer lipSyncer;
-
-
- internal TTSLipSyncer(AsyncLipSyncer lipSyncer)
- {
- this.lipSyncer = lipSyncer;
- InitTts();
- }
-
- ~TTSLipSyncer()
- {
- DeinitTts();
- }
-
- internal event EventHandler PlayReadyCallback;
-
- internal TtsClient TtsHandle
- {
- get { return ttsHandle; }
- }
-
- internal VoiceInfo VoiceInfo
- {
- get { return voiceInfo; }
- set
- {
- voiceInfo = value;
- }
- }
-
- internal List<VoiceInfo> GetSupportedVoices()
- {
- var voiceInfoList = new List<VoiceInfo>();
-
- if (ttsHandle == null)
- {
- Log.Error(LogTag, $"ttsHandle is null");
- return voiceInfoList;
- }
-
- var supportedVoices = ttsHandle.GetSupportedVoices();
- foreach (var supportedVoice in supportedVoices)
- {
- Log.Info(LogTag, $"{supportedVoice.Language} & {supportedVoice.VoiceType} is supported");
- voiceInfoList.Add(new VoiceInfo() { Lang = supportedVoice.Language, Type = supportedVoice.VoiceType });
- }
- return voiceInfoList;
- }
-
- internal bool IsSupportedVoice(string lang)
- {
- if (ttsHandle == null)
- {
- Log.Error(LogTag, $"ttsHandle is null");
- return false;
- }
- var supportedVoices = ttsHandle.GetSupportedVoices();
-
- foreach (var supportedVoice in supportedVoices)
- {
- if (supportedVoice.Language.Equals(lang))
- {
- Log.Info(LogTag, $"{lang} is supported");
- return true;
- }
- }
- return false;
- }
-
- internal bool IsSupportedVoice(VoiceInfo voiceInfo)
- {
- if (ttsHandle == null)
- {
- Log.Error(LogTag, $"ttsHandle is null");
- return false;
- }
- var supportedVoices = ttsHandle.GetSupportedVoices();
- foreach (var supportedVoice in supportedVoices)
- {
- if (supportedVoice.Language.Equals(voiceInfo.Lang) && (supportedVoice.VoiceType == voiceInfo.Type))
- {
- Log.Info(LogTag, $"{voiceInfo.Lang} & {voiceInfo.Type} is supported");
- return true;
- }
- }
- return false;
- }
-
-
- internal void AddText(string txt, VoiceInfo voiceInfo)
- {
- if (voiceInfo.Lang == null || voiceInfo.Type == null)
- {
- Log.Error(LogTag, "VoiceInfo's value is null");
- }
- if (ttsHandle == null)
- {
- Log.Error(LogTag, $"ttsHandle is null");
- return;
- }
- var temp = new UtteranceText();
- temp.Text = txt;
- temp.UttID = ttsHandle.AddText(txt, voiceInfo.Lang, (int)voiceInfo.Type, 0);
- try
- {
- textList.Add(temp);
- }
- catch (Exception e)
- {
- Log.Error(LogTag, $"Error AddText" + e.Message);
- }
- }
-
- internal void AddText(string txt, string lang)
- {
- if (ttsHandle == null)
- {
- Log.Error(LogTag, $"ttsHandle is null");
- return;
- }
- var temp = new UtteranceText();
- temp.Text = txt;
- temp.UttID = ttsHandle.AddText(txt, lang, (int)voiceInfo.Type, 0);
- try
- {
- textList.Add(temp);
- }
- catch (Exception e)
- {
- Log.Error(LogTag, $"Error AddText" + e.Message);
- }
- }
-
- internal void Prepare(EventHandler playReadyCallback)
- {
- if (ttsHandle == null)
- {
- Log.Error(LogTag, $"ttsHandle is null");
- return;
- }
- Log.Info(LogTag, "Prepare TTS");
- isPrepared = true;
- isAsync = false;
- PlayReadyCallback = playReadyCallback;
- Play(true);
- }
-
- internal bool PlayPreparedText()
- {
- if (byteList != null && byteList.Count != 0)
- {
- Log.Info(LogTag, "PlayPreparedText TTS");
- currentAvatar?.AvatarAnimator?.PlayLipSync(byteList.ToArray(), sampleRate);
- return true;
- }
- return false;
- }
-
- internal void Play(bool isPrepared = false)
- {
- if (ttsHandle == null)
- {
- Log.Error(LogTag, $"ttsHandle is null");
- return;
- }
-
- this.isPrepared = isPrepared;
- isAsync = false;
- ttsHandle.Play();
- }
-
- internal void PlayAsync(EventHandler playReadyCallback)
- {
- if (ttsHandle == null)
- {
- Log.Error(LogTag, $"ttsHandle is null");
- return;
- }
-
- isPrepared = false;
- isAsync = true;
- PlayReadyCallback = playReadyCallback;
- ttsHandle.Play();
- }
-
- public void Pause()
- {
- if (ttsHandle == null)
- {
- Log.Error(LogTag, $"ttsHandle is null");
- return;
- }
- ttsHandle.Pause();
- }
-
- internal void Stop()
- {
- if (ttsHandle == null)
- {
- Log.Error(LogTag, $"ttsHandle is null");
- return;
- }
- ttsHandle.Stop();
- currentAvatar?.AvatarAnimator?.StopLipSync();
- }
-
- private void InitTts()
- {
- try
- {
- ttsHandle = new TtsClient();
-
- // Register Callbacks
- ttsHandle.DefaultVoiceChanged += TtsDefaultVoiceChangedCallback;
- ttsHandle.EngineChanged += TtsEngineChangedCallback;
- ttsHandle.ErrorOccurred += TtsErrorOccuredCallback;
- ttsHandle.StateChanged += TtsStateChangedCallback;
- ttsHandle.UtteranceCompleted += TtsUtteranceCompletedCallback;
- ttsHandle.UtteranceStarted += TtsUtteranceStartedCallback;
-
- ttsHandle.SynthesizedPcm += TtsSyntheiszedPCM;
- ttsHandle.PlayingMode = PlayingMode.ByClient;
-
- ttsHandle.Prepare();
-
- voiceInfo = new VoiceInfo
- {
- Lang = ttsHandle.DefaultVoice.Language,
- Type = ttsHandle.DefaultVoice.VoiceType
- };
-
- textList = new List<UtteranceText>();
- Log.Info(LogTag, voiceInfo.Lang + ", " + voiceInfo.Type.ToString());
-
- }
- catch (Exception e)
- {
- Log.Error(LogTag, "[ERROR] Fail to prepare Tts");
- Log.Error(LogTag, e.Message);
- }
- }
-
- internal void DeinitTts()
- {
- try
- {
- if (ttsHandle != null)
- {
- ttsHandle.Unprepare();
-
- // Unregister Callbacks
- ttsHandle.DefaultVoiceChanged -= TtsDefaultVoiceChangedCallback;
- ttsHandle.EngineChanged -= TtsEngineChangedCallback;
- ttsHandle.ErrorOccurred -= TtsErrorOccuredCallback;
- ttsHandle.StateChanged -= TtsStateChangedCallback;
- ttsHandle.UtteranceCompleted -= TtsUtteranceCompletedCallback;
- ttsHandle.UtteranceStarted -= TtsUtteranceStartedCallback;
-
- ttsHandle.Dispose();
- ttsHandle = null;
- }
-
- if (textList != null)
- {
- textList.Clear();
- textList = null;
- }
-
- if (byteList != null)
- {
- byteList.Clear();
- byteList = null;
- }
- currentAvatar = null;
- }
- catch (Exception e)
- {
- Log.Error(LogTag, "[ERROR] Fail to unprepare Tts");
- Log.Error(LogTag, e.Message);
- }
- }
-
- private void TtsSyntheiszedPCM(object sender, SynthesizedPcmEventArgs e)
- {
-
- var dataSize = e.Data.Length;
- var audio = new byte[dataSize];
- sampleRate = e.SampleRate;
-
- //Marshal.Copy(e.Data, audio, 0, dataSize);
- switch (e.EventType) //START
- {
- case SynthesizedPcmEvent.Start://start
- Tizen.Log.Info(LogTag, "------------------Start : " + e.UtteranceId);
- Tizen.Log.Info(LogTag, "Output audio Size : " + dataSize);
- Tizen.Log.Info(LogTag, "SampleRate" + e.SampleRate);
- if (byteList == null)
- {
- byteList = new List<byte>();
- }
- if (recordedBuffer == null)
- {
- recordedBuffer = new byte[0];
- }
- byteList.Clear();
-
- if (isAsync)
- {
- recordedBuffer = Array.Empty<byte>();
-
- desiredBufferLength = (int)(e.SampleRate * desiredBufferDuration * audioBufferMultiflier);
- audioTailLength = (int)(sampleRate * audioTailLengthFactor * audioBufferMultiflier);
- audioTailBuffer = new byte[audioTailLength];
- PlayReadyCallback?.Invoke(null, EventArgs.Empty);
- InitAsyncBuffer();
- lipSyncer.SampleRate = sampleRate;
- }
- break;
- case SynthesizedPcmEvent.Continue://continue
- if (isAsync)
- {
- recordedBuffer = recordedBuffer.Concat(e.Data).ToArray();
- //PlayAsync
- if (recordedBuffer.Length >= desiredBufferLength)
- {
- Tizen.Log.Error(LogTag, "Current recordbuffer length :" + recordedBuffer.Length);
- UpdateBuffer(recordedBuffer, sampleRate);
-
- Buffer.BlockCopy(recordedBuffer, recordedBuffer.Length - audioTailLength, audioTailBuffer, 0, audioTailLength);
-
- recordedBuffer = Array.Empty<byte>();
- recordedBuffer = recordedBuffer.Concat(audioTailBuffer).ToArray();
- Array.Clear(audioTailBuffer, 0, audioTailLength);
- }
- }
- else
- {
- byteList.AddRange(e.Data);
- }
- break;
- case SynthesizedPcmEvent.Finish://finish
- Tizen.Log.Info(LogTag, "------------------Finish : " + e.UtteranceId);
- if (!isAsync)
- {
- if (!isPrepared)
- {
- //Play voice immediately
- //PlayPreparedText();
- }
- else
- {
- //Notify finished state
- Log.Info(LogTag, "Notify finished state");
- PlayReadyCallback?.Invoke(null, EventArgs.Empty);
- }
- }
- else
- {
- lipSyncer.SetFinishAsyncLip(true);
- }
- break;
- case SynthesizedPcmEvent.Fail: //fail
- break;
-
- }
- }
-
- private void TtsUtteranceStartedCallback(object sender, UtteranceEventArgs e)
- {
- Log.Debug(LogTag, "Utterance start now (" + e.UtteranceId + ")");
- }
-
- private void TtsUtteranceCompletedCallback(object sender, UtteranceEventArgs e)
- {
- Log.Debug(LogTag, "Utterance complete (" + e.UtteranceId + ")");
-
- foreach (UtteranceText item in textList)
- {
- if (item.UttID == e.UtteranceId)
- {
- textList.Remove(item);
- Log.Debug(LogTag, "TextList Count (" + textList.Count.ToString() + ")");
- break;
- }
- }
- }
-
- private void TtsStateChangedCallback(object sender, StateChangedEventArgs e)
- {
- Log.Debug(LogTag, "Current state is changed from (" + e.Previous + ") to (" + e.Current + ")");
- }
-
- private void TtsErrorOccuredCallback(object sender, ErrorOccurredEventArgs e)
- {
- Log.Error(LogTag, "Error is occured (" + e.ErrorMessage + ")");
- }
-
- private void TtsEngineChangedCallback(object sender, EngineChangedEventArgs e)
- {
- Log.Debug(LogTag, "Prefered engine is changed (" + e.EngineId + ") (" + e.VoiceType.Language + ")");
- }
-
- private void TtsDefaultVoiceChangedCallback(object sender, DefaultVoiceChangedEventArgs e)
- {
- Log.Debug(LogTag, "Default voice is changed from (" + e.Previous + ") to (" + e.Current + ")");
- }
-
- internal void InitAsyncBuffer()
- {
- if (!lipSyncer.IsAsyncInit)
- {
- audioLength = (int)(sampleRate * 0.16f * 2f);
-
- lipSyncer.InitAsyncLipsync();
- lipSyncer.IsAsyncInit = true;
-
- lipSyncer.SetFinishAsyncLip(false);
- isAsyncLipStarting = false;
- }
- }
-
- internal void UpdateBuffer(byte[] recordBuffer, int sampleRate)
- {
- if (lipSyncer != null)
- {
- Log.Error(LogTag, "OnTTSBufferChanged");
- lipSyncer.EnqueueAnimation(recordBuffer, sampleRate, audioLength);
- if (!isAsyncLipStarting)
- {
- lipSyncer.StartAsyncLipPlayTimer();
- isAsyncLipStarting = true;
- }
- }
- else
- {
- Log.Error(LogTag, "avatarLipSyncer is null");
- }
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-namespace Tizen.AIAvatar
-{
- internal struct UtteranceText
- {
- internal string Text;
- internal int UttID;
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2024 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.ComponentModel;
-using Tizen.NUI;
-using Tizen.NUI.Scene3D;
-
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- public abstract class AnimationModule
- {
- private AvatarMotionState currentMotionState = AvatarMotionState.Unavailable;
-
- private readonly Object motionChangedLock = new Object();
- private event EventHandler<AvatarMotionChangedEventArgs> motionChanged;
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AnimationModule()
- {
-
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarMotionState CurrentMotionState
- {
- get
- {
- return currentMotionState;
- }
- set
- {
- if (currentMotionState == value)
- {
- return;
- }
- var preState = currentMotionState;
- currentMotionState = value;
- if (motionChanged != null)
- {
- motionChanged?.Invoke(this, new AvatarMotionChangedEventArgs(preState, currentMotionState));
- }
- }
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public abstract void Init(Animation animation);
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public abstract void Play(IAnimationModuleData data);
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public abstract void Stop();
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public abstract void Pause();
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public abstract void Destroy();
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- protected void SetCurrentState(AvatarMotionState state)
- {
- CurrentMotionState = state;
- }
-
-
- /// <summary>
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public event EventHandler<AvatarMotionChangedEventArgs> MotionStateChanged
- {
- add
- {
- lock (motionChangedLock)
- {
- motionChanged += value;
- }
-
- }
-
- remove
- {
- lock (motionChangedLock)
- {
- if (motionChanged == null)
- {
- Log.Error(LogTag, "Remove StateChanged Failed : motionChanged is null");
- return;
- }
- motionChanged -= value;
- }
- }
- }
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public enum AnimationModuleType
- {
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeBlinker,
- [EditorBrowsable(EditorBrowsableState.Never)]
- LipSyncer,
- [EditorBrowsable(EditorBrowsableState.Never)]
- MotionBehavior,
- [EditorBrowsable(EditorBrowsableState.Never)]
- JointTransformer,
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2024 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using Tizen.NUI;
-
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal class AsyncLipSyncer : LipSyncer
- {
- private readonly uint AsyncPlayTime = 160;
- private Queue<Animation> lipAnimations;
- private Queue<byte[]> lipAudios;
- private Timer asyncVowelTimer;
-
- private bool isAsyncInit = false;
- private bool isFinishAsyncLip = false;
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal AsyncLipSyncer()
- {
-
- }
-
- internal int SampleRate { get; set; }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal bool IsAsyncInit { get=>isAsyncInit; set=>isAsyncInit = value; }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal void SetFinishAsyncLip(bool finished)
- {
- isFinishAsyncLip = finished;
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- protected new void OnLipAnimationFinished(object sender, EventArgs e)
- {
- if (!isAsyncInit)
- {
- CurrentMotionState = AvatarMotionState.Stopped;
- }
- else
- {
- Tizen.Log.Error(LogTag, "OnLipAnimationFinished---------------c");
- //Async State
- if (isFinishAsyncLip && lipAnimations.Count == 0)
- {
- Tizen.Log.Error(LogTag, "Finish vowel lip sync");
-
- AudioPlayer.Stop();
- CurrentMotionState = AvatarMotionState.Stopped;
- DestroyVowelTimer();
- isAsyncInit = false;
- }
- }
- }
-
- internal void InitAsyncLipsync()
- {
- if (lipAnimations == null)
- {
- lipAnimations = new Queue<Animation>();
- }
- else
- {
- lipAnimations.Clear();
- }
-
- if (lipAudios == null)
- {
- lipAudios = new Queue<byte[]>();
- }
- else
- {
- lipAudios.Clear();
- }
- }
-
- internal void EnqueueAnimation(byte[] recordBuffer, int sampleRate, int audioLength)
- {
- var createdAni = CreateAsyncLipAnimation(recordBuffer, sampleRate);
- if (createdAni != null)
- {
- lipAnimations.Enqueue(createdAni);
- }
-
- //Use Audio Full File
- ///var createdAni = avatarLipSyncer.CreateLipAnimation(recordBuffer, sampleRate);
- //lipAnimations.Enqueue(createdAni);
-
- var currentAudioBuffer = new byte[audioLength];
- Buffer.BlockCopy(recordBuffer, 0, currentAudioBuffer, 0, audioLength);
-
- lipAudios.Enqueue(currentAudioBuffer);
- }
-
- internal bool PlayAsyncLip(int sampleRate, bool isFinishAsyncLip)
- {
- try
- {
- if (lipAudios.Count <= 0 && lipAnimations.Count <= 0)
- {
- Tizen.Log.Info(LogTag, "Return lipaudio 0");
- if (isFinishAsyncLip)
- {
- Tizen.Log.Info(LogTag, "Finish Async lipsync");
- return false;
- }
- return true;
- }
- Tizen.Log.Info(LogTag, "Async timer tick lipAudios : " + lipAudios.Count);
- Tizen.Log.Info(LogTag, "Async timer tick lipAnimations : " + lipAnimations.Count);
-
- var lipAnimation = lipAnimations.Dequeue();
- lipAnimation.Finished += OnLipAnimationFinished;
-
- ResetLipAnimation(lipAnimation);
- PlayLipAnimation();
- var audioBuffer = lipAudios.Dequeue();
- AudioPlayer.PlayAsync(audioBuffer, sampleRate);
- return true;
-
- }
- catch (Exception ex)
- {
- Log.Error(LogTag, $"---Log Tick : {ex.StackTrace}");
-
- return false;
- }
- }
-
- internal void StartAsyncLipPlayTimer()
- {
- if (asyncVowelTimer == null)
- {
- Tizen.Log.Info(LogTag, "Start Async");
- asyncVowelTimer = new Timer(AsyncPlayTime);
- asyncVowelTimer.Tick += OnAsyncVowelTick;
- asyncVowelTimer.Start();
- }
- return;
- }
-
- private void DestroyVowelTimer()
- {
- if (asyncVowelTimer != null)
- {
-
- asyncVowelTimer.Tick -= OnAsyncVowelTick;
- asyncVowelTimer.Stop();
- asyncVowelTimer.Dispose();
- asyncVowelTimer = null;
- }
- }
-
- private bool OnAsyncVowelTick(object source, Tizen.NUI.Timer.TickEventArgs e)
- {
- return PlayAsyncLip(SampleRate, isFinishAsyncLip);
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2024 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-using Tizen.NUI;
-using Tizen.NUI.Scene3D;
-
-namespace Tizen.AIAvatar
-{
- internal class AvatarMotions
- {
- private MotionData eyeMotionData;
- private const int blinkDuration = 200;
-
- internal AvatarMotions(AvatarProperties properties)
- {
- CreateEyeBlinkMotionData(properties);
- }
-
- internal MotionData EyeMotionData { get { return eyeMotionData; } }
-
- private void CreateEyeBlinkMotionData(AvatarProperties avatarProperties)
- {
- var keyFrames = new KeyFrames();
- keyFrames.Add(0.1f, 0.0f);
- keyFrames.Add(0.5f, 1.0f);
- keyFrames.Add(0.9f, 0.0f);
-
- var headBlendShapeEyeLeft = new AvatarBlendShapeIndex(avatarProperties.NodeMapper, NodeType.HeadGeo, avatarProperties.BlendShapeMapper, BlendShapeType.EyeBlinkLeft);
- var headBlendShapeEyeRight = new AvatarBlendShapeIndex(avatarProperties.NodeMapper, NodeType.HeadGeo, avatarProperties.BlendShapeMapper, BlendShapeType.EyeBlinkRight);
- var eyelashBlendShapeEyeLeft = new AvatarBlendShapeIndex(avatarProperties.NodeMapper, NodeType.EyelashGeo, avatarProperties.BlendShapeMapper, BlendShapeType.EyeBlinkLeft);
- var eyelashBlendShapeEyeRight = new AvatarBlendShapeIndex(avatarProperties.NodeMapper, NodeType.EyelashGeo, avatarProperties.BlendShapeMapper, BlendShapeType.EyeBlinkRight);
-
- eyeMotionData = new MotionData(blinkDuration);
- if (headBlendShapeEyeLeft != null)
- {
- eyeMotionData.Add(headBlendShapeEyeLeft, new MotionValue(keyFrames));
- }
- if (headBlendShapeEyeRight != null)
- {
- eyeMotionData.Add(headBlendShapeEyeRight, new MotionValue(keyFrames));
- }
- if (eyelashBlendShapeEyeLeft != null)
- {
- eyeMotionData.Add(eyelashBlendShapeEyeLeft, new MotionValue(keyFrames));
- }
- if (eyelashBlendShapeEyeRight != null)
- {
- eyeMotionData.Add(eyelashBlendShapeEyeRight, new MotionValue(keyFrames));
- }
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Reflection;
-using Tizen.NUI.Scene3D;
-using Tizen.NUI;
-
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
- internal class BlendShapePlayer
- {
- private Dictionary<AnimationModuleType, AnimationModule> animationModules = new Dictionary<AnimationModuleType, AnimationModule>();
-
- private EyeBlinker blinker;
- private AsyncLipSyncer lipSyncer;
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public BlendShapePlayer()
- {
- blinker = new EyeBlinker();
- lipSyncer = new AsyncLipSyncer();
-
- animationModules.Add(AnimationModuleType.EyeBlinker, blinker);
- animationModules.Add(AnimationModuleType.LipSyncer, lipSyncer);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AsyncLipSyncer LipSyncer => lipSyncer;
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void SetBlinkAnimation(Animation blinkerAnimation)
- {
- animationModules[AnimationModuleType.EyeBlinker].Init(blinkerAnimation);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void SetLipSyncAnimation(Animation lipsyncAnimation)
- {
- animationModules[AnimationModuleType.LipSyncer].Init(lipsyncAnimation);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AnimationModule GetAnimationModule(AnimationModuleType type)
- {
- return animationModules[type];
- }
-
- /// <summary>
- /// Start eye blink animation
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void StartEyeBlink()
- {
- animationModules[AnimationModuleType.EyeBlinker]?.Play(null);
- }
-
- /// <summary>
- /// Stop eye blink animation
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void StopEyeBlink()
- {
- animationModules[AnimationModuleType.EyeBlinker]?.Stop();
- }
-
- /// <summary>
- /// Play synchronization avatar lip based on the voice file(byte[])
- /// </summary>
- /// <param name="audio">byte array of voice</param>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void PlayLipSync(byte[] audio)
- {
- if (animationModules[AnimationModuleType.LipSyncer] == null)
- {
- Log.Error(LogTag, $"error : avatarLipSync is null");
- return;
- }
- var lipData = new LipSyncData();
-
- lipData.AudioFile = new byte[audio.Length];
- Buffer.BlockCopy(audio, 0, lipData.AudioFile, 0, audio.Length);
- animationModules[AnimationModuleType.LipSyncer].Play(lipData);
- }
-
- /// <summary>
- /// Play synchronization avatar lip based on the voice file(byte[])
- /// </summary>
- /// <param name="audio">byte array of voice</param>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void PlayLipSync(byte[] audio, int sampleRate)
- {
- if (animationModules[AnimationModuleType.LipSyncer] == null)
- {
- Log.Error(LogTag, $"error : avatarLipSync is null");
- return;
- }
- var lipData = new LipSyncData();
-
- lipData.AudioFile = new byte[audio.Length];
- Buffer.BlockCopy(audio, 0, lipData.AudioFile, 0, audio.Length);
- lipData.SampleRate = sampleRate;
- animationModules[AnimationModuleType.LipSyncer].Play(lipData);
- }
-
- /// <summary>
- /// Pause lip synchronization
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void PauseLipSync()
- {
- if (animationModules[AnimationModuleType.LipSyncer] == null)
- {
- Log.Error(LogTag, $"error : avatarLipSync is null");
- return;
- }
- animationModules[AnimationModuleType.LipSyncer].Pause();
- }
-
- /// <summary>
- /// Stop lip synchronization
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void StopLipSync()
- {
- if (animationModules[AnimationModuleType.LipSyncer] == null)
- {
- Log.Error(LogTag, $"error : avatarLipSync is null");
- return;
- }
- animationModules[AnimationModuleType.LipSyncer].Stop();
- }
-
- internal void DestroyAnimations()
- {
- foreach ( var animationModule in animationModules.Values )
- {
- animationModule.Destroy();
- }
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.ComponentModel;
-using Tizen.NUI;
-using Tizen.NUI.Scene3D;
-
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal class EyeBlinker : AnimationModule
- {
- private const int blinkIntervalMinimum = 800;
- private const int blinkIntervalMaximum = 3000;
- private Animation eyeAnimation;
-
- private Timer blinkTimer;
-
- private bool isPlaying = false;
- private const int blinkDuration = 200;
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal EyeBlinker()
- {
-
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public override void Init(Animation eyeAnimation)
- {
- this.eyeAnimation = eyeAnimation;
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public override void Play(IAnimationModuleData data)
- {
- //data
- StartEyeBlink();
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public override void Stop()
- {
- StopEyeBlink();
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public override void Pause()
- {
- eyeAnimation?.Pause();
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public override void Destroy()
- {
- DestroyAnimation();
- }
-
- private void StartEyeBlink()
- {
- DestroyBlinkTimer();
-
- blinkTimer = new Timer(blinkDuration);
- if (blinkTimer != null)
- {
- blinkTimer.Tick += OnBlinkTimer;
- blinkTimer?.Start();
- isPlaying = true;
- }
- }
-
- private void PauseEyeBlink()
- {
- blinkTimer?.Stop();
- isPlaying = false;
- }
-
- private void StopEyeBlink()
- {
- blinkTimer?.Stop();
- isPlaying = false;
- }
-
- private void DestroyAnimation()
- {
- DestroyBlinkTimer();
- if (eyeAnimation != null)
- {
- eyeAnimation.Stop();
- eyeAnimation.Dispose();
- eyeAnimation = null;
- }
- isPlaying = false;
- }
-
- private bool OnBlinkTimer(object source, Timer.TickEventArgs e)
- {
- if (eyeAnimation == null)
- {
- Log.Error(LogTag, "eye animation is not ready");
- return false;
- }
- eyeAnimation?.Play();
-
- var random = new Random();
- var fortimerinterval = (uint)random.Next(blinkIntervalMinimum, blinkIntervalMaximum);
- blinkTimer.Interval = fortimerinterval;
- return true;
- }
-
- private void DestroyBlinkTimer()
- {
- if (blinkTimer != null)
- {
- blinkTimer.Tick -= OnBlinkTimer;
- blinkTimer.Stop();
- blinkTimer.Dispose();
- blinkTimer = null;
- }
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.ComponentModel;
-using Tizen.NUI;
-using Tizen.NUI.Scene3D;
-
-namespace Tizen.AIAvatar
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal class JointTransformer
- {
- [EditorBrowsable(EditorBrowsableState.Never)]
- public JointTransformer()
- {
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Init(Animation animation)
- {
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Play(IAnimationModuleData data)
- {
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Pause()
- {
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Stop()
- {
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Destroy()
- {
- }
-
- private void SetJointMotion(AvatarProperties properties, JointType jointType, MotionTransformIndex.TransformTypes type, Rotation rotation)
- {
- var motionTransform = new AvatarJointTransformIndex(properties.JointMapper, jointType, type);
- var motionData = new MotionData();
- motionData.Add(
- motionTransform,
- new MotionValue()
- {
- //TODO : Tizen_7.0에 pitch, yaw, roll patch 추가하기
- //PropertyValue = new PropertyValue(new Rotation(new Radian(pitch), new Radian(yaw), new Radian(roll))),
- }
- );
- //avatar.SetMotionData(motionData);
- }
-
- private void SetJointMotion(string keyValue, float pitch, float yaw, float roll)
- {
- var motionData = new MotionData();
- motionData.Add(
- new MotionTransformIndex()
- {
- ModelNodeId = new PropertyKey(keyValue),
- TransformType = MotionTransformIndex.TransformTypes.Orientation,
- },
- new MotionValue()
- {
- //TODO : Tizen_7.0에 pitch, yaw, roll patch 추가하기
- //PropertyValue = new PropertyValue(new Rotation(new Radian(pitch), new Radian(yaw), new Radian(roll))),
- }
- );
- //avatar.SetMotionData(motionData);
- }
-
- private void SetJointMotion(string keyValue, MotionTransformIndex.TransformTypes type, Rotation rotation)
- {
- var motionData = new MotionData();
- motionData.Add(
- new MotionTransformIndex()
- {
- ModelNodeId = new PropertyKey(keyValue),
- TransformType = type,
- },
- new MotionValue()
- {
- PropertyValue = new PropertyValue(rotation),
- }
- );
- //avatar.SetMotionData(motionData);
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using Tizen.NUI;
-using Tizen.NUI.Scene3D;
-
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal class LipSyncer : AnimationModule
- {
- private Avatar avatar;
- private Animation lipAnimation = null;
-
- //private VowelConverter vowelConverter;
- private AudioPlayer audioPlayer;
-
- //Mic
- private Queue<string[]> vowelPools = new Queue<string[]>();
- private string prevVowel = "sil";
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal LipSyncer()
- {
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal AudioPlayer AudioPlayer { get { return audioPlayer; } }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public override void Init(Animation lipAnimation)
- {
- this.lipAnimation = lipAnimation;
- //vowelConverter = new VowelConverter();
- audioPlayer = new AudioPlayer();
-
- CurrentMotionState = AvatarMotionState.Ready;
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public override void Play(IAnimationModuleData data)
- {
- if (data is LipSyncData lipSyncData)
- {
- if (lipSyncData.AudioFile != null)
- {
- PlayLipSync(lipSyncData.AudioFile, lipSyncData.SampleRate);
- }
- }
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public override void Stop()
- {
- StopLipSync();
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public override void Pause()
- {
- PauseLipSync();
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public override void Destroy()
- {
- DestroyLipAnimation();
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- protected Animation CreateAsyncLipAnimation(byte[] buffer, int sampleRate)
- {
- EnqueueVowels(buffer, false);
- return CreateLipAnimationByVowelsQueue(sampleRate);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- //TODO : lipAnimation 자체를 Animator안에서 생성하고 관리될 수 있게 수정.
- protected void ResetLipAnimation(Animation lipAnimation)
- {
- DestroyLipAnimation();
- this.lipAnimation = lipAnimation;
- if (this.lipAnimation != null)
- {
- this.lipAnimation.Finished += OnLipAnimationFinished;
- }
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- protected void PlayLipAnimation()
- {
- if (lipAnimation == null)
- {
- Log.Error(LogTag, "Current Lip Animation is null");
- }
- lipAnimation?.Play();
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- protected void OnLipAnimationFinished(object sender, EventArgs e)
- {
- CurrentMotionState = AvatarMotionState.Stopped;
- }
-
- private void PlayLipSync(byte[] audio)
- {
- if (audio == null)
- {
- Tizen.Log.Error(LogTag, "audi data is null");
- return;
- }
-
- DestroyLipAnimation();
- var lipAnimation = CreateLipAnimation(audio, CurrentAudioOptions.SampleRate);
- if (lipAnimation != null)
- {
- ResetLipAnimation(lipAnimation);
- PlayLipAnimation();
- }
- else
- {
-
- Tizen.Log.Error(LogTag, "lipAnimation is null");
- }
- audioPlayer.Play(audio);
- CurrentMotionState = AvatarMotionState.Playing;
- }
-
- private void PlayLipSync(byte[] audio, int sampleRate)
- {
- DestroyLipAnimation();
- var lipAnimation = CreateLipAnimation(audio, sampleRate);
- if (lipAnimation != null)
- {
- ResetLipAnimation(lipAnimation);
- PlayLipAnimation();
- }
- audioPlayer.Play(audio, sampleRate);
- CurrentMotionState = AvatarMotionState.Playing;
- }
-
- private void PlayLipSync(string path)
- {
- var bytes = Utils.ReadAllBytes(path);
- if (bytes != null)
- {
- PlayLipSync(bytes);
- }
- else
- {
- Log.Error(LogTag, "Failed to load audio file");
- }
- }
-
- private void PauseLipSync()
- {
- PauseLipAnimation();
- audioPlayer.Pause();
- CurrentMotionState = AvatarMotionState.Paused;
- }
-
- private void StopLipSync()
- {
- StopLipAnimation();
- audioPlayer.Stop();
- CurrentMotionState = AvatarMotionState.Stopped;
- }
-
- private void PauseLipAnimation()
- {
- if (lipAnimation != null)
- {
- lipAnimation?.Pause();
- }
- else
- {
- Log.Error(LogTag, "Current Lip Animation is null");
- }
- }
-
- private void StopLipAnimation()
- {
- if (lipAnimation != null)
- {
- DestroyLipAnimation();
-
- var lipAnimation = ResetLipAnimation();
- if (lipAnimation != null)
- {
- ResetLipAnimation(lipAnimation);
- PlayLipAnimation();
- }
- else
- {
- Log.Error(LogTag, "Current Lip Animation is null");
- }
- }
- else
- {
- Log.Error(LogTag, "Current Lip Animation is null");
- }
- }
-
- private void DestroyLipAnimation()
- {
- if (lipAnimation != null)
- {
- lipAnimation.Stop();
- lipAnimation.Dispose();
- lipAnimation = null;
- }
- }
-
- private Animation CreateLipAnimation(byte[] array, int sampleRate)
- {
- Animation lipKeyframes = null;// CreateKeyFrame(array, sampleRate);
- if (lipKeyframes != null)
- {
-
- return null;// CreateLipAnimation(lipKeyframes);
- }
- return null;
- }
-
- private void EnqueueVowels(byte[] buffer, bool deleteLast = false)
- {
- var vowels = PredictVowels(buffer);
- if (vowels != null)
- {
- vowelPools.Enqueue(vowels);
- if (deleteLast)
- {
- var vowelList = new List<string>(vowels);
- vowelList.RemoveAt(vowelList.Count - 1);
- vowels = vowelList.ToArray();
- }
- }
- }
-
- private Animation CreateLipAnimationByVowelsQueue(int sampleRate = 0)
- {
- if (sampleRate == 0)
- {
- sampleRate = CurrentAudioOptions.SampleRate;
- }
- if (vowelPools.Count > 0)
- {
- AttachPreviousVowel(vowelPools.Dequeue(), out var newVowels);
- Log.Info(LogTag, $"vowelRecognition: {String.Join(", ", newVowels)}");
-
- //var lipKeyFrames = vowelConverter?.CreateKeyFrames(newVowels, sampleRate, true);
- return null;// CreateLipAnimation(lipKeyFrames, true);
- }
- return null;
- }
-
- private Animation ResetLipAnimation()
- {
- vowelPools.Clear();
- var newVowels = new string[1];
- newVowels[0] = prevVowel = "sil";
- vowelPools.Enqueue(newVowels);
- return CreateLipAnimationByVowelsQueue();
- }
-
- private string[] PredictVowels(byte[] audioData)
- {
- string[] vowels = null;// vowelConverter?.PredictVowels(audioData);
- return vowels;
- }
-
- private void AttachPreviousVowel(in string[] vowels, out string[] newVowels)
- {
- newVowels = new string[vowels.Length + 1];
- newVowels[0] = prevVowel;
- Array.Copy(vowels, 0, newVowels, 1, vowels.Length);
- prevVowel = vowels[vowels.Length - 1];
- }
-
- /*
- private AnimationKeyFrame CreateKeyFrame(byte[] audio, int sampleRate)
- {
- var keyFrames = vowelConverter?.CreateKeyFrames(audio, sampleRate);
- if (keyFrames == null)
- {
- Log.Error(LogTag, $"Failed to initialize KeyFrames");
- }
-
- return keyFrames;
- }
-
- private Animation CreateLipAnimation(AnimationKeyFrame animationKeyFrames, bool isMic = false)
- {
- var animationTime = (int)(animationKeyFrames.AnimationTime * 1000f);
- var lipAnimation = new Animation(animationTime);
-
- var motionData = new MotionData(animationTime);
- for (var i = 0; i < animationKeyFrames.NodeNames.Length; i++)
- {
- var nodeName = animationKeyFrames.NodeNames[i];
- for (var j = 0; j < animationKeyFrames.BlendShapeCounts[i]; j++)
- {
- var blendShapeIndex = new BlendShapeIndex(new PropertyKey(nodeName), new PropertyKey(j));
- var keyFrameList = animationKeyFrames.GetKeyFrames(nodeName, j);
- if (keyFrameList.Count == 0)
- {
- continue;
- }
-
- var keyFrames = new KeyFrames();
- CreateKeyTimeFrames(ref keyFrames, keyFrameList, isMic);
-
- motionData.Add(blendShapeIndex, new MotionValue(keyFrames));
- lipAnimation = avatar.GenerateMotionDataAnimation(motionData);
- }
- }
- return lipAnimation;
- }
- private KeyFrames CreateKeyTimeFrames(ref KeyFrames keyFrames, List<KeyFrame> keyFrameList, bool isMic = false)
- {
- foreach (var key in keyFrameList)
- {
- keyFrames.Add(key.time, key.value);
- }
- if (!isMic) keyFrames.Add(1.0f, 0.0f);
-
- return keyFrames;
- }
- */
-
- internal void OnRecordBufferChanged(byte[] recordBuffer, int sampleRate)
- {
- EnqueueVowels(recordBuffer);
- }
-
- internal void OnRecodingTick()
- {
- var lipAnimation = CreateLipAnimationByVowelsQueue();
- if (lipAnimation != null)
- {
- ResetLipAnimation(lipAnimation);
- PlayLipAnimation();
- }
- else
- {
- Log.Error(LogTag, "Current Lip Animation is null");
- }
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2024 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.ComponentModel;
-using Tizen.NUI;
-using Tizen.NUI.Scene3D;
-
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
- internal class MotionPlayer
- {
- private Animation motionAnimation;
- internal Animation MotionAnimation { get => motionAnimation; private set => motionAnimation = value; }
-
- internal MotionPlayer()
- {
-
- }
-
- /// <summary>
- /// Play avatar animation by AnimationInfo
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal void PlayAnimation(Animation motionAnimation, int duration = 3000, bool isLooping = false, int loopCount = 1)
- {
- ResetAnimations();
- if (motionAnimation != null)
- {
- MotionAnimation = motionAnimation;
- MotionAnimation?.Play();
- }
- else
- {
- Tizen.Log.Error(LogTag, "motionAnimation is null");
- }
- }
-
- /// <summary>
- /// Pause avatar animation
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal void PauseMotionAnimation()
- {
- MotionAnimation?.Pause();
- }
-
- /// <summary>
- /// Stop avatar animation
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal void StopMotionAnimation()
- {
- MotionAnimation?.Stop();
- }
-
- private void ResetAnimations()
- {
- if (MotionAnimation != null)
- {
- MotionAnimation.Stop();
- MotionAnimation.Dispose();
- MotionAnimation = null;
- }
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2024 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- public interface IAnimationModuleData
- {
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public enum MotionDataType
- {
- [EditorBrowsable(EditorBrowsableState.Never)]
- AnimationInfo,
- [EditorBrowsable(EditorBrowsableState.Never)]
- MotionData
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2024 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Text;
-using Tizen.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class LipSyncData : IAnimationModuleData
- {
- [EditorBrowsable(EditorBrowsableState.Never)]
- public byte[] AudioFile { get; set; }
- [EditorBrowsable(EditorBrowsableState.Never)]
- public int SampleRate { get; set; }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2024 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.ComponentModel;
-using Tizen.NUI.Scene3D;
-
-namespace Tizen.AIAvatar
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class MotionBehaviorData : IAnimationModuleData
- {
- [EditorBrowsable(EditorBrowsableState.Never)]
- public MotionDataType Type { get; set; } = MotionDataType.AnimationInfo;
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AnimationInfo AnimationInfo { get; set; }
- [EditorBrowsable(EditorBrowsableState.Never)]
- public MotionData MotionData { get; set; }
- [EditorBrowsable(EditorBrowsableState.Never)]
- public int Duration { get; set; } = 3000;
- [EditorBrowsable(EditorBrowsableState.Never)]
- public bool IsLooping { get; set; } = false;
- [EditorBrowsable(EditorBrowsableState.Never)]
- public int LoopCount { get; set; } = 1;
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
- /// <summary>
- ///
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class AvatarMotionChangedEventArgs : EventArgs
- {
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarMotionChangedEventArgs(AvatarMotionState previous, AvatarMotionState current)
- {
- Previous = previous;
- Current = current;
- }
-
- /// <summary>
- /// The previous state.
- /// </summary>
- /// <since_tizen> 3 </since_tizen>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarMotionState Previous
- {
- get;
- internal set;
- }
-
- /// <summary>
- /// The current state.
- /// </summary>
- /// <since_tizen> 3 </since_tizen>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarMotionState Current
- {
- get;
- internal set;
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
- /// <summary>
- /// Enumeration for the states.
- /// </summary>
- /// <since_tizen> 3 </since_tizen>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public enum AvatarMotionState
- {
- /// <summary>
- /// Created state.
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- Failed = -1,
-
- /// <summary>
- /// Ready state.
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- Ready = 0,
-
- /// <summary>
- /// Playing state.
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- Playing = 3,
-
- /// <summary>
- /// Paused state.
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- Paused = 4,
-
- /// <summary>
- /// Stopped state.
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- Stopped = 5,
-
- /// <summary>
- /// Unavailable state.
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- Unavailable
- };
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.ComponentModel;
-using Tizen.Multimedia;
-
-namespace Tizen.AIAvatar
-{
- /// <summary>
- /// Provides the ability to audio
- /// </summary>
- // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class AudioOptions
- {
- private int sampleRate;
- private AudioChannel channel;
- private AudioSampleType sampleType;
-
- /// <summary>
- /// Initializes a new instance of the AudioOptions class with the specified sample rate, channel, and sampleType.
- /// </summary>
- /// <param name="sampleRate">the audio sample rate (8000 ~ 192000Hz)</param>
- /// <param name="channel">the audio channel type.</param>
- /// <param name="sampleType">the audio sample type.</param>
- // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AudioOptions(int sampleRate, AudioChannel channel, AudioSampleType sampleType)
- {
- this.sampleRate = sampleRate;
- this.channel = channel;
- this.sampleType = sampleType;
- }
-
- /// <summary>
- /// The audio sample rate (8000 ~ 192000Hz)
- /// </summary>
- // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
- [EditorBrowsable(EditorBrowsableState.Never)]
- public int SampleRate { get => sampleRate; set => sampleRate = value; }
-
- /// <summary>
- /// The audio channel type
- /// </summary>
- // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AudioChannel Channel { get => channel; set => channel = value; }
-
- /// <summary>
- /// The audio sample type
- /// </summary>
- // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AudioSampleType SampleType { get => sampleType; set => sampleType = value; }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.ComponentModel;
-using Tizen.NUI.Scene3D;
-using Tizen.NUI;
-
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
- /// <summary>
- /// Avatar is a Class to show 3D avatar objects.
- /// It is subclass of Model s.t. we can control Avatar like models animation easly.
- /// For example,
- ///
- /// Avatar supports AR Emoji.
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class Avatar : Model
- {
- private AvatarProperties avatarProperties = new DefaultAvatarProperties();
- private AvatarMotions avatarMotions;
-
- private BlendShapePlayer avatarAnimator;
- private MotionPlayer motionPlayer;
- private JointTransformer jointTransformer;
-
- /// <summary>
- /// Create an initialized AvatarModel.
- /// </summary>
- // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
- [EditorBrowsable(EditorBrowsableState.Never)]
- public Avatar() : base()
- {
- InitAvatar();
- }
-
- /// <summary>
- /// Create an initialized AREmojiDefaultAvatar.
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public Avatar(AvatarInfo avatarInfo) : base(avatarInfo.ResourcePath)
- {
- InitAvatar();
- }
-
- /// <summary>
- /// Create an initialized Avatar.
- /// </summary>
- /// <param name="avatarlUrl">avatar file url.(e.g. glTF, and DLI).</param>
- /// <param name="resourceDirectoryUrl"> The url to derectory containing resources: binary, image etc.</param>
- /// <remarks>
- /// If resourceDirectoryUrl is empty, the parent directory url of avatarUrl is used for resource url.
- ///
- /// http://tizen.org/privilege/mediastorage for local files in media storage.
- /// http://tizen.org/privilege/externalstorage for local files in external storage.
- /// </remarks>
- // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
- [EditorBrowsable(EditorBrowsableState.Never)]
- public Avatar(string avatarUrl, string resourceDirectoryUrl = "") : base(avatarUrl, resourceDirectoryUrl)
- {
- InitAvatar();
- }
-
- /// <summary>
- /// Copy constructor.
- /// </summary>
- /// <param name="avatar">Source object to copy.</param>
- // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
- [EditorBrowsable(EditorBrowsableState.Never)]
- public Avatar(Avatar avatar) : base(avatar)
- {
- InitAvatar();
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarProperties AvatarProperties
- {
- get => avatarProperties;
- set => avatarProperties = value;
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal BlendShapePlayer AvatarAnimator
- {
- get => avatarAnimator;
- set => avatarAnimator = value;
- }
-
-
- /// <summary>
- /// Play avatar animation by AnimationInfo
- /// </summary>
- /// <param name="index">index of default avatar animations</param>
- [EditorBrowsable(EditorBrowsableState.Never)]
- private void PlayAnimation(AnimationInfo animationInfo, int duration = 3000, bool isLooping = false, int loopCount = 1)
- {
- if (animationInfo == null)
- {
- Tizen.Log.Error(LogTag, "animationInfo is null");
- return;
- }
- if (animationInfo.MotionData == null)
- {
- Tizen.Log.Error(LogTag, "animationInfo.MotionData is null");
- return;
- }
- var motionAnimation = GenerateMotionDataAnimation(animationInfo.MotionData);
- if (motionAnimation != null)
- {
- motionAnimation.Duration = duration;
- motionAnimation.Looping = isLooping;
- motionAnimation.LoopCount = loopCount;
- motionAnimation.BlendPoint = 0.2f;
- motionPlayer.PlayAnimation(motionAnimation);
- }
- else
- {
- Tizen.Log.Error(LogTag, "motionAnimation is null");
- }
- }
-
- /// <summary>
- /// Play avatar animation by MotionData
- /// </summary>
- /// <param name="index">index of default avatar animations</param>
- [EditorBrowsable(EditorBrowsableState.Never)]
- private void PlayAnimation(MotionData motionData, int duration = 3000, bool isLooping = false, int loopCount = 1)
- {
-
- if (motionData == null)
- {
- Tizen.Log.Error(LogTag, "motionData is null");
- return;
- }
- var motionAnimation = GenerateMotionDataAnimation(motionData);
- if (motionAnimation != null)
- {
- motionAnimation.Duration = duration;
- motionAnimation.Looping = isLooping;
- motionAnimation.LoopCount = loopCount;
- motionAnimation.BlendPoint = 0.2f;
- motionPlayer.PlayAnimation(motionAnimation);
- }
- else
- {
- Tizen.Log.Error(LogTag, "motionAnimation is null");
- }
- }
-
- private void PlayAnimation(int index, int duration = 3000, bool isLooping = false, int loopCount = 1)
- {
- //TODO by index
- //var motionAnimation = GenerateMotionDataAnimation(animationInfoList[index].MotionData);
- /*motionAnimation.Duration = duration;
- motionAnimation.Looping = isLooping;
- motionAnimation.LoopCount = loopCount;
- motionAnimation.BlendPoint = 0.2f;
-
- motionPlayer.PlayAnimation(motionAnimation);*/
- }
-
- /// <summary>
- /// Pause avatar animation
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- private void PauseMotionAnimation()
- {
- motionPlayer.PauseMotionAnimation();
- }
-
- /// <summary>
- /// Stop avatar animation
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- private void StopMotionAnimation()
- {
- motionPlayer?.StopMotionAnimation();
- }
-
-
- private void InitAvatar()
- {
- avatarMotions = new AvatarMotions(avatarProperties);
-
- AvatarAnimator = new BlendShapePlayer();
-
- avatarProperties.AvatarPropertiesChanged += (s, e) =>
- {
- var eyeAnimation = CreateMotionAnimation(avatarMotions.EyeMotionData);
- if (eyeAnimation != null)
- {
- avatarAnimator.SetBlinkAnimation(eyeAnimation);
- }
- };
-
- ResourcesLoaded += (s, e) =>
- {
- var eyeAnimation = CreateMotionAnimation(avatarMotions.EyeMotionData);
- if (eyeAnimation != null)
- {
- avatarAnimator.SetBlinkAnimation(eyeAnimation);
- }
- };
- }
-
- private Animation CreateMotionAnimation(MotionData data)
- {
- return GenerateMotionDataAnimation(data);
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
- /// <summary>
- /// The type of predefined blendshape. We can customize each type name by "TODO_mapper"
- /// TODO : Explain me
- /// TODO : Need to check each joint exist in AR Emoji
- /// Note : This is temperal name of joints.
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal enum BlendShapeType
- {
- #region Left Eyes
- /// <summary>
- /// EyeBlinkLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeBlinkLeft = 0,
-
- /// <summary>
- /// EyeSquintLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeSquintLeft,
-
- /// <summary>
- /// EyeDownLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeDownLeft,
-
- /// <summary>
- /// EyeInLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeInLeft,
-
- /// <summary>
- /// EyeOpenLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeOpenLeft,
-
- /// <summary>
- /// EyeOutLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeOutLeft,
-
- /// <summary>
- /// EyeUpLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeUpLeft,
- #endregion
-
- #region Right Eyes
- /// <summary>
- /// EyeBlinkRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeBlinkRight,
-
- /// <summary>
- /// EyeSquintRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeSquintRight,
-
- /// <summary>
- /// EyeDownRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeDownRight,
-
- /// <summary>
- /// EyeInRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeInRight,
-
- /// <summary>
- /// EyeOpenRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeOpenRight,
-
- /// <summary>
- /// EyeOutRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeOutRight,
-
- /// <summary>
- /// EyeUpRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeUpRight,
- #endregion
-
- #region Mouth and Jaw
- /// <summary>
- /// JawForward blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- JawForward,
-
- /// <summary>
- /// JawOpen blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- JawOpen,
-
- /// <summary>
- /// JawChew blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- JawChew,
-
- /// <summary>
- /// JawLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- JawLeft,
-
- /// <summary>
- /// JawRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- JawRight,
-
- /// <summary>
- /// MouthLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- MouthLeft,
-
- /// <summary>
- /// MouthFrownLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- MouthFrownLeft,
-
- /// <summary>
- /// MouthSmileLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- MouthSmileLeft,
-
- /// <summary>
- /// MouthDimpleLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- MouthDimpleLeft,
-
- /// <summary>
- /// MouthRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- MouthRight,
-
- /// <summary>
- /// MouthFrownRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- MouthFrownRight,
-
- /// <summary>
- /// MouthSmileRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- MouthSmileRight,
-
- /// <summary>
- /// MouthDimpleRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- MouthDimpleRight,
- #endregion
-
- #region Lips
- /// <summary>
- /// LipsStretchLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- LipsStretchLeft,
-
- /// <summary>
- /// LipsStretchRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- LipsStretchRight,
-
- /// <summary>
- /// LipsUpperClose blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- LipsUpperClose,
-
- /// <summary>
- /// LipsLowerClose blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- LipsLowerClose,
-
- /// <summary>
- /// LipsUpperUp blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- LipsUpperUp,
-
- /// <summary>
- /// LipsLowerDown blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- LipsLowerDown,
-
- /// <summary>
- /// LipsUpperOpen blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- LipsUpperOpen,
-
- /// <summary>
- /// LipsLowerOpen blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- LipsLowerOpen,
-
- /// <summary>
- /// LipsFunnel blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- LipsFunnel,
-
- /// <summary>
- /// LipsPucker blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- LipsPucker,
- #endregion
-
- #region Eyebrows, Cheeks, and Chin
- /// <summary>
- /// BrowDownLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- BrowDownLeft,
-
- /// <summary>
- /// BrowDownRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- BrowDownRight,
-
- /// <summary>
- /// BrowUpCenter blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- BrowUpCenter,
-
- /// <summary>
- /// BrowUpLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- BrowUpLeft,
-
- /// <summary>
- /// BrowUpRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- BrowUpRight,
-
- /// <summary>
- /// CheekSquintLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- CheekSquintLeft,
-
- /// <summary>
- /// CheekSquintRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- CheekSquintRight,
-
- /// <summary>
- /// ChinLowerRaise blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- ChinLowerRaise,
-
- /// <summary>
- /// ChinUpperRaise blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- ChinUpperRaise,
- #endregion
-
- #region Tongue
- /// <summary>
- /// TongueOut blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- TongueOut,
-
- /// <summary>
- /// TongueUp blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- TongueUp,
-
- /// <summary>
- /// TongueDown blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- TongueDown,
-
- /// <summary>
- /// TongueLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- TongueLeft,
-
- /// <summary>
- /// TongueRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- TongueRight,
- #endregion
-
- #region ETC
- /// <summary>
- /// Sneer blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- Sneer,
-
- /// <summary>
- /// Puff blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- Puff,
-
- /// <summary>
- /// PuffLeft blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- PuffLeft,
-
- /// <summary>
- /// PuffRight blendshape
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- PuffRight,
- #endregion
- /// <summary>
- /// Max value of default blendshape. It will be used when we determine the motion index is default or custom.
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- DefaultBlendShapeMax,
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
- /// <summary>
- /// The type of predefined skeletal joint. We can customize each type name by "TODO_mapper"
- /// TODO : Explain me
- /// TODO : Need to check each joint exist in AR Emoji
- /// Note : This is temperal name of joints.
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal enum JointType
- {
- #region Head
- /// <summary>
- /// Head joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- Head = 0,
-
- /// <summary>
- /// Neck joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- Neck,
-
- /// <summary>
- /// EyeLeft joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeLeft,
-
- /// <summary>
- /// EyeRight joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyeRight,
- #endregion
-
- #region Left Upper Body
- /// <summary>
- /// ShoulderLeft joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- ShoulderLeft,
-
- /// <summary>
- /// ElbowLeft joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- ElbowLeft,
-
- /// <summary>
- /// WristLeft joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- WristLeft,
- #endregion
-
- #region Right Upper Body
- /// <summary>
- /// ShoulderRight joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- ShoulderRight,
-
- /// <summary>
- /// ElbowRight joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- ElbowRight,
-
- /// <summary>
- /// WristRight joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- WristRight,
- #endregion
-
- #region Left Lower Body
- /// <summary>
- /// HipLeft joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- HipLeft,
-
- /// <summary>
- /// KneeLeft joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- KneeLeft,
-
- /// <summary>
- /// AnkleLeft joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- AnkleLeft,
-
- /// <summary>
- /// ForeFootLeft joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- ForeFootLeft,
- #endregion
-
- #region Right Lower Body
- /// <summary>
- /// HipRight joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- HipRight,
-
- /// <summary>
- /// KneeRight joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- KneeRight,
-
- /// <summary>
- /// AnkleRight joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- AnkleRight,
-
- /// <summary>
- /// ForeFootRight joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- ForeFootRight,
- #endregion
-
- #region Left Hand Finger
- /// <summary>
- /// FingerThumb1Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerThumb1Left,
-
- /// <summary>
- /// FingerThumb2Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerThumb2Left,
-
- /// <summary>
- /// FingerThumb3Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerThumb3Left,
-
- /// <summary>
- /// FingerThumb4Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerThumb4Left,
-
- /// <summary>
- /// FingerIndex1Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerIndex1Left,
-
- /// <summary>
- /// FingerIndex2Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerIndex2Left,
-
- /// <summary>
- /// FingerIndex3Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerIndex3Left,
-
- /// <summary>
- /// FingerIndex4Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerIndex4Left,
-
- /// <summary>
- /// FingerMiddle1Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerMiddle1Left,
-
- /// <summary>
- /// FingerMiddle2Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerMiddle2Left,
-
- /// <summary>
- /// FingerMiddle3Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerMiddle3Left,
-
- /// <summary>
- /// FingerMiddle4Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerMiddle4Left,
-
- /// <summary>
- /// FingerRing1Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerRing1Left,
-
- /// <summary>
- /// FingerRing2Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerRing2Left,
-
- /// <summary>
- /// FingerRing3Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerRing3Left,
-
- /// <summary>
- /// FingerRing4Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerRing4Left,
-
- /// <summary>
- /// FingerPinky1Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerPinky1Left,
-
- /// <summary>
- /// FingerPinky2Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerPinky2Left,
-
- /// <summary>
- /// FingerPinky3Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerPinky3Left,
-
- /// <summary>
- /// FingerPinky4Left joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerPinky4Left,
- #endregion
-
- #region Right Hand Finger
- /// <summary>
- /// FingerThumb1Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerThumb1Right,
-
- /// <summary>
- /// FingerThumb2Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerThumb2Right,
-
- /// <summary>
- /// FingerThumb3Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerThumb3Right,
-
- /// <summary>
- /// FingerThumb4Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerThumb4Right,
-
- /// <summary>
- /// FingerIndex1Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerIndex1Right,
-
- /// <summary>
- /// FingerIndex2Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerIndex2Right,
-
- /// <summary>
- /// FingerIndex3Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerIndex3Right,
-
- /// <summary>
- /// FingerIndex4Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerIndex4Right,
-
- /// <summary>
- /// FingerMiddle1Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerMiddle1Right,
-
- /// <summary>
- /// FingerMiddle2Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerMiddle2Right,
-
- /// <summary>
- /// FingerMiddle3Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerMiddle3Right,
-
- /// <summary>
- /// FingerMiddle4Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerMiddle4Right,
-
- /// <summary>
- /// FingerRing1Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerRing1Right,
-
- /// <summary>
- /// FingerRing2Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerRing2Right,
-
- /// <summary>
- /// FingerRing3Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerRing3Right,
-
- /// <summary>
- /// FingerRing4Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerRing4Right,
-
- /// <summary>
- /// FingerPinky1Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerPinky1Right,
-
- /// <summary>
- /// FingerPinky2Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerPinky2Right,
-
- /// <summary>
- /// FingerPinky3Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerPinky3Right,
-
- /// <summary>
- /// FingerPinky4Right joint
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- FingerPinky4Right,
- #endregion
-
- /// <summary>
- /// Max value of default joint. It will be used when we determine the motion index is default or custom.
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- DefaultJointMax,
- }
-
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal enum NodeType
- {
- [EditorBrowsable(EditorBrowsableState.Never)]
- HeadGeo,
- [EditorBrowsable(EditorBrowsableState.Never)]
- MouthGeo,
- [EditorBrowsable(EditorBrowsableState.Never)]
- EyelashGeo
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2024 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using Newtonsoft.Json;
-using System.Net.Http;
-using System;
-using System.Threading.Tasks;
-using Tizen.Uix.Tts;
-using System.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal class AvatarLLM
- {
- private AvatarTTS avatarTTS;
- private IRestClient restClient;
- private const string playgroundURL = "https://playground-api.sec.samsung.net";
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal AvatarLLM()
- {
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal void InitAvatarLLM(AvatarTTS avatarTTS)
- {
- this.avatarTTS = avatarTTS;
- // Setup RestClinet
- var restClientFactory = new RestClientFactory();
- restClient = restClientFactory.CreateClient(playgroundURL);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal async Task StartTTSWithLLMAsync(string text, string token)
- {
- var bearerToken = token;
- var jsonData = "{\"messages\": [{\"role\": \"user\", \"content\": \"" + text + "\"}]}";
-
- try
- {
- var postResponse = await restClient.SendRequestAsync(HttpMethod.Post, "/api/v1/chat/completions", bearerToken, jsonData);
- var responseData = JsonConvert.DeserializeObject<dynamic>(postResponse);
- string content = responseData["response"]["content"];
- Log.Info("Tizen.AIAvatar", content);
-
- //TTS 호출
- var voiceInfo = new VoiceInfo()
- {
- Lang = "en_US",
- Type = Voice.Female,
- };
-
- avatarTTS.PlayTTSAsync(content, voiceInfo, (o, e) => {
-
- });
-
- }
- catch (Exception ex)
- {
- Log.Error("Tizen.AIAvatar", "에러 발생: " + ex.Message);
- }
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.ComponentModel;
-
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal class AvatarMic
- {
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal AvatarMic()
- {
- }
-
- /// <summary>
- /// Initialize Microphone for using live lip sync
- /// </summary>
- /// <privilege>http://tizen.org/privilege/recorder</privilege>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal bool InitAvatarMic(Avatar avatar)
- {
- if (AudioRecorder.Instance == null)
- {
- Log.Error(LogTag, $"Failed to initialize AudioRecorder");
- return false;
- }
- AudioRecorder.Instance?.InitMic(avatar?.AvatarAnimator);
-
- return true;
- }
-
- /// <summary>
- /// Deinitialize Microphone
- /// </summary>
- /// <privilege>http://tizen.org/privilege/recorder</privilege>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal bool DeinitAvatarMIC()
- {
- AudioRecorder.Instance?.DeinitMic();
-
- return true;
- }
-
- /// <summary>
- /// Start synchronization avatar lip based on the microphone's voice
- /// </summary>
- /// <privilege>http://tizen.org/privilege/recorder</privilege>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal void StartMic()
- {
- AudioRecorder.Instance?.StartRecording();
- }
-
- /// <summary>
- /// Stop microphone
- /// </summary>
- /// <privilege>http://tizen.org/privilege/recorder</privilege>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal void StopMic()
- {
- AudioRecorder.Instance?.StopRecording();
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.Collections.Generic;
-using System.ComponentModel;
-using System;
-using Tizen.Uix.Tts;
-
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal class AvatarTTS
- {
- private TTSLipSyncer ttsLipSyncer;
-
- private event EventHandler ttsReadyFinished;
- private readonly Object ttsReadyFinishedLock = new Object();
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal AvatarTTS()
- {
- }
-
- /// <summary>
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal event EventHandler TTSReadyFinished
- {
- add
- {
- lock (ttsReadyFinishedLock)
- {
- ttsReadyFinished += value;
- }
-
- }
-
- remove
- {
- lock (ttsReadyFinishedLock)
- {
- if (ttsReadyFinished == null)
- {
- Log.Error(LogTag, "ttsReadyFinished is null");
- return;
- }
- ttsReadyFinished -= value;
- }
- }
- }
-
- /// <summary>
- /// <param name=""></param>
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal TtsClient CurrentTTSClient => ttsLipSyncer?.TtsHandle;
-
- /// <summary>
- /// <param name=""></param>
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal void InitTTS(Avatar avatar)
- {
- if (ttsLipSyncer == null)
- {
- try
- {
- ttsLipSyncer = new TTSLipSyncer(avatar?.AvatarAnimator?.LipSyncer);
- }
- catch (Exception e)
- {
- Log.Error(LogTag, $"error :{e.Message}");
- Log.Error(LogTag, $"{e.StackTrace}");
- }
- }
- }
-
- /// <summary>
- /// <param name=""></param>
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal void DeinitTTS()
- {
- if (ttsLipSyncer != null)
- {
- try
- {
- ttsLipSyncer.DeinitTts();
- ttsLipSyncer = null;
- }
- catch (Exception e)
- {
- Log.Error(LogTag, $"error :{e.Message}");
- Log.Error(LogTag, $"{e.StackTrace}");
- }
- }
- }
-
- /// <summary>
- /// <param name=""></param>
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal List<VoiceInfo> GetSupportedVoices()
- {
- return ttsLipSyncer.GetSupportedVoices();
- }
-
- /// <summary>
- /// <param name=""></param>
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal bool PrepareTTS(string text, VoiceInfo voiceInfo, EventHandler ttsReadyFinishedCallback = null)
- {
- if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
- {
- Log.Error(LogTag, "tts is null");
- return false;
- }
-
-
- if (!ttsLipSyncer.IsSupportedVoice(voiceInfo))
- {
- Log.Info(LogTag, $"{voiceInfo.Lang} & {voiceInfo.Type} is not supported");
- return false;
- }
-
- Log.Info(LogTag, "Current TTS State :" + ttsLipSyncer.TtsHandle.CurrentState);
- if (ttsLipSyncer.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
- {
- Log.Info(LogTag, "TTS is not ready");
- return false;
- }
-
- try
- {
- ttsLipSyncer.AddText(text, voiceInfo);
- ttsLipSyncer.Prepare(ttsReadyFinishedCallback);
- }
- catch (Exception e)
- {
- Log.Error(LogTag, $"error :{e.Message}");
- Log.Error(LogTag, $"{e.StackTrace}");
- return false;
- }
- return true;
- }
-
- /// <summary>
- /// <param name=""></param>
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal bool PrepareTTS(string text, string lang = "ko_KR", EventHandler ttsReadyFinishedCallback = null)
- {
- if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
- {
- Log.Error(LogTag, "tts is null");
- return false;
- }
-
- if (!ttsLipSyncer.IsSupportedVoice(lang))
- {
- Log.Error(LogTag, $"{lang} is not supported");
- return false;
- }
-
- Log.Info(LogTag, "Current TTS State :" + ttsLipSyncer.TtsHandle.CurrentState);
- if (ttsLipSyncer.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
- {
- Log.Error(LogTag, "TTS is not ready");
- return false;
- }
- try
- {
- ttsLipSyncer.AddText(text, lang);
- ttsLipSyncer.Prepare(ttsReadyFinishedCallback);
- }
- catch (Exception e)
- {
- Log.Error(LogTag, $"error :{e.Message}");
- Log.Error(LogTag, $"{e.StackTrace}");
- return false;
- }
- return true;
- }
-
- /// <summary>
- /// <param name=""></param>
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal bool PlayPreparedTTS()
- {
- if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
- {
- Log.Error(LogTag, "tts is null");
- return false;
- }
-
- return ttsLipSyncer.PlayPreparedText();
- }
-
- /// <summary>
- /// <param name=""></param>
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal bool PlayTTS(string text, VoiceInfo voiceInfo)
- {
- if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
- {
- Log.Error(LogTag, "tts is null");
- return false;
- }
-
-
- if (!ttsLipSyncer.IsSupportedVoice(voiceInfo))
- {
- Log.Info(LogTag, $"{voiceInfo.Lang} & {voiceInfo.Type} is not supported");
- return false;
- }
-
- Log.Info(LogTag, "Current TTS State :" + ttsLipSyncer.TtsHandle.CurrentState);
- if (ttsLipSyncer.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
- {
- Log.Info(LogTag, "TTS is not ready");
- return false;
- }
-
- try
- {
- ttsLipSyncer.AddText(text, voiceInfo);
- ttsLipSyncer.Play();
- }
- catch (Exception e)
- {
- Log.Error(LogTag, $"error :{e.Message}");
- Log.Error(LogTag, $"{e.StackTrace}");
- return false;
- }
- return true;
- }
-
- /// <summary>
- /// <param name=""></param>
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal bool PlayTTS(string text, string lang = "ko_KR")
- {
- if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
- {
- Log.Error(LogTag, "tts is null");
- return false;
- }
-
- if (!ttsLipSyncer.IsSupportedVoice(lang))
- {
- Log.Error(LogTag, $"{lang} is not supported");
- return false;
- }
-
- Log.Info(LogTag, "Current TTS State :" + ttsLipSyncer.TtsHandle.CurrentState);
- if (ttsLipSyncer.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
- {
- Log.Error(LogTag, "TTS is not ready");
- return false;
- }
- try
- {
- ttsLipSyncer.AddText(text, lang);
- ttsLipSyncer.Play();
- }
- catch (Exception e)
- {
- Log.Error(LogTag, $"error :{e.Message}");
- Log.Error(LogTag, $"{e.StackTrace}");
- return false;
- }
- return true;
- }
-
- /// <summary>
- /// <param name=""></param>
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal bool PlayTTSAsync(string text, VoiceInfo voiceInfo, EventHandler ttsReadyFinishedCallback = null)
- {
- if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
- {
- Log.Error(LogTag, "tts is null");
- return false;
- }
-
-
- if (!ttsLipSyncer.IsSupportedVoice(voiceInfo))
- {
- Log.Info(LogTag, $"{voiceInfo.Lang} & {voiceInfo.Type} is not supported");
- return false;
- }
-
- Log.Info(LogTag, "Current TTS State :" + ttsLipSyncer.TtsHandle.CurrentState);
- if (ttsLipSyncer.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
- {
- Log.Info(LogTag, "TTS is not ready");
- return false;
- }
-
- try
- {
- ttsLipSyncer.AddText(text, voiceInfo);
- ttsLipSyncer.PlayAsync(ttsReadyFinishedCallback);
- }
- catch (Exception e)
- {
- Log.Error(LogTag, $"error :{e.Message}");
- Log.Error(LogTag, $"{e.StackTrace}");
- return false;
- }
- return true;
- }
-
- /// <summary>
- /// <param name=""></param>
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal bool PlayTTS(string text, string lang = "ko_KR", EventHandler ttsReadyFinishedCallback = null)
- {
- if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
- {
- Log.Error(LogTag, "tts is null");
- return false;
- }
-
- if (!ttsLipSyncer.IsSupportedVoice(lang))
- {
- Log.Error(LogTag, $"{lang} is not supported");
- return false;
- }
-
- Log.Info(LogTag, "Current TTS State :" + ttsLipSyncer.TtsHandle.CurrentState);
- if (ttsLipSyncer.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
- {
- Log.Error(LogTag, "TTS is not ready");
- return false;
- }
- try
- {
- ttsLipSyncer.AddText(text, lang);
- ttsLipSyncer.PlayAsync(ttsReadyFinishedCallback);
- }
- catch (Exception e)
- {
- Log.Error(LogTag, $"error :{e.Message}");
- Log.Error(LogTag, $"{e.StackTrace}");
- return false;
- }
- return true;
- }
-
- /// <summary>
- /// <param name=""></param>
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal void PauseTTS()
- {
- if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
- {
- Log.Error(LogTag, "tts is null");
- return;
- }
-
- try
- {
- Log.Info(LogTag, "Current TTS State :" + ttsLipSyncer.TtsHandle.CurrentState);
- ttsLipSyncer?.Pause();
- }
- catch (Exception e)
- {
- Log.Error(LogTag, $"error :{e.Message}");
- Log.Error(LogTag, $"{e.StackTrace}");
- }
- }
-
- /// <summary>
- /// <param name=""></param>
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal void StopTTS()
- {
- if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
- {
- Log.Error(LogTag, "tts is null");
- return;
- }
-
- try
- {
- Log.Info(LogTag, "Current TTS State :" + ttsLipSyncer.TtsHandle.CurrentState);
- ttsLipSyncer?.Stop();
- }
- catch (Exception e)
- {
- Log.Error(LogTag, $"error :{e.Message}");
- Log.Error(LogTag, $"{e.StackTrace}");
- }
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-using System.ComponentModel;
-using Tizen.NUI.Scene3D;
-
-namespace Tizen.AIAvatar
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class AnimationInfo
- {
- [EditorBrowsable(EditorBrowsableState.Never)]
- public MotionData MotionData { get; internal set; }
- [EditorBrowsable(EditorBrowsableState.Never)]
- public string MotionName { get; internal set; }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AnimationInfo(MotionData motionData, string motionName)
- {
- MotionData = motionData;
- MotionName = motionName;
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.ComponentModel;
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class AvatarInfo
- {
- [EditorBrowsable(EditorBrowsableState.Never)]
- public string Name { get; private set; }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public string ThumbnailPath { get; private set; }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal string ResourcePath { get; private set; }
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal AvatarInfoOption avatarInfoOption { get; private set; }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarInfo(string path, string Name, AvatarInfoOption info = AvatarInfoOption.Thumbnail)
- {
- this.Name = Name;
- this.avatarInfoOption = info;
-
- if (info == AvatarInfoOption.Thumbnail)
- {
- ThumbnailPath = path;
- }
- else
- {
- ResourcePath = path;
- }
- }
-
- internal AvatarInfo(string directoryPath)
- {
- string path = ApplicationResourcePath + EmojiAvatarResourcePath;
- Name = directoryPath.Substring(path.Length, directoryPath.Length - path.Length);
- ResourcePath = $"{directoryPath}/{AIAvatar.ExternalModel}";
- }
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public enum AvatarInfoOption
- {
- [EditorBrowsable(EditorBrowsableState.Never)]
- Thumbnail = 0,
- [EditorBrowsable(EditorBrowsableState.Never)]
- Resource = 1,
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-using System.ComponentModel;
-using Tizen.Uix.Tts;
-
-namespace Tizen.AIAvatar
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- public struct VoiceInfo
- {
- private string lang;
- private Voice type;
-
- /// <summary>
- ///
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public string Lang { get => lang; set => lang = value; }
-
- /// <summary>
- ///
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public Voice Type { get => type; set => type = value; }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.ComponentModel;
-using Tizen.NUI;
-using Tizen.NUI.Scene3D;
-
-namespace Tizen.AIAvatar
-{
- /// <summary>
- /// Specialized <see cref="BlendShapeIndex"/> to control avatar blend shape.
- /// </summary>
- /// <example>
- /// <code>
- /// AvatarBlendShapeIndex leftEyeBlink = new AvatarBlendShapeIndex(avatar.BlendShapeMapper, BlendShapeType.EyeBlinkLeft);
- ///
- /// // We can change the property later.
- /// AVatarBlendShapeIndex rightEyeBlink = new AvatarJointTransformIndex(avatar.BlendShapeMapper);
- /// rightEyeBlink.AvatarBlendShapeType = (uint)BlendShapeType.EyeBlinkRight;
- /// </code>
- /// </example>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class AvatarBlendShapeIndex : BlendShapeIndex
- {
- internal AvatarPropertyMapper nameMapper = null;
- internal uint blendShapeType;
-
- /// <summary>
- /// Create an initialized avatar blend shape index.
- /// </summary>
- /// <param name="mapper">Name mapper for this index</param>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarBlendShapeIndex(AvatarPropertyMapper mapper) : base()
- {
- nameMapper = mapper;
- }
-
- /// <summary>
- /// Create an initialized avatar blend shape index with input blend shape ID.
- /// </summary>
- /// <param name="mapper">Name mapper for this index</param>
- /// <param name="blendShapeId">Blend shape ID for this motion index</param>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarBlendShapeIndex(AvatarPropertyMapper mapper, PropertyKey blendShapeId) : base(new PropertyKey(GetPropertyNameFromMapper(mapper, blendShapeId)))
- {
- nameMapper = mapper;
- }
-
- /// <summary>
- /// Create an initialized avatar blend shape index with blend shape type.
- /// </summary>
- /// <param name="mapper">Name mapper for this index</param>
- /// <param name="blendShapeType">Type of blend shape for this motion index</param>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal AvatarBlendShapeIndex(AvatarPropertyMapper blendShapeMapper, BlendShapeType blendShapeType) : this(blendShapeMapper, (uint)blendShapeType)
- {
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="mapper">Name mapper for this index</param>
- /// <param name="blendShapeType">Type of blend shape for this motion index</param>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarBlendShapeIndex(AvatarPropertyMapper mapper, uint blendShapeType) : base(new PropertyKey(GetPropertyNameFromMapper(mapper, blendShapeType)))
- {
- nameMapper = mapper;
- this.blendShapeType = blendShapeType;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="mapper">Name mapper for this index</param>
- /// <param name="blendShapeType">Type of blend shape for this motion index</param>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal AvatarBlendShapeIndex(AvatarPropertyMapper nodeMapper, NodeType nodeType, AvatarPropertyMapper blendShapeMapper, BlendShapeType blendShapeType) : base(new PropertyKey(GetPropertyNameFromMapper(nodeMapper, (uint)nodeType)), new PropertyKey(GetPropertyNameFromMapper(blendShapeMapper, (uint)blendShapeType)))
- {
- nameMapper = blendShapeMapper;
- this.blendShapeType = (uint)blendShapeType;
- }
-
- /// <summary>
- /// Get the name of given index.
- /// </summary>
- /// <param name="mapper">Name mapper for given index</param>
- /// <param name="id">Target ID what we want to get name</param>
- /// <returns>Name, or null if invalid</returns>
- private static string GetPropertyNameFromMapper(AvatarPropertyMapper mapper, PropertyKey id)
- {
- if (id == null)
- {
- return "";
- }
- if (id.Type == PropertyKey.KeyType.String)
- {
- return id.StringKey;
- }
- return mapper?.GetPropertyName((uint)id.IndexKey) ?? "";
- }
-
- /// <summary>
- /// Get the name of given JointType.
- /// </summary>
- /// <param name="mapper">Name mapper for given index</param>
- /// <param name="blendShapeType">Type of joint what we want to get name</param>
- /// <returns>Name, or null if invalid</returns>
- private static string GetPropertyNameFromMapper(AvatarPropertyMapper mapper, uint blendShapeType)
- {
- return mapper?.GetPropertyName(blendShapeType) ?? "";
- }
-
- /// <summary>
- /// TODO : Explain me
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarPropertyMapper NameMapper
- {
- get
- {
- return nameMapper;
- }
- set
- {
- nameMapper = value;
-
- using PropertyKey blendShapeId = new(GetPropertyNameFromMapper(nameMapper, blendShapeType));
- base.BlendShapeId = blendShapeId;
- }
- }
-
- /// <summary>
- /// TODO : Explain me
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public uint AvatarBlendShapeType
- {
- get
- {
- return blendShapeType;
- }
- set
- {
- blendShapeType = value;
-
- using PropertyKey blendShapeId = new(GetPropertyNameFromMapper(nameMapper, blendShapeType));
- base.BlendShapeId = blendShapeId;
- }
- }
-
- /// <summary>
- /// Hijack property to control Avatar specified logic.
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public new PropertyKey BlendShapeId
- {
- get
- {
- return base.BlendShapeId;
- }
- set
- {
- if (value != null)
- {
- if (value.Type == PropertyKey.KeyType.Index)
- {
- blendShapeType = (uint)value.IndexKey;
- }
- using PropertyKey blendShapeId = new(GetPropertyNameFromMapper(nameMapper, value));
- base.BlendShapeId = blendShapeId;
- }
- else
- {
- base.BlendShapeId = value;
- }
- }
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System.ComponentModel;
-using Tizen.NUI;
-using Tizen.NUI.Scene3D;
-
-namespace Tizen.AIAvatar
-{
- /// <summary>
- /// Specialized <see cref="MotionTransformIndex"/> to control avatar joint transform.
- /// </summary>
- /// <example>
- /// <code>
- /// AvatarJointTransformIndex position = new AvatarJointTransformIndex(avatar.JointMapper, JointType.Head, MotionTransformIndex.TransformTypes.Position);
- ///
- /// // We can change the property later.
- /// AvatarJointTransformIndex orientation = new AvatarJointTransformIndex(avatar.JointMapper);
- /// orientation.AvatarJointType = (uint)JointType.Neck;
- /// orientation.TransformType = MotionTransformIndex.TransformTypes.Orientation;
- /// </code>
- /// </example>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class AvatarJointTransformIndex : MotionTransformIndex
- {
- internal AvatarPropertyMapper nameMapper = null;
- internal uint jointType;
-
- /// <summary>
- /// Create an initialized avatar joint transform index.
- /// </summary>
- /// <param name="mapper">Name mapper for this index</param>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarJointTransformIndex(AvatarPropertyMapper mapper) : base()
- {
- nameMapper = mapper;
- }
-
- /// <summary>
- /// Create an initialized avatar joint transform index with input node id, and transform type.
- /// </summary>
- /// <param name="mapper">Name mapper for this index</param>
- /// <param name="modelNodeId">Node ID for this motion index</param>
- /// <param name="transformType">Transform property type for this motion index</param>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarJointTransformIndex(AvatarPropertyMapper mapper, PropertyKey modelNodeId, TransformTypes transformType) : base(new PropertyKey(GetPropertyNameFromMapper(mapper, modelNodeId)), transformType)
- {
- nameMapper = mapper;
- }
-
- /// <summary>
- /// Create an initialized avatar joint transform index with input node id, and transform type.
- /// </summary>
- /// <param name="mapper">Name mapper for this index</param>
- /// <param name="jointType">Type of joint for this motion index</param>
- /// <param name="transformType">Transform property type for this motion index</param>
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal AvatarJointTransformIndex(AvatarPropertyMapper mapper, JointType jointType, TransformTypes transformType) : this(mapper, (uint)jointType, transformType)
- {
- }
-
- /// <summary>
- /// Create an initialized avatar joint transform index with input node id, and transform type.
- /// </summary>
- /// <param name="mapper">Name mapper for this index</param>
- /// <param name="jointType">Type of joint for this motion index</param>
- /// <param name="transformType">Transform property type for this motion index</param>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarJointTransformIndex(AvatarPropertyMapper mapper, uint jointType, TransformTypes transformType) : base(new PropertyKey(GetPropertyNameFromMapper(mapper, jointType)), transformType)
- {
- nameMapper = mapper;
- this.jointType = jointType;
- }
-
- private static string GetPropertyNameFromMapper(AvatarPropertyMapper mapper, PropertyKey id)
- {
- if (id == null)
- {
- return "";
- }
- if (id.Type == PropertyKey.KeyType.String)
- {
- return id.StringKey;
- }
- return mapper?.GetPropertyName((uint)id.IndexKey) ?? "";
- }
-
- private static string GetPropertyNameFromMapper(AvatarPropertyMapper mapper, uint jointType)
- {
- return mapper?.GetPropertyName(jointType) ?? "";
- }
-
- /// <summary>
- /// TODO : Explain me
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarPropertyMapper NameMapper
- {
- get
- {
- return nameMapper;
- }
- set
- {
- nameMapper = value;
-
- using PropertyKey nodeId = new(GetPropertyNameFromMapper(nameMapper, jointType));
- base.ModelNodeId = nodeId;
- }
- }
-
- /// <summary>
- /// TODO : Explain me
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public uint AvatarJointType
- {
- get
- {
- return jointType;
- }
- set
- {
- jointType = value;
-
- using PropertyKey nodeId = new(GetPropertyNameFromMapper(nameMapper, jointType));
- base.ModelNodeId = nodeId;
- }
- }
-
- /// <summary>
- /// Hijack property to control Avatar specified logic.
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public new PropertyKey ModelNodeId
- {
- get
- {
- return base.ModelNodeId;
- }
- set
- {
- if (value != null)
- {
- if (value.Type == PropertyKey.KeyType.Index)
- {
- jointType = (uint)value.IndexKey;
- }
- using PropertyKey nodeId = new(GetPropertyNameFromMapper(nameMapper, value));
- base.ModelNodeId = nodeId;
- }
- else
- {
- base.ModelNodeId = value;
- }
- }
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class AvatarProperties
- {
- private AvatarPropertyMapper jointMapper;
- private AvatarPropertyMapper blendShapeMapper;
- private AvatarPropertyMapper nodeMapper;
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarProperties(AvatarPropertyMapper jointMapper, AvatarPropertyMapper blendShapeMapper, AvatarPropertyMapper nodeMapper)
- {
- JointMapper = new AvatarPropertyMapper(jointMapper);
- BlendShapeMapper = new AvatarPropertyMapper(blendShapeMapper);
- NodeMapper = new AvatarPropertyMapper(nodeMapper);
- }
-
- internal event EventHandler<AvatarProperties> AvatarPropertiesChanged;
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarPropertyMapper JointMapper
- {
- get
- {
- return jointMapper;
- }
- set
- {
- jointMapper = value;
- AvatarPropertiesChanged?.Invoke(jointMapper, this);
- }
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarPropertyMapper BlendShapeMapper
- {
- get
- {
- return blendShapeMapper;
- }
- set
- {
- blendShapeMapper = value;
- AvatarPropertiesChanged?.Invoke(blendShapeMapper, this);
- }
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarPropertyMapper NodeMapper
- {
- get
- {
- return nodeMapper;
- }
- set
- {
- nodeMapper = value;
- AvatarPropertiesChanged?.Invoke(nodeMapper, this);
- }
- }
- }
-}
+++ /dev/null
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-using System.Collections.Generic;
-using System.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
- /// <summary>
- /// TODO : Explain more detail
- /// </summary>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class AvatarPropertyMapper
- {
- /// <summary>
- /// Mapper between index and property name
- /// </summary>
- private Dictionary<uint, string> mapper = null;
-
- /// <summary>
- /// The counter of index. It will be increased one when we register custom index.
- /// </summary>
- private uint customIndexCounter = 0;
-
- /// <summary>
- /// Create an initialized AvatarPropertyNameMapper.
- /// </summary>
- // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarPropertyMapper()
- {
- mapper = new Dictionary<uint, string>();
- customIndexCounter = 0u;
- }
-
- /// <summary>
- /// Copy constructor.
- /// </summary>
- // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
- [EditorBrowsable(EditorBrowsableState.Never)]
- public AvatarPropertyMapper(AvatarPropertyMapper rhs)
- {
- if (rhs != null)
- {
- mapper = new Dictionary<uint, string>(rhs.Mapper);
- customIndexCounter = rhs.customIndexCounter;
- }
- else
- {
- mapper = new Dictionary<uint, string>();
- customIndexCounter = 0u;
- }
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- protected uint CustomIndexCounter
- {
- get
- {
- return customIndexCounter;
- }
- set
- {
- customIndexCounter = value;
- }
- }
-
- /// <summary>
- /// Get current mapper information.
- /// </summary>
- // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
- [EditorBrowsable(EditorBrowsableState.Never)]
- public Dictionary<uint, string> Mapper
- {
- get
- {
- return mapper;
- }
- }
-
- /// <summary>
- /// TODO : Explain me.
- /// </summary>
- /// <param name="index">The index of property what we want to set</param>
- /// <returns>The index of property, or uint.MaxValue if not exist</returns>
- // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
- [EditorBrowsable(EditorBrowsableState.Never)]
- public string this[uint index]
- {
- set
- {
- SetPropertyName(index, value);
- }
- get
- {
- return GetPropertyName(index);
- }
- }
-
- /// <summary>
- /// TODO : Explain me.
- /// </summary>
- /// <param name="name">The name of custom property</param>
- /// <returns>The index of property matched with name.</returns>
- // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
- [EditorBrowsable(EditorBrowsableState.Never)]
- public uint RegisterCustomProperty(string name)
- {
- uint ret = GetPropertyIndexByName(name);
- if (ret >= customIndexCounter)
- {
- ret = customIndexCounter++;
- SetPropertyName(ret, name);
- }
- return ret;
- }
-
- /// <summary>
- /// TODO : Explain me.
- /// </summary>
- /// <param name="name">The name of property what we want to get index</param>
- /// <returns>The index of property, or uint.MaxValue if not exist</returns>
- // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
- [EditorBrowsable(EditorBrowsableState.Never)]
- public uint GetPropertyIndexByName(string name)
- {
- // TODO : Do this without iteration
- foreach (var pair in mapper)
- {
- if (pair.Value == name)
- {
- return pair.Key;
- }
- }
- return uint.MaxValue;
- }
-
- /// <summary>
- /// TODO : Explain me.
- /// </summary>
- /// <param name="index">The index of property what we want to set</param>
- /// <param name="name">The name of property what we want to set</param>
- /// <remark>
- /// New property will be added if we use index that not exist in mapper.
- /// </remark>
- internal void SetPropertyName(uint index, string name)
- {
- mapper.TryAdd(index, name);
- }
-
- /// <summary>
- /// TODO : Explain me.
- /// </summary>
- /// <param name="index">The index of property what we want to set</param>
- /// <returns>The name of property, or null if not exist</returns>
- internal string GetPropertyName(uint index)
- {
- string ret = null;
- mapper.TryGetValue(index, out ret);
- return ret;
- }
- }
-}
--- /dev/null
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+namespace Tizen.AIAvatar
+{
+ internal class Test
+ {
+ public void CreateTest()
+ {
+ var avatar1 = new Avatar();
+
+ var avatarInfo = new AvatarInfo("avatarName", "resourcePath");
+ var avatar2 = new Avatar(avatarInfo);
+
+ var avatar3 = new Avatar("resourcePath");
+
+ var avatar4 = new Avatar(avatar1);
+
+ avatar1.Dispose();
+ avatar2.Dispose();
+ avatar3.Dispose();
+ avatar4.Dispose();
+ }
+ }
+}
--- /dev/null
+<Project Sdk="Tizen.NET.Sdk/1.1.9">
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net6.0</TargetFramework>
+ <AssemblyName>AIAvatarSample</AssemblyName>
+ </PropertyGroup>
+
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugType>portable</DebugType>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>None</DebugType>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\..\src\Tizen.AIAvatar\Tizen.AIAvatar.csproj" />
+ <ProjectReference Include="../../src/Tizen.NUI.Components/Tizen.NUI.Components.csproj" />
+ </ItemGroup>
+</Project>
--- /dev/null
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.7.34009.444
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.AIAvatar.Sample", "Tizen.AIAvatar.Sample.csproj", "{0176A159-FCC2-45B8-ABEE-63FEBFEA5079}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.AIAvatar", "..\..\src\Tizen.AIAvatar\Tizen.AIAvatar.csproj", "{D1971AA8-5B1D-467A-9E86-292B01C4DDB8}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {0176A159-FCC2-45B8-ABEE-63FEBFEA5079}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0176A159-FCC2-45B8-ABEE-63FEBFEA5079}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0176A159-FCC2-45B8-ABEE-63FEBFEA5079}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0176A159-FCC2-45B8-ABEE-63FEBFEA5079}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D1971AA8-5B1D-467A-9E86-292B01C4DDB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D1971AA8-5B1D-467A-9E86-292B01C4DDB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D1971AA8-5B1D-467A-9E86-292B01C4DDB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D1971AA8-5B1D-467A-9E86-292B01C4DDB8}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
--- /dev/null
+/*
+ * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using global::System;
+using System.IO;
+using System.Collections.Generic;
+using Tizen.NUI;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI.Scene3D;
+using Tizen.AIAvatar;
+using Tizen;
+using Tizen.Uix.Tts;
+
+namespace AIAvatar
+{
+ public class AvatarScene : SceneView
+ {
+ private static readonly string ApplicationResourcePath = "/usr/apps/org.tizen.default-avatar-resource/shared/res/";
+ private static readonly string EmojiAvatarResourcePath = "/models/EmojiAvatar/";
+ private static readonly string DefaultMotionResourcePath = "/animation/motion/";
+ private static string resourcePath = Utils.ResourcePath;
+
+ private const int cameraAnimationDurationMilliSeconds = 2000;
+ private const int sceneTransitionDurationMilliSeconds = 1500;
+
+ private Avatar defaultAIAvatar;
+ private List<AvatarInfo> avatarList;
+ private List<AnimationInfo> animationInfoList;
+ private LipSyncController lipSyncController;
+
+ private bool isBlink = false;
+ private bool isShowing = true;
+
+ private int avatarIndex = 0;
+ private float iblFactor = 0.3f;
+
+ public float IBLFactor
+ {
+ get
+ {
+ return iblFactor;
+ }
+ set
+ {
+ iblFactor = value;
+ ImageBasedLightScaleFactor = value;
+ }
+ }
+
+ public AvatarScene()
+ {
+ PivotPoint = Tizen.NUI.PivotPoint.TopLeft;
+ ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft;
+ PositionUsesPivotPoint = true;
+
+ // Setup Image Based Light
+ SetupSceneViewIBL();
+
+ // Setup camera preset
+ SetupDefaultCamera();
+
+ // Setup Default Avatar Position & Orientation
+ SetupDefaultAvatar();
+
+ // Load Default Animations
+ LoadDefaultAnimations();
+ }
+
+ private void LoadDefaultAnimations()
+ {
+ var motionAnimations = Directory.GetFiles(ApplicationResourcePath + DefaultMotionResourcePath, "*.bvh");
+ animationInfoList = new List<AnimationInfo>();
+
+ foreach (var path in motionAnimations)
+ {
+ var motionData = new MotionData();
+ motionData.LoadMotionCaptureAnimation(path, true, new Vector3(0.01f, 0.01f, 0.01f), false);
+
+ var animationInfo = new AnimationInfo(motionData, GetFileNameWithoutExtension(path));
+ animationInfoList.Add(animationInfo);
+ }
+ }
+
+ public void SetupSceneViewIBLFactor(float value)
+ {
+ IBLFactor = value;
+ }
+
+ public void StartAvatarTalk_1()
+ {
+ PlayLip("cs-CZ-Wavenet-A.wav");
+ }
+
+ public void StartAvatarTalk_2()
+ {
+ PlayLip("da-DK-Wavenet-A.wav");
+ }
+
+ public void StartAvatarTalk_3()
+ {
+ PlayLip("el-GR-Wavenet-A.wav");
+ }
+
+ public void StartAvatarTalkByPath()
+ {
+ PlayLip($"{resourcePath}/voice/voice_0.bin");
+ }
+
+ private void PlayLip(string source)
+ {
+ var path = $"{resourcePath}/voice/{source}";
+ var bytes = ReadAllBytes(path);
+
+ Tizen.Log.Error("AIAvatar", "audio path : " + path);
+ if (lipSyncController == null)
+ {
+ lipSyncController = new LipSyncController(defaultAIAvatar);
+ }
+
+ if (bytes != null)
+ {
+ lipSyncController.PlayLipSync(bytes);
+ }
+ else
+ {
+ Tizen.Log.Error("AIAvatar", "Fail to load bytes");
+ }
+ }
+
+ public void StartRandomAnimation()
+ {
+ var randomIdx = new Random().Next(0, animationInfoList.Count);
+ defaultAIAvatar.PlayAnimation(animationInfoList[randomIdx].MotionData);
+ }
+
+ public void StartMic()
+ {
+ InitMic();
+ lipSyncController?.StartMic();
+ }
+
+ public void StopMic()
+ {
+ InitMic();
+ lipSyncController?.StopMic();
+ }
+
+ public void EyeBlink()
+ {
+ if (!isBlink)
+ {
+ defaultAIAvatar.StartEyeBlink();
+ }
+ else
+ {
+ defaultAIAvatar.StopEyeBlink();
+ }
+ isBlink = !isBlink;
+ }
+
+ public void ShowHide()
+ {
+ if (!isShowing)
+ {
+ defaultAIAvatar.Show();
+ }
+ else
+ {
+ defaultAIAvatar.Hide();
+ }
+ isShowing = !isShowing;
+ }
+
+ public void InintTTsTest()
+ {
+ InitTTS();
+ }
+
+ public void StartTTSTest()
+ {
+ lipSyncController.StopTTS();
+ VoiceInfo voiceInfo = new VoiceInfo()
+ {
+ Language = "en_US",
+ Type = VoiceType.Female,
+ };
+
+ lipSyncController.PrepareTTS(Utils.TTSText, voiceInfo, (o, e) =>
+ {
+ lipSyncController.PlayPreparedTTS();
+ });
+ }
+
+ public void StopTTSTest()
+ {
+ if (lipSyncController == null)
+ {
+ Tizen.Log.Error("AIAvatar", "lipSyncController is null");
+ return;
+ }
+ lipSyncController.StopTTS();
+ }
+
+ public void SwitchCamera()
+ {
+ CameraTransition(1, cameraAnimationDurationMilliSeconds);
+ }
+
+ public void SetupSceneViewCameraFov(float value)
+ {
+ var camera = GetSelectedCamera();
+ camera.FieldOfView = new Radian(value);
+ }
+
+ public void ChangeAvatar()
+ {
+ DestroyAvatar();
+ if (avatarIndex + 1 <= avatarList.Count - 1)
+ {
+ avatarIndex++;
+ }
+ else
+ {
+ avatarIndex = 0;
+ }
+ CreateAvatar();
+
+ }
+
+ internal static string GetFileNameWithoutExtension(string path)
+ {
+ return System.IO.Path.GetFileNameWithoutExtension(path);
+ }
+
+ private void InitTTS()
+ {
+ if (lipSyncController == null)
+ {
+ lipSyncController = new LipSyncController(defaultAIAvatar);
+ }
+ lipSyncController.InitializeTTS();
+ }
+
+ private void InitMic()
+ {
+ if (lipSyncController == null)
+ {
+ lipSyncController = new LipSyncController(defaultAIAvatar);
+ }
+ lipSyncController.InitializeMic();
+ }
+
+ private byte[] ReadAllBytes(string path)
+ {
+ try
+ {
+ var bytes = File.ReadAllBytes(path);
+ return bytes;
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+
+ private void SetupDefaultAvatar()
+ {
+ avatarList = AvatarExtension.GetDefaultAvatarList();
+ foreach (var info in avatarList)
+ {
+ Tizen.Log.Info("AvatarSample", $"Avatar Name :{info.Name}\n");
+ Tizen.Log.Info("AvatarSample", $"Avatar Thumbnail :{info.ThumbnailPath}\n");
+ }
+
+ CreateAvatar();
+ }
+
+ private void CreateAvatar()
+ {
+ defaultAIAvatar = new Avatar(avatarList[avatarIndex])
+ {
+ Position = new Position(0.0f, -1.70f, -2.0f),
+ Size = new Size(1.0f, 1.0f, 1.0f),
+ Orientation = new Rotation(new Radian(new Degree(0.0f)), Vector3.YAxis)
+ };
+ //var animator = defaultAIAvatar.CurrentAnimator;
+ //animator.MotionStateChanged += OnMotionStateChanged;
+ //animator.LipStateChanged += OnLipStateChanged;
+ Add(defaultAIAvatar);
+ }
+
+ private void DestroyAvatar()
+ {
+ if (defaultAIAvatar != null)
+ {
+ Remove(defaultAIAvatar);
+ /*
+ var animator = defaultAIAvatar.CurrentAnimator;
+ animator.MotionStateChanged -= OnMotionStateChanged;
+ animator.LipStateChanged -= OnLipStateChanged;
+*/
+ defaultAIAvatar.Dispose();
+ defaultAIAvatar = null;
+ }
+
+ }
+
+ private void OnMotionStateChanged(object sender, AvatarMotionChangedEventArgs e)
+ {
+ var avatar = sender as Avatar;//Avatar changed state
+
+ switch (e.Current)
+ {
+ case AvatarMotionState.Ready:
+ Log.Error(Utils.LogTag, "Current Avatar State is Ready");
+ break;
+
+ case AvatarMotionState.Playing:
+ Log.Error(Utils.LogTag, "Current Avatar State is Playing");
+ break;
+
+ case AvatarMotionState.Paused:
+ Log.Error(Utils.LogTag, "Current Avatar State is Paused");
+ break;
+
+ case AvatarMotionState.Stopped:
+ Log.Error(Utils.LogTag, "Current Avatar State is Stopped");
+ break;
+ }
+ }
+
+ private void OnLipStateChanged(object sender, AvatarMotionChangedEventArgs e)
+ {
+ var avatar = sender as Avatar;//Avatar changed state
+
+ switch (e.Current)
+ {
+ case AvatarMotionState.Ready:
+ Log.Error(Utils.LogTag, "Current Avatar Lip is Ready");
+ break;
+
+ case AvatarMotionState.Playing:
+ Log.Error(Utils.LogTag, "Current Avatar Lip is Playing");
+ break;
+
+ case AvatarMotionState.Paused:
+ Log.Error(Utils.LogTag, "Current Avatar Lip is Paused");
+ break;
+
+ case AvatarMotionState.Stopped:
+ Log.Error(Utils.LogTag, "Current Avatar Lip is Stopped");
+ break;
+ }
+ }
+
+ private void SetupSceneViewIBL()
+ {
+ SetImageBasedLightSource(resourcePath + "images/" + "Irradiance.ktx", resourcePath + "images/" + "Radiance.ktx", IBLFactor);
+ }
+
+ private void SetupDefaultCamera()
+ {
+ // Default camera setting
+ // Note : SceneView always have 1 default camera.
+ var defaultCamera = GetCamera(0u);
+
+ defaultCamera.PositionX = 0.0f;
+ defaultCamera.PositionY = -2.3f;
+ defaultCamera.PositionZ = 0.0f;
+ defaultCamera.FieldOfView = new Radian(new Degree(45.0f));
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using global::System;
+using Tizen.NUI;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI.Scene3D;
+using Tizen.NUI.Components;
+
+namespace AIAvatar
+{
+ public class UIControlPanel
+ {
+ private readonly Vector2 SceneViewPadding = new Vector2(0.0f, 0.0f);
+ private ButtonStyle buttonStyle;
+ private SliderStyle sliderStyle;
+
+ private Window uiWindow;
+ private AvatarScene mainScene;
+ private View controlPannel;
+
+ private TextField editor;
+
+ private readonly float ControlPannelWidthScale = 0.25f;// Relative size of window width. windowSize.Width * 0.5f
+
+ public event EventHandler OnExitButtonClicked;
+
+ public void MakeControlPannel(Window uiWindow, AvatarScene mainScene)
+ {
+ this.uiWindow = uiWindow;
+ this.mainScene = mainScene;
+
+ if (controlPannel != null)
+ {
+ return;
+ }
+
+ InitializeButtonStyle();
+ InitializeSliderStyle();
+
+ controlPannel = new ScrollableBase()
+ {
+ BackgroundColor = new Color(0.85f, 0.85f, 0.85f, 0.25f),
+
+ PivotPoint = PivotPoint.TopLeft,
+ ParentOrigin = ParentOrigin.TopLeft,
+ PositionUsesPivotPoint = true,
+ //CornerRadius = 30.0f,
+ Padding = new Extents(30, 30, 30, 30),
+
+ Layout = new LinearLayout()
+ {
+ LinearOrientation = LinearLayout.Orientation.Vertical,
+ HorizontalAlignment = HorizontalAlignment.Begin,
+ VerticalAlignment = VerticalAlignment.Top,
+ CellPadding = new Size2D(10, 20),
+ },
+ HideScrollbar = false,
+ };
+ uiWindow.Add(controlPannel);
+
+ // Make control buttons
+ MakeControlPannelComponents();
+ }
+
+ public void ReizeUIPanel(View avatarScene, View circleView)
+ {
+ var windowSize = uiWindow.Size;
+ var layoutLTR = (windowSize.Width >= windowSize.Height);
+
+ var sceneViewSize = new Vector2(0, 0);
+ var sceneViewPosition = new Vector2(0, 0);
+ var controlPannelSize = new Vector2(0, 0);
+ var controlPannelPosition = new Vector2(0, 0);
+ if (layoutLTR) // layout Left to Right
+ {
+ sceneViewSize = new Vector2(windowSize.Width - SceneViewPadding.X * 3.0f - ControlPannelWidthScale * windowSize.Width,
+ windowSize.Height - SceneViewPadding.Y * 2.0f);
+ sceneViewPosition = new Vector2(SceneViewPadding.X * 2.0f + ControlPannelWidthScale * windowSize.Width, SceneViewPadding.Y);
+ controlPannelSize = new Vector2(ControlPannelWidthScale * windowSize.Width, windowSize.Height - SceneViewPadding.Y * 2.0f);
+ controlPannelPosition = new Vector2(SceneViewPadding.X, SceneViewPadding.Y);
+ }
+ else // layout Top to Bottom
+ {
+ sceneViewSize = new Vector2(windowSize.Width - SceneViewPadding.X * 2.0f,
+ windowSize.Height - SceneViewPadding.Y * 3.0f - ControlPannelWidthScale * windowSize.Height);
+ sceneViewPosition = new Vector2(SceneViewPadding.X, SceneViewPadding.Y * 2.0f + ControlPannelWidthScale * windowSize.Height);
+ controlPannelSize = new Vector2(windowSize.Width - SceneViewPadding.X * 2.0f, ControlPannelWidthScale * windowSize.Height);
+ controlPannelPosition = new Vector2(SceneViewPadding.X, SceneViewPadding.Y);
+ }
+
+ // Update SceneView and ControlPannel size/position if we has.
+ if (avatarScene != null && circleView != null)
+ {
+ avatarScene.SizeWidth = sceneViewSize.Width;
+ avatarScene.SizeHeight = sceneViewSize.Height;
+ avatarScene.PositionX = 0;//sceneViewPosition.X;
+ avatarScene.PositionY = 0;//sceneViewPosition.Y;
+
+ circleView.SizeWidth = sceneViewSize.Width;
+ circleView.SizeHeight = sceneViewSize.Width;
+ circleView.PositionX = sceneViewPosition.X;
+ circleView.PositionY = sceneViewPosition.Y;
+ }
+
+ if (controlPannel != null)
+ {
+ controlPannel.SizeWidth = controlPannelSize.Width;
+ controlPannel.SizeHeight = controlPannelSize.Height;
+ controlPannel.PositionX = controlPannelPosition.X;
+ controlPannel.PositionY = controlPannelPosition.Y;
+ }
+ }
+
+ private void InitializeButtonStyle()
+ {
+ buttonStyle = new ButtonStyle()
+ {
+ Size = new Size(252, 48),
+ ItemSpacing = new Size2D(8, 8),
+ CornerRadius = 12.0f,
+ ItemHorizontalAlignment = HorizontalAlignment.Center,
+ ItemVerticalAlignment = VerticalAlignment.Center,
+ BorderlineWidth = 5.0f,
+ BackgroundColor = new Selector<Color>()
+ {
+ Normal = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR1,
+ Pressed = Styles.BIG_TAG_NORMAL_BACKGROUND_COLOR1,
+ Focused = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR2,
+ Selected = Styles.BIG_TAG_NORMAL_BACKGROUND_COLOR1,
+ Disabled = new Color(0.792f, 0.792f, 0.792f, 1),
+ },
+ BorderlineColorSelector = new Selector<Color>()
+ {
+ Normal = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR2,
+ Pressed = Styles.BIG_TAG_NORMAL_BACKGROUND_COLOR2,
+ Focused = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR1,
+ Selected = Styles.BIG_TAG_NORMAL_BACKGROUND_COLOR2,
+ Disabled = new Color(0.94f, 0.95f, 0.93f, 1.0f),
+ },
+ Text = new TextLabelStyle()
+ {
+ TextColor = new Color("#0D0D0D"),
+ PixelSize = 24,
+ },
+ };
+ }
+
+ private void InitializeSliderStyle()
+ {
+ sliderStyle = new SliderStyle()
+ {
+ Size = new Size(850, 50),
+ TrackThickness = 8,
+ Track = new ImageViewStyle()
+ {
+ Size = new Size(800, 8),
+ CornerRadius = 4.0f,
+ BackgroundColor = new Selector<Color>()
+ {
+ Normal = new Color(1.0f, 0.37f, 0.0f, 0.1f),
+ Disabled = new Color(1.0f, 0.37f, 0.0f, 0.1f),
+ },
+ },
+ Progress = new ImageViewStyle()
+ {
+ Size = new Size(800, 8),
+ CornerRadius = 4.0f,
+ BackgroundColor = new Selector<Color>()
+ {
+ //Normal = new Color("#FF6200"),
+ Normal = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR2,
+ Disabled = new Color("#CACACA"),
+ },
+ },
+ Thumb = new ImageViewStyle()
+ {
+ Size = new Size(24, 24),
+ BackgroundColor = new Selector<Color>()
+ {
+ Normal = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR1,
+ Pressed = Styles.BIG_TAG_NORMAL_BACKGROUND_COLOR1,
+ Focused = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR2,
+ Disabled = new Color(0.94f, 0.95f, 0.93f, 1.0f),
+ },
+ BorderlineWidth = 5.0f,
+ BorderlineColorSelector = new Selector<Color>()
+ {
+ Normal = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR2,
+ Pressed = Styles.BIG_TAG_NORMAL_BACKGROUND_COLOR2,
+ Focused = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR1,
+ Disabled = new Color(0.94f, 0.95f, 0.93f, 1.0f),
+ },
+ CornerRadius = new Vector4(0.5f, 0.5f, 0.5f, 0.5f),
+ CornerRadiusPolicy = VisualTransformPolicyType.Relative,
+ },
+ ValueIndicatorImage = new ImageViewStyle()
+ {
+ Size = new Size(43, 40),
+ BorderlineWidth = 1.0f,
+ BorderlineColor = new Color("#FF6200"),
+ BackgroundColor = new Color(1.0f, 1.0f, 1.0f, 0.0f),
+ CornerRadius = 12.0f,
+ },
+ ValueIndicatorText = new TextLabelStyle()
+ {
+ SizeHeight = 24,
+ PixelSize = 16,
+ TextColor = new Color("#FF6200"),
+ }
+ };
+ }
+
+ private void MakeControlPannelComponents()
+ {
+ AddControlEditor();
+ AddControlButton("ChangeAvatar", "button_avatar", mainScene.ChangeAvatar);
+ AddControlButton("Random Animation", "button_bvh", mainScene.StartRandomAnimation);
+ AddControlButton("EyeBlink", "button_bvh", mainScene.EyeBlink);
+
+ AddControlButton("TTS Init", "button_avatar", mainScene.InintTTsTest);
+ AddControlButton("TTS Start", "button_avatar", mainScene.StartTTSTest);
+ AddControlButton("TTS Stop", "button_avatar", mainScene.StopTTSTest);
+ AddControlButton("CZ", "button_avatar", mainScene.StartAvatarTalk_1);
+ AddControlButton("DK", "button_avatar", mainScene.StartAvatarTalk_2);
+ AddControlButton("GR", "button_avatar", mainScene.StartAvatarTalk_3);
+ AddControlButton("Start Mic", "button_bvh", mainScene.StartMic);
+ AddControlButton("Stop Mic", "button_bvh", mainScene.StopMic);
+ AddControlButton("Show/Hide", "button_bvh", mainScene.ShowHide);
+ AddControlSlider("Camera FOV", "slider_camera_fov", 0.1f, 1.3f, mainScene.GetSelectedCamera().FieldOfView.ConvertToFloat(), mainScene.SetupSceneViewCameraFov);
+ AddControlSlider("IBL intensity", "slider_ibl_factor", 0.1f, 0.8f, mainScene.IBLFactor, mainScene.SetupSceneViewIBLFactor);
+ AddControlButton("Quit", "button_quit", Exit);
+ }
+
+ private void AddControlEditor()
+ {
+ editor = new TextField()
+ {
+ Text = Utils.TTSText,
+ PlaceholderText = "Input Your Message",
+ Name = "InputText",
+ BackgroundColor = Color.White,
+ WidthSpecification = LayoutParamPolicies.MatchParent,
+ };
+ editor.TextChanged += (obj, e) => {
+ Utils.TTSText = e.TextField.Text;
+ };
+
+ controlPannel.Add(editor);
+ }
+
+ private void AddControlButton(string buttonText, string buttonName, Action func)
+ {
+ var button = new Button(buttonStyle)
+ {
+ Text = buttonText,
+ Name = buttonName,
+ WidthSpecification = LayoutParamPolicies.MatchParent,
+ };
+ button.Clicked += (o, e) =>
+ {
+ Button me = o as Button;
+ if (me == null) return;
+
+ func();
+ };
+
+ controlPannel.Add(button);
+ }
+
+ private Slider AddControlSlider(string sliderText, string sliderName, float minValue, float maxValue, float currentValue, Action<float> func)
+ {
+ var dummy = new View()
+ {
+ WidthSpecification = LayoutParamPolicies.MatchParent,
+ Layout = new LinearLayout()
+ {
+ LinearOrientation = LinearLayout.Orientation.Horizontal,
+ HorizontalAlignment = HorizontalAlignment.Begin,
+ VerticalAlignment = VerticalAlignment.Center,
+ CellPadding = new Size2D(5, 0),
+ },
+ };
+ var label = new TextLabel()
+ {
+ WidthSpecification = LayoutParamPolicies.MatchParent,
+ Text = sliderText,
+ FontSizeScale = 0.5f,
+ Focusable = false,
+ Weight = 0.2f,
+ MultiLine = true,
+ };
+ dummy.Add(label);
+ var slider = new Slider(sliderStyle)
+ {
+ WidthSpecification = LayoutParamPolicies.MatchParent,
+ MinValue = minValue,
+ MaxValue = maxValue,
+ CurrentValue = currentValue,
+ Name = sliderName,
+ Weight = 0.8f,
+ };
+ slider.ValueChanged += (o, e) =>
+ {
+ func(e.CurrentValue);
+ };
+ dummy.Add(slider);
+ controlPannel.Add(dummy);
+
+ return slider;
+ }
+
+ private void Exit()
+ {
+ OnExitButtonClicked?.Invoke(null, null);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using Tizen.AIAvatar;
+using Tizen.NUI;
+using Tizen.NUI.BaseComponents;
+using Tizen.Security;
+
+using Tizen;
+using Tizen.Multimedia;
+
+namespace AIAvatar
+{
+ partial class Program : NUIApplication
+ {
+ private Window window;
+ private Tizen.NUI.Size windowSize;
+ private AvatarScene avatarScene;
+ private View circleMaskView;
+
+ private UIControlPanel uiControlPanel;
+
+ protected override void OnCreate()
+ {
+ base.OnCreate();
+ Initialize();
+ }
+
+ private void Initialize()
+ {
+ //var asyncAudioCapture = new AsyncAudioCapture(24000, AudioChannel.Mono, AudioSampleType.S16Le);
+
+ window = NUIApplication.GetDefaultWindow();
+ window.BackgroundColor = new Color(0.85f, 0.85f, 0.85f, 1.0f); ;
+
+ window.AddAvailableOrientation(Window.WindowOrientation.Landscape);
+ window.AddAvailableOrientation(Window.WindowOrientation.Portrait);
+ window.AddAvailableOrientation(Window.WindowOrientation.LandscapeInverse);
+ window.AddAvailableOrientation(Window.WindowOrientation.PortraitInverse);
+
+ window.Resized += OnResizedEvent;
+ window.KeyEvent += OnKeyEvent;
+
+ MakeBackground();
+ MakeAvatarScene();
+ MakeUIPanel();
+
+ RecalculatePositionSizeFromWindowSize();
+ }
+
+ private void OnResizedEvent(object sender, Window.ResizedEventArgs e)
+ {
+ RecalculatePositionSizeFromWindowSize();
+ }
+
+ private void MakeBackground()
+ {
+ var backgroundView = new View()
+ {
+ WidthResizePolicy = ResizePolicyType.FillToParent,
+ HeightResizePolicy = ResizePolicyType.FillToParent,
+ BackgroundImage = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "images/UI_BG.png",
+ };
+ backgroundView.TouchEvent += (o, e) =>
+ {
+ if (e.Touch.GetState(0) == PointStateType.Down)
+ {
+ Tizen.Log.Error("NUI", $"Background touched.\n");
+ }
+ return true;
+ };
+ window.Add(backgroundView);
+ }
+
+ private void MakeAvatarScene()
+ {
+ avatarScene = new AvatarScene();
+ avatarScene.UseFramebuffer = true;
+
+ avatarScene.SetAlphaMaskUrl(Tizen.Applications.Application.Current.DirectoryInfo.Resource + "images/contact-cards-mask.png");
+ avatarScene.EnableCropToMask(false);
+ //window.Add(avatarScene);
+
+ circleMaskView = new View()
+ {
+ Size = new Tizen.NUI.Size(),
+ Position = new Position(0, 0),
+ BackgroundColor = new Tizen.NUI.Color(0.0f, 0.0f, 0.0f, 0.0f),//new Tizen.NUI.Color("#221F1F"),
+ };
+ window.Add(circleMaskView);
+ circleMaskView.Add(avatarScene);
+
+ //circleMaskView.ClippingMode = ClippingModeType.ClipChildren;
+ //circleMaskView.CornerRadius = new Vector4(0.5f,0.5f,0.5f,0.5f);
+ circleMaskView.CornerRadiusPolicy = VisualTransformPolicyType.Relative;
+ }
+
+ private void MakeUIPanel()
+ {
+ uiControlPanel = new UIControlPanel();
+ uiControlPanel.OnExitButtonClicked += OnExitButtonClicked;
+ uiControlPanel.MakeControlPannel(window, avatarScene);
+ }
+
+ private void OnExitButtonClicked(object sender, EventArgs e)
+ {
+ ExitApplication();
+ }
+
+ private void RecalculatePositionSizeFromWindowSize()
+ {
+ uiControlPanel.ReizeUIPanel(avatarScene, circleMaskView);
+ }
+
+ private void OnKeyEvent(object sender, Window.KeyEventArgs e)
+ {
+
+ if (e.Key.State != Key.StateType.Down)
+ {
+ return;
+ }
+
+ if (e.Key.KeyPressedName == "Escape" || e.Key.KeyPressedName == "XF86Back" || e.Key.KeyPressedName == "BackSpace")
+ {
+ ExitApplication();
+ }
+ }
+
+ private void ExitApplication()
+ {
+ Utils.FullGC();
+ Exit();
+ }
+
+ static void Main(string[] args)
+ {
+ var app = new Program();
+ app.Run(args);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using Tizen.NUI;
+
+namespace AIAvatar
+{
+ public static class Styles
+ {
+ public static readonly Color BIG_TAG_NORMAL_BACKGROUND_COLOR1 = new Color(0xFF / 255.0f, 0xFE / 255.0f, 0xFE / 255.0f, 0.8f);
+ public static readonly Color BIG_TAG_NORMAL_BACKGROUND_COLOR2 = new Color(0xDB / 255.0f, 0xE3 / 255.0f, 0xFF / 255.0f, 0.8f);
+ public static readonly Color BIG_TAG_FOCUS_BACKGROUND_COLOR1 = new Color(0xD9 / 255.0f, 0xE1 / 255.0f, 0xFF / 255.0f, 0.8f);
+ public static readonly Color BIG_TAG_FOCUS_BACKGROUND_COLOR2 = new Color(0xB2 / 255.0f, 0xC4 / 255.0f, 0xFF / 255.0f, 0.8f);
+ public static readonly Color BIG_TAG_NORMAL_SHADOW_COLOR = new Color(62.0f / 255.0f, 62.0f / 255.0f, 62.0f / 255.0f, 0.8f);
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace AIAvatar
+{
+ public static class Utils
+ {
+ static public string LogTag = "AvatarSample";
+ static public string TTSText = "Select an avatar that will guide you through the functions of your age.";
+ static public string ResourcePath = Tizen.Applications.Application.Current.DirectoryInfo.Resource;
+ static public void FullGC()
+ {
+ global::System.GC.Collect();
+ global::System.GC.WaitForPendingFinalizers();
+ global::System.GC.Collect();
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns="http://tizen.org/ns/packages" api-version="7.0" package="AIAvatarSample" version="1.0.0">
+ <profile name="common" />
+ <ui-application appid="AIAvatar.Sample"
+ exec="AIAvatarSample.dll"
+ type="dotnet"
+ multiple="false"
+ taskmanage="true"
+ nodisplay="false"
+ launch_mode="single"
+ api-version="10">
+ <label>Tizen.AIAvatar</label>
+ <icon>Tizen.AIAvatar.png</icon>
+ <metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
+ </ui-application>
+ <privileges>
+ <privilege>http://tizen.org/privilege/internet</privilege>
+ <privilege>http://tizen.org/privilege/window.priority.set</privilege>
+ <privilege>http://tizen.org/privilege/filesystem.read</privilege>
+ <privilege>http://tizen.org/privilege/recorder</privilege>
+ </privileges>
+</manifest>