Skip to content

Commit 41a0043

Browse files
committed
[[ AndroidAudioRecorder ]] Add android audio recorder library
This patch adds an LCB library which gives access to the Android MediaRecorder API, allowing audio recording on Android. It also includes a sample stack demonstrating the feature.
1 parent dd315c1 commit 41a0043

5 files changed

Lines changed: 344 additions & 0 deletions

File tree

Installer/package.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,8 @@ component Extensions
412412
rfolder macosx:packaged_extensions/com.livecode.widget.iconpicker
413413
into [[ToolsFolder]]/Extensions place
414414
rfolder macosx:packaged_extensions/com.livecode.library.androidbgaudio
415+
into [[ToolsFolder]]/Extensions place
416+
rfolder macosx:packaged_extensions/com.livecode.library.androidaudiorecorder
415417
into [[ToolsFolder]]/Extensions place
416418
rfolder macosx:packaged_extensions/com.livecode.library.iconsvg
417419
into [[ToolsFolder]]/Extensions place

extensions/extensions.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
'modules/scriptitems/scriptitems.lcb',
2323

2424
'libraries/androidbgaudio/androidbgaudio.lcb',
25+
'libraries/androidaudiorecorder/androidaudiorecorder.lcb',
2526
'libraries/canvas/canvas.lcb',
2627
'libraries/iconsvg/iconsvg.lcb',
2728
'libraries/json/json.lcb',
Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
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
5.08 KB
Binary file not shown.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Android Audio Recorder
2+
An android audio recorder has been added. As well as starting and
3+
stopping recording, it allows the selection of various input
4+
sources, encoding types and output formats. The library places
5+
the following handlers in the message path:
6+
- `androidRecorderStartRecording pFileName`: Start recording an audio file, using the given filename
7+
- `androidRecorderStopRecording`: Stop recording
8+
- `androidRecorderGetMaxAmplitude`: Returns the max amplitude of the recording since last sampled
9+
- `androidRecorderSetRecordCompressionType pType`: Set the record compression type
10+
- `androidRecorderSetRecordFormat pFormat`: Set the record output format
11+
- `androidRecorderSetRecordInput pSource`: Set the record input source
12+

0 commit comments

Comments
 (0)