Public, protected, default and private in Java

Here is some simple rules to memorize these access modifiers :

  • private: class scope.
  • default (or package-private): package scope
  • protected: package scope + child (like pakage, but we can subclass it from different pakages). The protected modifier always keeps the “parent-child” relationship.
  • public : everywhere.

As a result, if we divide access right into three rights:

  • (D)irect (invoke from a method inside the same class)
  • (R)eference (invoke a method using a reference to the class, or via “dot” syntax)
  • (I)nheritance (via subclassing)

then we have this simple table:

enter image description here

Advertisements
Posted in Java | Tagged | Leave a comment

Get URL Base / Hostname of the servlet

Here is a simple method for getting hostname of the current servlet:

public String getUrlBase(HttpServletRequest request) 
        throws MalformedURLException {
    URL requestUrl = new URL(request.getRequestURL().toString());
    String portString = 
            requestUrl.getPort() == -1 ? "" : ":" + requestUrl.getPort();
    return requestUrl.getProtocol() + 
            "://" + requestUrl.getHost() + portString + "/";
}
Posted in Java | Tagged , | 2 Comments

Work with IO Stream in Java

In java, one can divide streams into two main types:

  • Connection streams, which Java use to directly connect to the source data (File on external memory, Network stream…):
    – FileInputStream/ FileOutStream is used to read binary file
    – FileReader/FileWriter is used to work with text file
    ….
  • Filter streams, or chain streams, used to wrap these connection streams in order to give it more functionality.
    – BufferedInputStream /BufferedOutputStream used to chain binary file
    – BufferedReader/ BufferedWriter used to chain text file

And here are some examples describing the typical way to:

  • Read / Write to binary file
  • Read / Write to text file
  • Read / Write to object (when using serializable)

1. Read / Write to binary file

Read:

package com.nxhoaf.io.binary;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;

public class MyBinaryFileReader {
    public static void main(String[] args) {
        try {
            // Create the source
            File file = new File("resources/input.bin");
            // Create a "connection" stream, which connect directly to source
            FileInputStream fis = new FileInputStream(file);
            // Create a "chain" (or "filter") stream
            BufferedInputStream bis = new BufferedInputStream(fis);

            // We temporally store the whole content here
            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            byte[] buffer = new byte[1024];
            int length = 0;
            while ((length = bis.read(buffer)) > 0) {
                baos.write(buffer, 0, length);
            }

            // Ok, get the whole content
            byte[] content = baos.toByteArray();
            for (byte b : content) {
                // Convert to int to apply Integer method
                // here, we use byte to store bits, so, use bit mask to convert
                // unsigned byte to int
                int i = b & 0xFF;
                System.out.print(Integer.toHexString(i) + "");

                // For more info:
                // int i = 234;
                // byte b = (byte) i;
                // System.out.println(b); // -22
                // int i2 = b & 0xFF;
                // System.out.println(i2); // 234
            }

            baos.close();
            bis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Writer:

package com.nxhoaf.io.binary;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;

public class MyBinaryFileWriter {
    public static void main(String[] args) {
        try {
            // Create the source
            File file = new File("resources/output.bin");
            // Create a "connection" stream, which connect directly to source
            FileOutputStream fos = new FileOutputStream(file);
            // Create a "chain" (or "filter") stream
            BufferedOutputStream bos = new BufferedOutputStream(fos);

            byte[] bytes = {
                    0x48,// H
                    0x65,// e
                    0x6C,// l
                    0x6C,// l
                    0x6F,// o
            };

            bos.write(bytes);
            bos.close();

            // As the content is in binary form (not human-readable), we cannot
            // read it, use this tool instead to check the file content
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2.Read / Write to text file
Read:

package com.nxhoaf.io.text;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

/**
 * TextFileReader demo
 * @author nxhoaf
 *
 */
public class MyTextFileReader {
    public static void main(String[] args) {
        try {
            // Create the source
            File file = new File("resources/input-text.txt");
            // Create a "connection" stream, which connect directly to source
            FileReader fileReader = new FileReader(file);
            // Create a "chain" (or "filter") stream
            BufferedReader reader = new BufferedReader(fileReader);

            String line = "";
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

And write:

package com.nxhoaf.io.text;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;

/**
 * TextFileWriter demo
 * @author nxhoaf
 *
 */
public class MyTextFileWriter {
    public static void main(String[] args) {
        try {
            // Create the source
            File file = new File("resources/output-text.txt");
            // Create a "connection" stream, which connect directly to source
            FileWriter fileWriter = new FileWriter(file);
            // Create a "chain" (or "filter") stream
            BufferedWriter writer = new BufferedWriter(fileWriter);

            String[] lines = {"one", "two", "three"};

            for (String line : lines) {
                writer.write(line + "\n");
            }

            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3. Read / Write to object (when using serializable)
We’ll use Person.java class described below to test the read/write
Person class:

package com.nxhoaf.io.object;

import java.io.Serializable;

public class Person implements Serializable {
    private static final long serialVersionUID = 1091172054761152924L;
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String toString() {
        String result = "";
        result += Person.class.getName() + "\n";
        result += "name: " + name + " age: " + age;
        return result;
    }
}

Read:

package com.nxhoaf.io.object;

import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;

/**
 * ObjectReader demo
 * @author nxhoaf
 *
 */
public class MyObjectReader {
    public static void main(String[] args) {
        try {
            // Create the source
            File file = new File("resources/input.obj");
            // Create a "connection" stream, which connect directly to source
            FileInputStream fis = new FileInputStream(file);
            // Create a "chain" (or "filter") stream
            ObjectInputStream oos = new ObjectInputStream(fis);

            Person p1 = (Person) oos.readObject();
            Person p2 = (Person) oos.readObject();

            System.out.println(p1);
            System.out.println(p2);

            oos.close();

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

Write:

package com.nxhoaf.io.object;

import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

/**
 * ObjectWriter demo
 * @author nxhoaf
 *
 */
public class MyObjectWriter {
    public static void main(String[] args) {
        try {
            // Create the source
            File file = new File("resources/output.obj");
            // Create a "connection" stream, which connect directly to source
            FileOutputStream fos = new FileOutputStream(file);
            // Create a "chain" (or "filter") stream
            ObjectOutputStream oos = new ObjectOutputStream(fos);

            Person[] persons = {
                    new Person("Peter", 11),
                    new Person("Mary", 21)
            };

            for (Person p : persons) {
                oos.writeObject(p);
            }

            oos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Posted in Java | Tagged | Leave a comment

Functional inheritance in Javascript.

In his book “JavaScript the good parts”, Crockford introduced a new pattern of inheritance: functional inheritance. The idea is to use closure to make private fields, and to use privileged functions to access to these fields. Here, we will use a “People Inheritance” example to illustrate this pattern (see figure):

Image

1. People class

var people = function (info) {
    var that = {};
    // private
    var name = info.name;
    var age = info.age;
    // getter and setter
    that.getName = function () {
        return name;
    };
    that.setName = function (aName) {
        name = aName;
    };
    that.getAge = function () {
        return age;
    };
    that.setAge = function (anAge) {
        age = anAge;
    };
    return that;
};

2. Student class

var student = function (info) {
    // super
    var that = people(info);
    // private
    var major = info.major;
    that.getMajor = function () {
        return major;
    };
    that.setMajor = function (aMajor) {
        major = aMajor;
    };
    return that;
};

3. ItStudent class

var itStudent = function (info) {
    // super
    var that = student(info);
    var language = info.language;
    that.getLanguage = function () {
        return language;
    };
    that.setLanguage = function (aLanguage) {
        language = aLanguage;
    };
    return that;
};

Here is a simple test:

var p = people({name : "Alex", age : 24});
console.debug(p.age); // undefined
console.debug(p.getAge()); // 24

var s = student({name : "Alex", age : 24, major : "IT"});
console.debug(s.getName()); // Alex
console.debug(s.getMajor()); // IT

var i = itStudent({name : "Alex", age : 24, major : "IT", 
                   language : "js"});
console.debug(i.language); // Undefined
console.debug(i.getName()); // Alex
console.debug(i.getMajor()); // IT
console.debug(i.getLanguage()); // js

Note that I didn’t use upper case for the first character. In fact, Javascript have a simple convention: if the function is called with “new” keyword, upper case will be used for the first character, otherwise, lower case will be used. The reason for it related to the use of “this” object: “this” will refer to the created object if you use “new”, if not, “this” will refer to the global object.

Posted in ECMAScript, Javascript | Tagged | Leave a comment

Speech to Text in Action Script 3

Action script 3 and AIR are quite interesting with many supports to desktop and mobile application, and one of them is sound support. Inspired by this post, I would like to create a small application using AS3 and AIR, which can be used to record voice and then, send it to google spech-to-text api. As a result, we will get a text-format of what we’ve said.

Here, we will use Action Script 3 and Adobe Air 3.2 in Window 7 professional. The IDE is Flash Builder 4.6.

Before starting, It’s better to summarize what we will do:

1. Create a simple GUI interface.

2. Record what we said using micro

3. Encode raw data to .flac and send it to google

4. Mp3 encoder implementation.

Part1: Create a simple GUI interface.

Click here to download full source code

After this section, we will be able to create a simple interface like this:

Interface

Figure1: Interface of our application

  1. Create a new Flex Project: From within Flash Builder, choose File -> New -> Flex Project. In the Project name textbox, choose a name, SimpleSoundApp for ex. In TheApplication Type, choose Desktop (run in Adobe Air) as we will work with Desktop application
NewProject

Figure 2: NewProject

Choose Finish, now, you should have a project with directory structure like this:

DirectoryStructure

  1. Open SimpleSoundApp.mxml and add this code to it:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                       xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx"
                       width="315" height="438" title="Simple Recorder">
    <fx:Declarations>         <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
    <fx:Script>                 
        <![CDATA[             
            import mx.controls.Alert;
            public function toMp3IsClicked() {
                Alert.show("toMp3IsClicked");
            }
            public function toWavIsClicked() {
                Alert.show("toMp3IsClicked");
            }
            public function toFlacIsClicked() {
                Alert.show("toMp3IsClicked");
            }
            public function recordIsClicked() {
                Alert.show("toMp3IsClicked");
                label.text = "Recording...";
                record.enabled = false;
                stop.enabled = true;
            }
            public function stopIsClicked() {
                Alert.show("toMp3IsClicked");
                label.text = "Click Record to begin.";
                record.enabled = true;
                stop.enabled = false;
                toMp3.enabled = true;
                toWav.enabled = true;
                toFlac.enabled = true;
            }
            public function sendIsClicked() {
                Alert.show("toMp3IsClicked");
            }
        ]]>
    </fx:Script>
    <s:Group x="31" y="31" width="250" height="212">
        <s:Label id="label" x="10" y="80" width="240" height="55"
                 fontSize="24" text="Click Record to begin." />
    </s:Group>
    <s:Group x="31" y="269" width="250" height="134">
        <s:Button id="record" x="10" y="30" label="Record"
                  click="recordIsClicked();" name="record" />
        <s:Button id="stop" x="90" y="30" label="Stop" click="stopIsClicked();"
                  enabled="false" name="stop" />
        <s:Button id="send" x="170" y="30" label="Send" click="sendIsClicked()"
                  name="send" />
        <s:Button id="toMp3" x="10" y="90" label="To Mp3" click="toMp3IsClicked();"
                  enabled="false" name="toMp3" />
        <s:Button id="toWav" x="88" y="90" width="70" label="To Wav"
                  click="toWavIsClicked();" enabled="false" name="toWav" />
        <s:Button id="toFlac" x="170" y="90" label="To Flac"
                  click="toFlacIsClicked();" enabled="false" name="toFlac" />
    </s:Group>
</s:WindowedApplication>

For now, we have a simple interface as in figure 1. Note that all functions we listed here are just place-holder functions. It will be discussed later. Also, label is changed based on the current state of our application and buttons are enabled only if the record is finished (after clicking Record, and then Stop).

 

Part2: Record what we said using micro

In part 1, We’ve seen how to create a simple interface in AS3. Based on this interface, we will implement two function recordIsClicked and stopIsClicked. At the end of part 2, The application should be able to record what we said, and then, replay it.

  1. First, create a new pakage com.gmail.nxhoaf, and then, create a our Recorder class.
Recorder
Figure 3: Recorder
  1. Handle for recordIsClicked and stopIsClicked

We need to implement two functions to handle recordIsClicked and stopIsClicked named startRecord and stopRecord respectively.

  • Here is the startRecord() function
public function startRecord() :void {
    this.bytes = new ByteArray;
    mic.gain = 100;
    mic.rate = 44;
    mic.setSilenceLevel(0,4000);                    
    // Remove playback listener if any
    mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, onPlaying);
    // Add record listener
    mic.addEventListener(SampleDataEvent.SAMPLE_DATA, onRecording);
}

When recording, the device buffers the microphone input, so we need to create a ByteArray object. rate defines the sample rate and impacts the quality of the input audio, here, we use 44, this means 44100 Hz. Gain is the amount by which the microphone boosts the signal. We want to want to boost the microphone to the maximum assuming that were’re more likely in a noisy place.

Then, we remove any previous listener if any, and add our new listener. Here is the code for onRecording() function:

private function onRecording(event:SampleDataEvent):void {
    while (event.data.bytesAvailable) {
        var sample:Number = event.data.readFloat();
        bytes.writeFloat(sample);
    }
}
    • To stop the recording process, just remove the event listener:
public function stopRecord() :void {
    mic.removeEventListener(SampleDataEvent.SAMPLE_DATA,onRecording);
}
  1. Playback

To playback, we read the sample stored in our ByteArray object and play it. Note that after recording, we must rewind the byte position to the beginning…

public function playback () :void {
    if (bytes.length > 0) {
        bytes.position = 0;
        var sound:Sound = new Sound();
        sound.addEventListener(SampleDataEvent.SAMPLE_DATA,onPlaying);
        sound.play();
    }
}

private function onPlaying(event:SampleDataEvent): void {
    var sample:Number;
    for (var i:int = 0; i < 8192; i++) {
        if (!bytes.bytesAvailable) return;
        sample = bytes.readFloat();
        event.data.writeFloat(sample);
        event.data.writeFloat(sample);
    }
}

Audio data is made of samples. 8,192 samples is the maximum amount of data that
can be written to a sound at one time. If you try to play more samples, you will get a
runtime error. If you provide fewer than 2,048 samples, the application will assume the sound reached its end and will stop.

Even though the microphone is monophonic in, sound data needs to
be written to both left and right output channels. If you don’t do this, the recording
playback will be twice the speed at which it was recorded because two samples will run in one stereo sample of the sound object.

Full source code of theRecorder.as class is shown below:

package com.gmail.nxhoaf { 
    import flash.events.SampleDataEvent; 
    import flash.media.Microphone; 
    import flash.media.Sound;  
    import flash.utils.ByteArray;  
    public class Recorder     { 
        private var bytes:ByteArray;     
        private var mic:Microphone;  
        public function Recorder()
        {
        }

        public function setMicrophone (mic : Microphone) {
            this.mic = mic;
        }

        public function getMicrophone () {
            return mic;
        }

        public function startRecord() :void {
            this.bytes = new ByteArray();
            mic.gain = 100;
            mic.rate = 44;
            mic.setSilenceLevel(0,4000);                    
            // Remove playback listener if any
            mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, onPlaying);
            // Add record listener
            mic.addEventListener(SampleDataEvent.SAMPLE_DATA, onRecording);
        }

        public function stopRecord() :void {
            mic.removeEventListener(SampleDataEvent.SAMPLE_DATA,onRecording);
        }

        public function playback () :void {
            if (bytes.length > 0) {
                bytes.position = 0;
                var sound:Sound = new Sound();
                sound.addEventListener(SampleDataEvent.SAMPLE_DATA,onPlaying);
                sound.play();
            }
        }

        private function onRecording(event:SampleDataEvent):void {
            while (event.data.bytesAvailable) {
                var sample:Number = event.data.readFloat();
                bytes.writeFloat(sample);
            }
        }

        private function onPlaying(event:SampleDataEvent): void {
            var sample:Number;
            for (var i:int = 0; i < 8192; i++) {
                if (!bytes.bytesAvailable) return;
                sample = bytes.readFloat();
                event.data.writeFloat(sample);
                event.data.writeFloat(sample);
            }
        }
    }
}
  1. Modify SimpleSoundApp‘s interface

After implementing all back-end functions, we go to the front-end now, and modify a little bit the source, Alert.show(…) placeholder will be replaced with an more appropriate functions. Here is the new version of our SimpleSoundApp.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                       xmlns:s="library://ns.adobe.com/flex/spark"
                       xmlns:mx="library://ns.adobe.com/flex/mx"
                       width="315" height="438" title="Simple Recorder">
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
    <fx:Script>
        <![CDATA[
            import com.gmail.nxhoaf.Recorder;
            import com.gmail.nxhoaf.SoundFormat;

            import mx.controls.Alert;
            var recorder : Recorder = new Recorder();

            public function toMp3IsClicked() {
                Alert.show("toMp3IsClicked");
            }

            public function toWavIsClicked() {
                Alert.show("toWavIsClicked");
            }

            public function playbackIsClicked() {
                recorder.playback();
            }

            public function recordIsClicked() {
                var mic : Microphone = Microphone.getMicrophone();
                if (mic == null) {
                    return;
                }
                recorder.setMicrophone(mic);
                recorder.startRecord();
                label.text = "Recording...";
                record.enabled = false;
                stop.enabled = true;
            }

            public function stopIsClicked() {
                recorder.stopRecord();
                label.text = "Click Record to begin.";
                record.enabled = true;
                stop.enabled = false;
                toMp3.enabled = true;
                toWav.enabled = true;
                playback.enabled = true;
            }

            public function sendIsClicked() {
                Alert.show("sendIsClicked");        
            }
        ]]>
    </fx:Script>
    <s:Group x="31" y="31" width="250" height="212">
        <s:Label id="label" x="10" y="80" width="240" height="55" fontSize="24"
                 text="Click Record to begin."/>
    </s:Group>
    <s:Group x="31" y="269" width="250" height="134">
        <s:Button id="record" x="10" y="30" label="Record" click="recordIsClicked();" name="record"/>
        <s:Button id="stop" x="90" y="30" label="Stop" click="stopIsClicked();" enabled="false"
                  name="stop"/>
        <s:Button id="send" x="170" y="30" label="Send" click="sendIsClicked()" name="send"/>
        <s:Button id="toMp3" x="10" y="90" label="To Mp3" click="toMp3IsClicked();" enabled="false"
                  name="toMp3"/>
        <s:Button id="toWav" x="88" y="90" width="70" label="To Wav" click="toWavIsClicked();"
                  enabled="false" name="toWav"/>
        <s:Button id="playback" x="170" y="90" label="Play! " click="playbackIsClicked();"
                  enabled="false" name="playback"/>
    </s:Group>
</s:WindowedApplication>

That is, we’re now able to record sound, and play it back. In the next part, We’ll examine how to encode what we said to .mp3, .wav, and then, we will send it to google speech api to get out text-form of what we said…

 

Part 3: Encode raw data to .flac and send it to Google

So far, we’ve created a small application with a simple interface and some methods(record, playback..). Now, we’ll try to send what we say to Google. As a result, we should have the result in text-format. For example, when you say “Hello”, you will get a Json data structure telling you that your utterance is “Hello”.

In this part, we will:

  • Encode audio stream to .flac format. In fact, the audio file sent to Google can be .flac or Speex, but here, we choose .flac
  • Send .flac format to Google.

1. Encode audio stream to .flac format

First, download this lib which is used to encode the audio stream as .flac file. In the downloaded file, copy flac.swc and put it into libs folder of the project:

2. Send .flac format to Google.

The sending code is put in encodeToFlacAndSend() function:

public function encodeToFlacAndSend() : void {    
    var flacCodec:Object;
    flacCodec = (new cmodule.flac.CLibInit).init();
    bytes.position = 0;
    var rawData: ByteArray = new ByteArray();
    var flacData : ByteArray = new ByteArray();
    rawData = convert32to16(bytes);
    flacData.endian = Endian.LITTLE_ENDIAN;
    flacCodec.encode(    encodingCompleteHandler, 
        encodingProgressHandler, 
        rawData, 
        flacData, 
        rawData.length, 
        30);            
    function encodingCompleteHandler(event:*):void {
        trace("FLACCodec.encodingCompleteHandler(event):", event);
        //Alert.show(flacData.length.toString());            
        var PATH:String = "https://www.google.com/speech-api/v1/recognize?xjerr=1&client=chromium&lang=en-US";
        var urlRequest:URLRequest = new URLRequest(PATH);
        var urlLoader:URLLoader = new URLLoader();
        urlRequest.contentType = "audio/x-flac; rate=44000";
        urlRequest.data = flacData;
        urlRequest.method = URLRequestMethod.POST;

        urlLoader.dataFormat = URLLoaderDataFormat.TEXT;// default
        urlLoader.addEventListener(Event.COMPLETE, urlLoader_complete);
        urlLoader.addEventListener(ErrorEvent.ERROR, urlLoader_error);
        urlLoader.load(urlRequest);

        function urlLoader_complete(evt:Event):void {
            Alert.show(urlLoader.data);

        }
        function urlLoader_error(evt:ErrorEvent): void {
            Alert.show("*** speech to text *** " + evt.toString());
        }
    }

    function encodingProgressHandler(progress:int):void {
        trace("FLACCodec.encodingProgressHandler(event):", progress);;
    }
}

For now, SimpleSoundApp looks like this:

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                       xmlns:s="library://ns.adobe.com/flex/spark"
                       xmlns:mx="library://ns.adobe.com/flex/mx"
                       width="315" height="438"
                       title="Simple Recorder">
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
    <fx:Script >
        <![CDATA[
            import mx.controls.Alert;
            import com.gmail.nxhoaf.Recorder;
            var recorder : Recorder = new Recorder();
            public function toMp3IsClicked() {
                Alert.show("toMp3IsClicked");
            }
            public function toWavIsClicked() {
                Alert.show("toWavIsClicked");
            }
            public function playbackIsClicked() {
                recorder.playback();
            }
            public function recordIsClicked() {
                var mic : Microphone = Microphone.getMicrophone();
                if (mic == null) {
                    return;
                }
                recorder.setMicrophone(mic);
                recorder.startRecord();
                label.text = "Recording...";
                record.enabled = false;
                stop.enabled = true;
            }
            public function stopIsClicked() {
                recorder.stopRecord();
                label.text = "Click Record to begin.";
                record.enabled = true;
                stop.enabled = false;
                toMp3.enabled = true;
                toWav.enabled = true;
                playback.enabled = true;
            }
            public function sendIsClicked() {
                recorder.encodeToFlacAndSend();
            }
        ]]>
    </fx:Script>
    <s:Group x="31" y="31" width="250" height="212">
        <s:Label id="label" x="10" y="80" width="240" height="55" fontSize="24" text="Click Record to begin."/>
    </s:Group>
    <s:Group x="31" y="269" width="250" height="134">
        <s:Button id="record" name="record" x="10" y="30" label="Record" click="recordIsClicked();"/>
        <s:Button id="stop" name="stop" x="90" y="30" label="Stop" enabled="false" click="stopIsClicked();"/>
        <s:Button x="170" y="30" id="send" name="send" label="Send" click="sendIsClicked()"/>

        <s:Button id = "toMp3" name="toMp3" x="10" y="90" label="To Mp3" enabled="false" click="toMp3IsClicked();"/>
        <s:Button id="toWav" x="88" y="90" width="70" label="To Wav" enabled="false" name="toWav" click="toWavIsClicked();"/>
        <s:Button x="170" y="90" name="playback" id="playback" label="Play! " enabled="false" click="playbackIsClicked();"/>
    </s:Group>
</s:WindowedApplication>

And here is our Recorder.as:

package com.gmail.nxhoaf
{
    import cmodule.flac.CLibInit;

    import flash.events.ErrorEvent;
    import flash.events.Event;
    import flash.events.SampleDataEvent;
    import flash.media.Microphone;
    import flash.media.Sound;
    import flash.net.FileReference;
    import flash.net.URLLoader;
    import flash.net.URLLoaderDataFormat;
    import flash.net.URLRequest;
    import flash.net.URLRequestMethod;
    import flash.utils.ByteArray;
    import flash.utils.Endian;

    import mx.controls.Alert;

    public class Recorder
    {
        private var bytes:ByteArray;
        private var mic:Microphone;

        private static const FLOAT_MAX_VALUE:Number = 1.0;
        private static const SHORT_MAX_VALUE:int = 0x7fff;
        public function Recorder()
        {
        }

        public function setMicrophone (mic : Microphone) {
            this.mic = mic;
        }

        public function getMicrophone () {
            return mic;
        }

        public function startRecord() :void {
            this.bytes = new ByteArray();
            mic.gain = 100;
            mic.rate = 44;
            mic.setSilenceLevel(0,4000);
            // Remove playback listener if any
            mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, onPlaying);
            // Add record listener
            mic.addEventListener(SampleDataEvent.SAMPLE_DATA, onRecording);
        }

        public function stopRecord() :void {
            mic.removeEventListener(SampleDataEvent.SAMPLE_DATA,onRecording);
        }

        public function playback () :void {
            if (bytes.length > 0) {
                bytes.position = 0;
                var sound:Sound = new Sound();
                sound.addEventListener(SampleDataEvent.SAMPLE_DATA,onPlaying);
                sound.play();
            }
        }

        private function onRecording(event:SampleDataEvent):void {
            while (event.data.bytesAvailable) {
                var sample:Number = event.data.readFloat();
                bytes.writeFloat(sample);
            }
        }

        private function onPlaying(event:SampleDataEvent): void {
            var sample:Number;
            for (var i:int = 0; i < 8192; i++) {
                if (!bytes.bytesAvailable) return;
                sample = bytes.readFloat();
                event.data.writeFloat(sample);
                event.data.writeFloat(sample);
            }
        }

        public function encodeToFlacAndSend() : void {
            var flacCodec:Object;
            flacCodec = (new cmodule.flac.CLibInit).init();
            bytes.position = 0;
            var rawData: ByteArray = new ByteArray();
            var flacData : ByteArray = new ByteArray();
            rawData = convert32to16(bytes);
            flacData.endian = Endian.LITTLE_ENDIAN;
            flacCodec.encode( encodingCompleteHandler,
                encodingProgressHandler,
                rawData,
                flacData,
                rawData.length,
                30);
            function encodingCompleteHandler(event:*):void {
                trace("FLACCodec.encodingCompleteHandler(event):", event);
                //Alert.show(flacData.length.toString());
                var PATH:String = "https://www.google.com/speech-api/v1/recognize?xjerr=1&client=chromium&lang=en-US";
                var urlRequest:URLRequest = new URLRequest(PATH);
                var urlLoader:URLLoader = new URLLoader();
                urlRequest.contentType = "audio/x-flac; rate=44000";
                urlRequest.data = flacData;
                urlRequest.method = URLRequestMethod.POST;

                urlLoader.dataFormat = URLLoaderDataFormat.TEXT;// default
                urlLoader.addEventListener(Event.COMPLETE, urlLoader_complete);
                urlLoader.addEventListener(ErrorEvent.ERROR, urlLoader_error);
                urlLoader.load(urlRequest);

                function urlLoader_complete(evt:Event):void {
                    Alert.show(urlLoader.data);

                }
                function urlLoader_error(evt:ErrorEvent): void {
                    Alert.show("*** speech to text *** " + evt.toString());
                }
            }

            function encodingProgressHandler(progress:int):void {
                trace("FLACCodec.encodingProgressHandler(event):", progress);;
            }
        }

        /**
         * Converts an (raw) audio stream from 32-bit (signed, floating point)
         * to 16-bit (signed integer).
         *
         * @param source The audio stream to convert.
         */
        private function convert32to16(source:ByteArray):ByteArray {
            trace("BitrateConvertor.convert32to16(source)", source.length);

            var result:ByteArray = new ByteArray();
            result.endian = Endian.LITTLE_ENDIAN;

            while( source.bytesAvailable ) {
                var sample:Number = source.readFloat() * SHORT_MAX_VALUE;

                // Make sure we don't overflow.
                if (sample < -SHORT_MAX_VALUE) sample = -SHORT_MAX_VALUE;
                else if (sample > SHORT_MAX_VALUE) sample = SHORT_MAX_VALUE;

                result.writeShort(sample);
            }

            trace(" - result.length:", result.length);
            result.position = 0;
            return result;
        }

    }
}

This’s how I sent our recorded audio to Google. The next part will discuss how to encode our audio stream as .mp3 and .wav.

 

Part 4: Implementation of wav and mp3 encoder.

In this part, we’ll try to save an audio stream captured from microphone to .mp3 or .wav format. Here is what we will do:

  1. Save audio stream to .wav format

  2. Save audio stream to .mp3 format.

1. Save audio stream to .wav format

First, download the audio_sampler.zip here. Extract and then copy all the com folder to the src folder in our project. You should end up with something like this:

WAVWriter.as

Next, we’ll convert an audio input stream captured from microphone to .wav. Here is the encode function:

private function encodeToWav(bytes:ByteArray) : void {
    var wav:WAVWriter = new WAVWriter();
    wav.numOfChannels = 1;
    wav.sampleBitRate = 16;
    wav.samplingRate =     44100;

    bytes.position = 0;
    var wavData : ByteArray = new ByteArray();
    wavData.endian = Endian.BIG_ENDIAN;
    wav.processSamples(wavData,bytes,44100,1);            
    //wavData.position = 0;    
    (new FileReference()).save(wavData, ".wav");
}

The first thing is to create a new WAVWriter. We set numOfChannels to 1, it means that mono audio will be used. Then, we set the BitRate to 16, and Sample Rate to 44100 (for more info about Bit Rate and Sample Rate, see Bit Rate and Sample Rate). Wav. Also note that Big Endian is used.

Then, the audio stream is encoded as wav data and then, saved as .wav file to the external memory

For the interface, we’ll implement the toWavIsClicked():

public function toWavIsClicked() {
   recorder.saveAs(SoundFormat.WAV);
}

SoundFormat is just a class describing some sound extensions:

public class SoundFormat {
    public static const MP3:String = "mp3";
    public static const WAV:String = "wav";
    public static const FLAC:String = "flac";
}

For now, you can save your audio stream to .wav extension as in below:

saveToWav

2. Save audio stream to .mp3 format

In order to encode the audio stream as .mp3, download this lib. In the downloaded file, go to the bin folder and copy shineMP3_alchemy.swc into your libs folder:

mp3 encoder

Then, modify the Recorder.as a little bit :

private function encodeToMp3(bytes:ByteArray) : void {
    var wav:WAVWriter = new WAVWriter();
    wav.numOfChannels = 1;
    wav.sampleBitRate = 16;
    wav.samplingRate = 44100;

    bytes.position = 0;
    var wavData : ByteArray = new ByteArray();
    wavData.endian = Endian.BIG_ENDIAN;
    wav.processSamples(wavData,bytes,44100,1);            
    wavData.position = 0;

    mp3Encoder = new ShineMP3Encoder(wavData);
    mp3Encoder.addEventListener(Event.COMPLETE, mp3EncodeComplete);
    mp3Encoder.addEventListener(ProgressEvent.PROGRESS, mp3EncodeProgress);
    mp3Encoder.addEventListener(ErrorEvent.ERROR, mp3EncodeError);
    mp3Encoder.start();

    function mp3EncodeProgress(event : ProgressEvent) : void {
    }

    function mp3EncodeError(event : ErrorEvent) : void {
        Alert.show(event.toString());
    }

    function mp3EncodeComplete(event : Event) : void {
        mp3Encoder.saveAs();
    }
}

As the third-party lib accepts only .wav input format, we first convert the audio stream to .wav file. Then, use we convert wav back to mp3 file. As soon as the conversion finished, mp3EncodeComplete is called to save the encoded audio stream to .mp3.

public function toMp3IsClicked() {
    recorder.saveAs(SoundFormat.MP3);
}

mp3 encoder

 

Disclaim

This project is one part of my previous work.At the time of this writing, it worked perfectly. Now, I haven’t worked on it anymore. Thus, I can no longer support you on this.

Thank you!

Posted in ActionScript3 | Tagged , | Leave a comment