1+ /**
2+ This library provides Android audio recording functionality, via the
3+ android.media.MediaRecorder class.
4+
5+ Description:
6+ Use this library to record audio on Android.
7+
8+ >*Note:* This library requires RECORD_AUDIO, WRITE_EXTERNAL_STORAGE and
9+ > CAPTURE_AUDIO_OUTPUT permissions. These will be automatically added to
10+ > the application manifest when an application including this library is
11+ > built.
12+ */
13+
14+ library com.livecode.library.androidaudiorecorder
15+
16+ metadata version is "1.0.0"
17+ metadata author is "LiveCode"
18+ metadata title is "Android Audio Recorder"
19+
20+ -- We need permissions to record audio and write to the external
21+ -- storage directory.
22+ metadata android.permissions is "RECORD_AUDIO,WRITE_EXTERNAL_STORAGE,CAPTURE_AUDIO_OUTPUT"
23+
24+ use com.livecode.foreign
25+ use com.livecode.java
26+
27+ __safe foreign handler _JNI_MediaRecorderConstructor() returns JObject \
28+ binds to "java:android.media.MediaRecorder>new()"
29+ __safe foreign handler _JNI_MediaRecorderSetRecordInput(in pRecorder as JObject, in pSource as JInt) returns nothing \
30+ binds to "java:android.media.MediaRecorder>setAudioSource(I)V"
31+ __safe foreign handler _JNI_MediaRecorderSetRecordFormat(in pRecorder as JObject, in pFormat as JInt) returns nothing \
32+ binds to "java:android.media.MediaRecorder>setOutputFormat(I)V"
33+ __safe foreign handler _JNI_MediaRecorderSetRecordEncoder(in pRecorder as JObject, in pEncoder as JInt) returns nothing \
34+ binds to "java:android.media.MediaRecorder>setAudioEncoder(I)V"
35+ __safe foreign handler _JNI_MediaRecorderSetRecordOutputFile(in pRecorder as JObject, in pFile as JString) returns nothing \
36+ binds to "java:android.media.MediaRecorder>setOutputFile(Ljava/lang/String;)V"
37+ __safe foreign handler _JNI_MediaRecorderPrepare(in pRecorder as JObject) returns nothing \
38+ binds to "java:android.media.MediaRecorder>prepare()V"
39+ __safe foreign handler _JNI_MediaRecorderStart(in pRecorder as JObject) returns nothing \
40+ binds to "java:android.media.MediaRecorder>start()V"
41+ __safe foreign handler _JNI_MediaRecorderStop(in pRecorder as JObject) returns nothing \
42+ binds to "java:android.media.MediaRecorder>stop()V"
43+ __safe foreign handler _JNI_MediaRecorderReset(in pRecorder as JObject) returns nothing \
44+ binds to "java:android.media.MediaRecorder>reset()V"
45+ __safe foreign handler _JNI_MediaRecorderGetMaxAmplitude(in pRecorder as JObject) returns JInt \
46+ binds to "java:android.media.MediaRecorder>getMaxAmplitude()I"
47+
48+ __safe foreign handler _JNI_GetExternalStorageDir() returns JObject \
49+ binds to "java:android.os.Environment>getExternalStorageDirectory()Ljava/io/File;!static"
50+ __safe foreign handler _JNI_GetFilePath(in pFile as JObject) returns JString \
51+ binds to "java:java.io.File>getAbsolutePath()Ljava/lang/String;"
52+
53+ -- This will store an instance of a MediaRecorder
54+ private variable mRecorder as optional JObject
55+
56+ constant kRecordInputs is { \
57+ "Default" : 0, \
58+ "Mic" : 1, \
59+ "VoiceUplink" : 2, \
60+ "VoiceDownlink" : 3, \
61+ "VoiceCall" : 4, \
62+ "Camcorder" : 5, \
63+ "VoiceRecognition" : 6, \
64+ "VoiceCommunication" : 7, \
65+ "RemoteSubmix" : 8, \
66+ "Unprocessed" : 9 \
67+ }
68+ constant kRecordCompressionTypes is { \
69+ "Default" : 0, \
70+ "AMR NB" : 1, \
71+ "AMR WB" : 2, \
72+ "AAC" : 3, \
73+ "HE AAC" : 4, \
74+ "AAC ELD" : 5, \
75+ "Vorbis" : 6 \
76+ }
77+ constant kRecordFormats is { \
78+ "ThreeGPP" : 1, \
79+ "MPEG-4" : 2, \
80+ "AMR WB" : 4, \
81+ "AAC ADTS" : 6, \
82+ "WebM" : 9 \
83+ }
84+
85+ private variable mRecordInput as optional String
86+ private variable mRecordFormat as optional String
87+ private variable mRecordCompressionType as optional String
88+
89+ private handler IsAndroid() returns Boolean
90+ return the operating system is "android"
91+ end handler
92+
93+ handler GetOptionalProperty(in pVar as optional String, in pDefault as String) returns String
94+ if pVar is not nothing then
95+ return pVar
96+ end if
97+ return pDefault
98+ end handler
99+
100+ handler SetRecorderProperties() returns nothing
101+ if not IsAndroid() then
102+ throw "Not supported on this platform"
103+ end if
104+
105+ if mRecorder is nothing then
106+ return
107+ end if
108+
109+ variable tRecordInput as String
110+ put GetOptionalProperty(mRecordInput, "Mic") into tRecordInput
111+ variable tRecordFormat as String
112+ put GetOptionalProperty(mRecordFormat, "ThreeGPP") into tRecordFormat
113+ variable tRecordCompressionType as String
114+ put GetOptionalProperty(mRecordCompressionType, "AMR NB") into tRecordCompressionType
115+
116+ _JNI_MediaRecorderSetRecordInput(mRecorder, kRecordInputs[tRecordInput])
117+ _JNI_MediaRecorderSetRecordFormat(mRecorder, kRecordFormats[tRecordFormat])
118+ _JNI_MediaRecorderSetRecordEncoder(mRecorder, kRecordCompressionTypes[tRecordCompressionType])
119+ end handler
120+
121+ handler CreateRecorder() returns nothing
122+ if mRecorder is nothing then
123+ put _JNI_MediaRecorderConstructor() into mRecorder
124+ end if
125+
126+ SetRecorderProperties()
127+ end handler
128+
129+ /**
130+ Summary: Start recording an audio file, using the given filename
131+
132+ Example:
133+ androidRecorderStartRecording "recording.mp4"
134+
135+ Parameters:
136+ pFileName: The filename to use when recording audio
137+
138+ Description:
139+ Use the <androidRecorderStartRecording> handler to start recording audio
140+ on Android using the input source, compression type and output format
141+ selected using the <androidRecorderSetRecordInput>,
142+ <androidRecorderSetRecordCompressionType>, and <androidRecorderSetRecordFormat>
143+ handlers. The default input source, compression and output format are
144+ built-in microphone, AMR-NB and 3gpp respectively.
145+
146+ References:
147+ androidRecorderSetRecordInput (handler),
148+ androidRecorderSetRecordCompressionType (handler),
149+ androidRecorderSetRecordFormat (handler)
150+ */
151+ public handler androidRecorderStartRecording(in pFileName as String) returns String
152+ if not IsAndroid() then
153+ throw "Not supported on this platform"
154+ end if
155+ -- Record an mp4 file in the external storage directory with the given
156+ -- name, (i.e. /storage/emulated/0/pFileName.mp4 or equivalent)
157+
158+ CreateRecorder()
159+
160+ if mRecorder is nothing then
161+ throw "unable to create recorder"
162+ end if
163+
164+ variable tFile as JString
165+ put GetAbsolutePathToPublicDocument(pFileName) into tFile
166+ _JNI_MediaRecorderSetRecordOutputFile(mRecorder, tFile)
167+ _JNI_MediaRecorderPrepare(mRecorder)
168+ _JNI_MediaRecorderStart(mRecorder)
169+ return StringFromJString(tFile)
170+ end handler
171+
172+ handler GetAbsolutePathToPublicDocument(in pDoc as String) returns JString
173+ variable tDirectory as JObject
174+ put _JNI_GetExternalStorageDir() into tDirectory
175+
176+ variable tPath as String
177+ put StringFromJString(_JNI_GetFilePath(tDirectory)) & "/" & pDoc into tPath
178+ variable tFile as JObject
179+
180+ return StringToJString(tPath)
181+ end handler
182+
183+ /**
184+ Summary: Stop recording
185+
186+ Example:
187+ on mouseUp
188+ try
189+ androidRecorderStopRecording
190+ catch tError
191+ answer tError
192+ end try
193+ end mouseUp
194+
195+ Description:
196+ Use the <androidRecorderStopRecording> handler to stop the current
197+ recording. If no recording is currently happening, this handler will
198+ throw an error.
199+ */
200+ public handler androidRecorderStopRecording()
201+ -- Don't do anything if there is no recorder object
202+ if mRecorder is nothing then
203+ throw "recording has not been started!"
204+ return
205+ end if
206+
207+ _JNI_MediaRecorderStop(mRecorder)
208+ _JNI_MediaRecorderReset(mRecorder)
209+ end handler
210+
211+ /**
212+ Summary: Returns the max amplitude of the recording since last sampled
213+
214+ Example:
215+ -- Poll amplitude every 30 millisecs, and output values
216+ command LogAmplitude
217+ put androidRecorderGetMaxAmplitude()
218+ send "LogAmplitude" to me in 30 millisecs
219+ end UpdateAmplitudeAndTimer
220+
221+ Description:
222+ Use the <androidRecorderGetMaxAmplitude> handler to retrieve the maximum
223+ amplitude value reached during recording from the selected input source
224+ since the amplitude was last retrieved.
225+
226+ <androidRecorderGetMaxAmplitude> returns 0 if no recording is currently
227+ happening.
228+ */
229+
230+ --
231+ public handler androidRecorderGetMaxAmplitude() returns Integer
232+ if mRecorder is nothing then
233+ return 0
234+ end if
235+ return _JNI_MediaRecorderGetMaxAmplitude(mRecorder)
236+ end handler
237+
238+ /**
239+ Summary: Set the record input source
240+
241+ Example:
242+ androidRecorderSetRecordInput "mic"
243+
244+ Parameters:
245+ pInputSource (enum): The record input source
246+ - "Default": The device's default record input source
247+ - "Mic": Microphone audio source
248+ - "VoiceUplink": Voice call uplink (Tx) audio source
249+ - "VoiceDownlink": Voice call uplink (Tx) audio source.
250+ - "VoiceCall": Voice call uplink + downlink audio source
251+ - "Camcorder": Microphone audio source tuned for video recording, with the same orientation as the camera if available
252+ - "VoiceRecognition": Microphone audio source tuned for voice recognition
253+ - "VoiceCommunication": Microphone audio source tuned for voice recognition
254+ - "RemoteSubmix": Audio source for a submix of audio streams to be presented remotely
255+ - "Unprocessed": Microphone audio source tuned for unprocessed (raw) sound if available, behaves like Default otherwise
256+
257+ Description:
258+ Use the <androidRecorderSetRecordInput> handler to control the source
259+ and tuning of the record input.
260+ */
261+ public handler androidRecorderSetRecordInput(in pInputSource as String)
262+ if pInputSource is not among the keys of kRecordInputs then
263+ throw "unrecognised source"
264+ return
265+ end if
266+
267+ put pInputSource into mRecordInput
268+ SetRecorderProperties()
269+ end handler
270+
271+ /**
272+ Summary: Set the record compression type
273+
274+ Example:
275+ androidRecorderSetRecordCompressionType "AMR NB"
276+
277+ Parameters:
278+ pCompressionType (enum): The record compression type
279+ - "Default": The default compression type
280+ - "AMR NB": AMR Narrowband
281+ - "AMR WB": AMR Wideband
282+ - "AAC": AAC Low Complexity (AAC-LC)
283+ - "HE AAC": High Efficiency AAC (HE-AAC)
284+ - "AAC ELD": Enhanced Low Delay AAC (AAC-ELD)
285+ - "Vorbis" : Ogg Vorbis
286+
287+ Description:
288+ Use the <androidRecorderSetRecordCompressionType> handler to control the
289+ audio encoding used by the audio recorder.
290+ */
291+ public handler androidRecorderSetRecordCompressionType(in pCompressionType as String)
292+ if pCompressionType is not among the keys of kRecordCompressionTypes then
293+ throw "unrecognised compression type"
294+ return
295+ end if
296+
297+ put pCompressionType into mRecordCompressionType
298+ SetRecorderProperties()
299+ end handler
300+
301+ /**
302+ Summary: Set the record output format
303+
304+ Example:
305+ androidRecorderSetRecordFormat "ThreeGPP"
306+
307+ Parameters:
308+ pRecordFormat (enum): The record output format
309+ "ThreeGPP":
310+ "MPEG-4":
311+ "AMR WB":
312+ "AAC ADTS":
313+ "WebM":
314+
315+ Description:
316+ Use the <androidRecorderSetRecordFormat> handler to set the format of
317+ the output file.
318+ */
319+
320+ public handler androidRecorderSetRecordFormat(in pRecordFormat as String)
321+ if pRecordFormat is not among the keys of kRecordFormats then
322+ throw "unrecognised source"
323+ return
324+ end if
325+
326+ put pRecordFormat into mRecordFormat
327+ SetRecorderProperties()
328+ end handler
329+ end library
0 commit comments