Grabar y reproducir - CGastrell/phonegap GitHub Wiki
Grabar y reproducir.
Para terminar la aplicacion vamos a agregar un boton que grabe y detenga la grabacion. Vamos a necesitar crear un directorio audio
donde vamos a ubicar el archivo de grabacion y cuando detengamos la grabacion vamos a cargar ese mismo archivo para poder escucharlo.
HTML
Primero vamos a agregar un boton mas en el HTML
a continuacion de los otros 2 botones que teniamos:
<a href="#" id="rec" class="ui-btn ui-btn-b ui-icon-comment ui-btn-icon-top">Grabar()</a>
Archivo de grabacion
En este ejemplo simple vamos a usar siempre el mismo archivo, o sea que siempre vamos a estar sobreescribiendolo.
Para esto vamos a hacer una function que se encargue de:
- buscar el directorio
cordova.file.externalDataDirectory
- buscar en este directorio un directorio
audio
, si no existe debe crearlo- crear un archivo en el directorio
audio
que se llamerecord.amr
, si no existe debe crearlo
- crear un archivo en el directorio
- buscar en este directorio un directorio
No vale la pena usar fileApi
que alguna vez creamos. Podemos empezar por probar con una funcion simple:
function getRecordFile(){
var root = cordova.file.externalDataDirectory;
window.resolveLocalFileSystemURL(root, function(rootDir){
console.log(rootDir);
});
}
Prueben en la consola asi no hay que recompilar la aplicacion cada vez. La idea es ver como llegar hasta este archivo. Con esta funcion visualizamos el directorio raiz de almacenamiento de nuestra aplicacion.
Ahora expandimos la funcionalidad para buscar el directorio audio
y pasamos la opcion create:true
para que, si no lo encuentra, lo cree:
function getRecordFile(){
var root = cordova.file.externalDataDirectory;
window.resolveLocalFileSystemURL(root, function(rootDir){
console.log(rootDir);
rootDir.getDirectory('audio', {create: true}, function(audioDir){
console.log(audioDir);
});
});
}
Ahora la funcion deberia llegar a imprimir la segunda referencia de directorio: audioDir
. Volvemos a extender la funcionalidad para buscar el archivo record.amr
:
function getRecordFile(){
var root = cordova.file.externalDataDirectory;
window.resolveLocalFileSystemURL(root, function(rootDir){
console.log(rootDir);
rootDir.getDirectory('audio', {create: true}, function(audioDir){
console.log(audioDir);
audioDir.getFile('record.amr', {create: true}, function(file){
console.log(file);
});
});
});
}
Ahora que llegamos hasta aca podemos ver que tenemos un spaghetti code, pero como solo lo necesitamos una vez nos vamos permitir la atrocidad. Pero parte del problema es que, como estamos dentro del resultado de 3 funciones, no podemos simplemente devolver el valor. Cualquier llamada a return
en estas funciones no podra elevar el valor hasta la funcion inicial. Entonces la convertimos en asincronica agregando un parametro en la funcion inicial, que debera ser una funcion, y ejecutaremos esta funcion al llegar al resultado final:
function getRecordFile(callback){ // <-- recibimos una func como parametro
var root = cordova.file.externalDataDirectory;
window.resolveLocalFileSystemURL(root, function(rootDir){
rootDir.getDirectory('audio', {create: true}, function(audioDir){
audioDir.getFile('record.amr', {create: true}, function(file){
callback(file); // <-- ejecutamos la funcion, pasandole
// el resultado
});
});
});
}
recordApi
Para grabar con prolijidad vamos a hacer otro objeto en nuestra aplicacion: recordApi
. Se va a encargar de:
- inicializar el boton
- obtener el archivo de grabacion llamando a
getRecordFile()
- manejar las llamadas a
startRecord()
ystopRecord()
- usar
mediaApi.load
cuando termine la grabacion para que podamos escucharla
Inicializacion
var recordApi = {
initialize: function(){
//inicializar el boton
$('#rec').click(function(e){
e.preventDefault();
if(recordApi.isRecording) {
recordApi.stop();
}else{
recordApi.record();
}
});
//guardamos una referencia al boton
recordApi.button = $('#rec');
//inicializar estado
recordApi.isRecording = false;
//obtener la ruta al archivo de grabacion
getRecordFile(function(file){
recordApi.recordFile = file.nativeURL;
});
},
Handlers
El handler de error solo nos muestra el error. onStop
(que en mediaApi
llamamos onSuccess
) se encarga de que cuando termine la grabacion, esta se cargue en mediaApi
.
Y onStatus
ahora cumple la funcion de decorar la interfaz: a medida que detecte el cambio a RUNNING
(que en el caso de grabacion indica que esta grabando) cambiara el boton y cuando detecte el cambio a STOPPED
lo volvera a la normalidad:
onStop: function(){
recordApi.media.release();
mediaApi.load(recordApi.recordFile);
},
onError: function(err){
console.log('Recording error');
console.log(err);
},
onStatus: function(status){
switch(status) {
case Media.MEDIA_RUNNING:
console.log('Status change: running');
recordApi.isRecording = true;
recordApi.button.css('background-color','red');
recordApi.button.text('Grabando...');
break;
case Media.MEDIA_STOPPED:
console.log('Status change: stopped');
recordApi.isRecording = false;
recordApi.button.css('background-color', '#333');
recordApi.button.text('Grabar()');
break;
}
},
Start/Stop
Por ultimo estan las funciones que ejecuta el boton. Como las consecuencias de estas acciones las estamos manejando desde onStatus
, aca solo indicamos las acciones a tomar:
record: function(){
recordApi.media = new Media(recordApi.recordFile, recordApi.onStop, recordApi.onError, recordApi.onStatus);
recordApi.media.startRecord();
},
stop: function(){
recordApi.media.stopRecord();
},
Revisamos un poco la sintaxis y probamos.
Nota: para revisar la sintaxis es bueno mantener ordenado el objeto que estamos creando. Basicamente el objeto recordApi
tiene esta estructura (sin importar el orden de los metodos):
var recordApi = {
initialize: function(){},
record: function(){},
stop: function(){},
onStop: function(){},
onError: function(err){},
onStatus: function(status){}
}
Continuaremos revisando la interfaz y funcionalidad de Interview Recorder