Add Tizen.AIAvatar project (#6014)
[platform/core/csapi/tizenfx.git] / src / Tizen.AIAvatar / src / internal / Multimedia / AudioRecorder.cs
1 /*
2  * Copyright(c) 2023 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 using System;
19 using System.Linq;
20 using System.Text;
21 using Tizen.Multimedia;
22 using Tizen.NUI;
23 using static Tizen.AIAvatar.AIAvatar;
24
25 namespace Tizen.AIAvatar
26 {
27     internal class AudioRecorder
28     {
29         private const string privilegeForRecording = "http://tizen.org/privilege/recorder";
30
31         private AsyncAudioCapture asyncAudioCapture;
32
33         private byte[] recordedBuffer;
34         private float desiredBufferDuration = 0.16f;
35         private int desiredBufferLength;
36
37         private Timer audioRecordingTimer;
38
39         private Action audioRecdingAction;
40         private Action<byte[],int > bufferAction;
41
42         private static AudioRecorder instance;
43
44         internal static AudioRecorder Instance
45         {
46             get
47             {
48                 if (instance == null)
49                 {
50                     instance = new AudioRecorder();
51                 }
52                 return instance;
53             }
54         }
55
56         internal event EventHandler<RecordBufferChangedEventArgs> BufferChanged;
57
58         internal AudioRecorder()
59         {
60             Utils.CheckPrivilege(privilegeForRecording);
61             desiredBufferLength = (int)(CurrentAudioOptions.SampleRate * desiredBufferDuration * 2);
62         }
63
64
65         internal void InitMic(BlendShapePlayer animator, uint recordingTime = 160)
66         {
67             audioRecordingTimer = new Timer(recordingTime);
68             if (animator != null)
69             {
70                 //TODO : Connection MIC
71                 var lipSyncer = (animator.GetAnimationModule(AnimationModuleType.LipSyncer) as LipSyncer);
72                 if(lipSyncer != null)
73                 {
74                     Tizen.Log.Error(LogTag, "LipSyncer of animator is null");
75                     return;
76                 }
77                 this.audioRecdingAction = lipSyncer.OnRecodingTick;
78                 this.bufferAction = lipSyncer.OnRecordBufferChanged;
79
80                 BufferChanged += OnRecordBufferChanged;
81                 audioRecordingTimer.Tick += AudioRecordingTimerTick;
82             }
83         }
84
85
86         internal void DeinitMic()
87         {
88             StopRecording();
89             BufferChanged -= OnRecordBufferChanged;
90
91             if (audioRecordingTimer != null)
92             {
93                 audioRecordingTimer.Stop();
94                 audioRecordingTimer.Tick -= AudioRecordingTimerTick;
95
96                 audioRecordingTimer.Dispose();
97                 audioRecordingTimer = null;
98             }
99             audioRecdingAction = null;
100         }
101
102         internal void StartRecording()
103         {
104             audioRecordingTimer?.Start();
105             asyncAudioCapture = new AsyncAudioCapture(CurrentAudioOptions.SampleRate, CurrentAudioOptions.Channel, CurrentAudioOptions.SampleType);
106
107             recordedBuffer = new byte[0];
108             asyncAudioCapture.DataAvailable += (s, e) =>
109             {
110                 recordedBuffer = recordedBuffer.Concat(e.Data).ToArray();
111                 if (recordedBuffer.Length >= desiredBufferLength)
112                 {
113                     var recordedBuffer = this.recordedBuffer;
114                     this.recordedBuffer = Array.Empty<byte>();
115
116                     BufferChanged?.Invoke(this, new RecordBufferChangedEventArgs(recordedBuffer, CurrentAudioOptions.SampleRate));
117                 }
118             };
119             asyncAudioCapture.Prepare();
120             Log.Info(LogTag, "Start Recording - Preapre AsyncAudioCapture");
121         }
122
123         internal void StopRecording()
124         {
125             audioRecordingTimer?.Stop();
126             asyncAudioCapture.Dispose();
127         }
128
129         internal void PauseRecording()
130         {
131             asyncAudioCapture.Pause();
132         }
133
134         internal void ResumeRecording()
135         {
136             asyncAudioCapture.Resume();
137         }
138
139         private void OnRecordBufferChanged(object sender, RecordBufferChangedEventArgs e)
140         {
141             bufferAction?.Invoke(e.RecordedBuffer, CurrentAudioOptions.SampleRate);
142         }
143
144         private bool AudioRecordingTimerTick(object source, Timer.TickEventArgs e)
145         {
146             Log.Info(LogTag, "TickTimer");
147             audioRecdingAction?.Invoke();
148             return true;
149         }
150     }
151 }