I'm trying to record from my headphone port directly into my microphone port (using an aux cable) in Adobe Flash Builder / AIR, but I'm having a couple problems:
1. The recording never sounds "full" compared to the original MP3 file that I'm playing in iTunes. PCM is supposed to be an uncompressed format, so the quality should be lossless.
2. The saved recording (WAV) is always half the length of what I recorded. (Example: if i record for a minute, I'll end up with a 0:30 wav file, with the second half of the recording not there)
This is the code I'm using. Any ideas why I might be encountering these problems?
import com.adobe.audio.format.WAVWriter;
import flash.display.NativeWindowSystemChrome;
import flash.events.Event;
import flash.events.SampleDataEvent;
import flash.events.TimerEvent;
import flash.filters.BitmapFilterQuality;
import flash.filters.DropShadowFilter;
import flash.media.Microphone;
import flash.media.Sound;
import flash.system.Capabilities;
import flash.utils.Timer;
import mx.controls.Alert;
import mx.core.UIComponent;
import mx.core.Window;
import mx.events.CloseEvent;
import ui.AudioVisualization;
private var shadowFilter:DropShadowFilter;
private var newWindow:Window;
// MICROPHONE STUFFZ
[Bindable] private var microphoneList:Array = Microphone.names; // Set up list of microphones
protected var microphone:Microphone; // Initialize Microphone
protected var isRecording:Boolean = false; // Variable to check if we're recording or not
protected var micRecording:ByteArray; // Variable to store recorded audio data
public var file:File = File.desktopDirectory;
public var stream:FileStream = new FileStream();
public var i:int = 0;
public var myTimer:Timer;
// [Start] Recording Function
protected function startMicRecording():void
{
if(isRecording == true) stopMicRecording();
else
{
consoleTA.text += "\nRecording started...";
consoleTA.scrollToRange(int.MAX_VALUE, int.MAX_VALUE);
isRecording = true;
micRecording = new ByteArray();
microphone = Microphone.getMicrophone(comboMicList.selectedIndex);
microphone.gain = 50;
microphone.rate = 44;
microphone.setUseEchoSuppression(false);
microphone.setLoopBack(false);
// Start timer to measure duration of audio clip (runs every 1 seconds)
myTimer = new Timer(1000);
myTimer.start();
// Set amount of time required to register silence
var userSetSilence:int;
if(splitCB.selected == true){
userSetSilence = splitNS.value; // if checkbox is checked, use the value from the numeric stepper
}
else{
userSetSilence = 2;
}
userSetSilence *= 100;
microphone.setSilenceLevel(0.5, userSetSilence); // 2 seconds of silence = Register silence with onActivity (works for itunes skip)
microphone.addEventListener(SampleDataEvent.SAMPLE_DATA, gotMicData);
microphone.addEventListener(ActivityEvent.ACTIVITY, this.onMicActivity);
}
}
// [Stop] Recording Function
protected function stopMicRecording():void
{
myTimer.stop(); // Stop timer to get final audio clip duration
consoleTA.text += "\nRecording stopped. (" + myTimer.currentCount + "s)";
consoleTA.scrollToRange(int.MAX_VALUE, int.MAX_VALUE);
isRecording = false;
if(!microphone) return;
microphone.removeEventListener(SampleDataEvent.SAMPLE_DATA, gotMicData);
}
private function gotMicData(micData:SampleDataEvent):void
{
this.visualization.drawMicBar(microphone.activityLevel,0xFF0000);
if(microphone.activityLevel <= 5)
{
consoleTA.text += "\nNo audio detected"; //trace("no music playing");
consoleTA.scrollToRange(int.MAX_VALUE, int.MAX_VALUE);
}
// micData.data contains a ByteArray with our sample.
//Old: micRecording.writeBytes(micData.data);
while(micData.data.bytesAvailable) {
var sample:Number = micData.data.readFloat();
micRecording.writeFloat(sample);
}
}
protected function onMicActivity(event:ActivityEvent):void
{
//trace("activating=" + event.activating + ", activityLevel=" + microphone.activityLevel);
consoleTA.text += "\nactivating=" + event.activating + ", activityLevel=" + microphone.activityLevel;
consoleTA.scrollToRange(int.MAX_VALUE, int.MAX_VALUE);
// Mic started recording...
if(event.activating == true)
{
try{
//fs.open(file, FileMode.WRITE);
//fs.writ
}catch(e:Error){
trace(e.message);
}
}
// Mic stopped recording...
if(event.activating == false)
{
if(file)
{
i++;
myTimer.stop();
stopMicRecording();
if(deleteCB.selected == true)
{
if(myTimer.currentCount < deleteNS.value)
{
consoleTA.text += "\nAudio deleted. (Reason: Too short)";
consoleTA.scrollToRange(int.MAX_VALUE, int.MAX_VALUE);
}
else
{
writeWav(i);
}
}
else
{
writeWav(i);
}
startMicRecording();
}
}
}
private function save():void
{
consoleTA.text += "\nSaving..."; //trace("file saved!");
consoleTA.scrollToRange(int.MAX_VALUE, int.MAX_VALUE);
file = new File( );
file.browseForSave( "Save your wav" );
file.addEventListener( Event.SELECT, writeWav );
}
public function writeWav(i:int):void
{
var wavWriter:WAVWriter = new WAVWriter();
// Set settings
micRecording.position = 0;
wavWriter.numOfChannels = 2;
wavWriter.sampleBitRate = 16; //Audio sample bit rate: 8, 16, 24, 32
wavWriter.samplingRate = 44100;
var file:File = File.desktopDirectory.resolvePath("SoundSlug Recordings/"+sessionTA.text+"_"+i+".wav");
var stream:FileStream = new FileStream();
//file = file.resolvePath("/SoundSlug Recordings/testFile.wav");
stream.open( file, FileMode.WRITE );
// convert ByteArray to WAV
wavWriter.processSamples( stream, micRecording, 44100, 1 ); //change to 1?
stream.close();
consoleTA.text += "\nFile Saved: " + file.exists; //trace("saved: " + file.exists);
consoleTA.scrollToRange(int.MAX_VALUE, int.MAX_VALUE);
}
PS: I'm using the standard WavWriter AS class: http://ghostcat.googlecode.com/svn-history/r424/trunk/ghostcatfp10/src/ghostcat/media/WAVWriter.as