Reprint Please specify Source: http://blog.csdn.net/zhaokaiqiang1992
The first two articles describe the principles of sonic verification/communication and the implementation of sound playback, which will be the most important. is also the most difficult thing to understand, is how the sinvoice of these numbers are encoded transmission.
Due to the addition of a large number of hard-to-distinguish callback functions in the source code. For the sake of reading convenience, I did part of the renaming and the collation of the code, we do not feel surprised.
The structure of the project is given first:
This article focuses on the encoder class, the Singenerator class, and the buffer class.
In the previous article, we learned that Sinvoiceplayer is our direct contact and use of the class, using the Sinvoiceplayer. Play (text) method can be very easy to play out the number we want to transmit the corresponding audio signal. Then you can parse it.
In Sinvoiceplayer, the playback operation is performed by invoking the start () method of Pcmplayer, while in Pcmplayer, the audiotrace is called to achieve the last audio playback function.
Through a layer of encapsulation of audiotrace, the simple call of Sinvoiceplayer is finally realized.
Since Audiotrace is the class that finally makes audio playback. So where does the data to be played come from?
The answer is that the data comes from the Encoder class, the Singenerator class, and the buffer class.
Here is the code for encoder, which has been sorted
/* Copyright (C) Gujicheng * * Licensed under the GPL License Version 2.0; * You are not a use this file except in compliance with the License. * * If you had any question, please contact me. * ************************************************************************* * * Author information * * ************************************************************************* * Email: [EMAIL&NB sp;protected] * * * * qq:29600731 * * * * weibo:http://weibo.com/gujicheng197 * * *************************************** */package Com.libra.sinvoice;import Java.util.list;import com.libra.sinvoice.buffer.bufferdata;/** * * @ClassName: Com.libra.sinvoice.Encoder * @Description: Encoder * @author Zhaoka Iqiang * @date 2014-11-16 PM 1:32:17 * */public class Encoder implements Singenerator.singeneratorCallback {Private final static String TAG = "Encoder";p rivate final static int state_encoding = 1;private final static int state_stoped = 2;//index 0, 1, 2, 3, 4, 5, 6//Circlecount,, 10private final static int[] Code_fre Quency = {1422, 1575, 1764, 2004, 2321,2940, 4410};p rivate int mstate;private singenerator msingenerator;private Encoder Callback Encodercallback;public static interface Encodercallback {void Freeencodebuffer (bufferdata buffer); Bufferdata Getencodebuffer ();} Public Encoder (Encodercallback callback, int samplerate, int bits,int buffersize) {encodercallback = Callback;mstate = STA Te_stoped;msingenerator = new Singenerator (this, samplerate, bits, buffersize);} Public final static int Getmaxcodecount () {return code_frequency.length;} Public Final Boolean isstoped () {return (state_stoped = = mstate); Content of input from 0 to (code_frequency.length-1) public void encode (list<integer> codes, int duration) {if (ST ate_stoped = = mstate) {mstate = STAte_encoding;msingenerator.start (); for (int index:codes) {if (state_encoding = = mstate) {if (index >= 0 && in Dex < Code_frequency.length) {//Use sine generator to encode msingenerator.gen (Code_frequency[index], duration);} else {LOGHELPER.D ( TAG, "code index Error");}} else {LOGHELPER.D (TAG, "Encode Force stop"); Msingenerator.stop ();}} public void Stop () {if (state_encoding = = mstate) {mstate = State_stoped;msingenerator.stop ();}} @Overridepublic bufferdata Getgenbuffer () {if (null! = Encodercallback) {return Encodercallback.getencodebuffer ();} return null;} @Overridepublic void Freegenbuffer (Bufferdata buffer) {if (null! = Encodercallback) {Encodercallback.freeencodebuffer ( buffer);}}}
about this class, the main points are the following:
1. This class implements the Singenerator.singeneratorcallback interface, which is actually inside the Singenerator class. This interface is the main completion of data acquisition and release, in the following code I will explain
2. The numbers stored in the array code_frequency represent the corresponding frequencies from 0 to 6, and different data will be encoded according to different frequencies, Circlecount 31, 28, 25, 22, 19, 15, 10 means in the process of encoding, The corresponding frequency of the sine wave in a period of sampling quantity. Number of samples per cycle x Period total = total number of samples.
Remember the previous default_gen_duration=100, this variable refers to each number corresponding to the audio duration, 100 for 100 milliseconds, that is, 0.1 seconds.
As we have said, the default sample rate is 44.1KHZ, which is 44,100 samples in 1s. If you need to play 100 milliseconds, you just need to sample 44100/10=4410 times, because
Private Final Static int [] code_frequency = {1422, 1575, 1764, 2004, 2321,2940, 4410};
If we want to encode the number 0. So we know that 0 the corresponding vibration frequency is 1422HZ. This one is for a second. What if it's 100ms? Is 142.2HZ, we use, 142.2x31=4408.2, nearly 4,410 times, so say, we are based on the frequency of the audio we want to generate. You can know how many times each cycle needs to be taken, and you can calculate the interval of the sample.
Since encoder is only a wrapper class, the real implementation of the code is singenerator, in this tired, we can see a lot of encryption details.
The following is the code implementation of Singenerator
/* Copyright (C) Gujicheng * * Licensed under the GPL License Version 2.0; * You are not a use this file except in compliance with the License. * * If you had any question, please contact me. * ************************************************************************* * * Author information * * ************************************************************************* * Email: [EMAIL&NB sp;protected] * * * * qq:29600731 * * * * weibo:http://weibo.com/gujicheng197 * * *************************************** */package Com.libra.sinvoice;import com.libra.sinvoice.buffer.bufferdata;/** * * @ClassName: Com.libra.sinvoice.SinGenerator * @Description: Sine wave Generator * @author Zhaokaiqiang * @date 2014-11-15 2:51:34 * */public class Singenerator {private static final String TAG = "SingeneraTor ";p rivate static final int state_start = 1;private static final int state_stop = 2;//2^8 when peak public static final int BI Ts_8 = 128;//defaults to 2^16 when the peak public static final int bits_16 = 32768;//sample rate public static final int sample_rate_8 = 8000;public static final int sample_rate_11 = 11250;public static final int sample_rate_16 = 16000;public static final int unit_accur Acy_1 = 4;public static final int unit_accuracy_2 = 8;private int mstate;private int msamplerate;private int mbits;private static final int default_bits = bits_8;private static final int default_sample_rate = sample_rate_8;private static final int default_buffer_size = 1024;private int mfilledsize;private int mbuffersize;private singeneratorcallback Singeneratorcallback;public static interface Singeneratorcallback {Bufferdata getgenbuffer (); void Freegenbuffer ( Bufferdata buffer);} Public Singenerator (Singeneratorcallback callback) {This (callback, Default_sample_rate, Default_bits, Default_buffer _size);} Public Singenerator (SinGenEratorcallback callback, int samplerate,int bits, int buffersize) {singeneratorcallback = Callback;mbuffersize = BufferSi Ze;msamplerate = Samplerate;mbits = Bits;mfilledsize = 0;mstate = State_stop;} public void Stop () {if (State_start = = mstate) {mstate = State_stop;}} public void Start () {if (state_stop = = mstate) {mstate = State_start;}} /** * Encode numbers * * @param genrate * @param duration */public void gen (int genrate, int duration) {if (State_start = = Mstat e) {//fixed value 16384int n = Mbits/2;int totalcount = (Duration * msamplerate)/1000;double per = (Genrate/(double) Msampler ATE) * 2 * math.pi;double d = 0; LOGHELPER.D (TAG, "per:" + per + "___genrate:" + genrate); if (null! = Singeneratorcallback) {mfilledsize = 0;//gets the data to encode BUF Ferdata bufferdata = Singeneratorcallback.getgenbuffer (); if (null! = Bufferdata) {for (int i = 0; i < totalcount; ++i) {if (State_start = = mstate) {//calculates the sine of a different point int out = (int) (Math.sin (d) * N) + 128;//Assuming that the number of fills exceeds the size of the buffer, reset mfilledsize, Release Buffe Rdataif (MfilledsiZe >= mBufferSize-1) {//Free bufferbufferdata.setfilledsize (mfilledsize); Singeneratorcallback.freegenbuffer ( Bufferdata); mfilledsize = 0;bufferdata = Singeneratorcallback.getgenbuffer (); if (null = = Bufferdata) {LOGHELPER.D (TAG, "Get null buffer"); break;}} Transcode to byte type and save. & 0xFF is to prevent negative conversions from appearing abnormally bufferdata.bytedata[mfilledsize++] = (byte) (Out & 0xff); if (bits_16 = = mBits) { bufferdata.bytedata[mfilledsize++] = (byte) ((out >> 8) & 0xff);} D + = per;} else {LOGHELPER.D (TAG, "Sin gen Force Stop"); else {LOGHELPER.D (TAG, "get null buffer");} if (null! = Bufferdata) {bufferdata.setfilledsize (mfilledsize); Singeneratorcallback.freegenbuffer (bufferData);} mfilledsize = 0;}}}
The most basic method is Gen (), and we analyze this method in detail.
1int n = mBits /2; The n defined here. In the following code to participate in the operation, n refers to the sine function we want to create the peak, is the highest point value, the value of Mbits is 2^16/2=32768, here the peak divided by two, it should be for the recognition rate, because when the n is directly assigned to Mbits, the sound is more sharp, The recognition rate is much reduced, so the MBITS/2 peak is chosen here.
2.int totalcount = (Duration * msamplerate)/1000; This is the number of times to cycle, due to the duration=100, so the total number of samples is 4410, Cycle Run 4,410 times
3.Double per = (Genrate/(double) msamplerate) * 2 * Math. PI; This per-parameter is used to record the distance in the process of the loop, each step forward, and this is related to the frequency.
Let us take the issue of Figure 5 for example. From the Encoder class. We know that 5 of the corresponding frequency is 2940HZ, if we want to play 100ms sound, then we need to shake 294 times. That is, 294 sine cycles.
And that's 294 times. According to the frequency of 44.1KHZ. That is, the frequency of 100ms sample 4,410 times. can be calculated in each cycle, the need to sample 4410/294=15, so a cycle of sample quantity is 15 times. And the length of a sine cycle is 2PI. So, use 2pi/15=0.4186. This value is the per value here.
4.int out = (int) (Math.sin (d) * N) + 128; After the calculation of per. Using the variable d in the loop to accumulate each per, and then use the previous calculation formula, you can calculate the corresponding function at the sample point, after the following operation, the implementation of the number of the code
Transcode to byte type and save. & 0xFF is to prevent negative conversions from appearing abnormally
Bufferdata. Bytedata [mfilledsize+ +] = (byte) (Out & 0xff);
if (bits_16 = = mBits) {
Bufferdata. Bytedata [mfilledsize+ +] = (byte) ((out >> 8) & 0xff);
}
We see that when we save the code, we use the Buffre class, which is the class that stores the byte data. Used to store byte data after the encoding is complete. Here's a quick look at the code for this class
/* Copyright (C) Gujicheng * * Licensed under the GPL License Version 2.0; * You are not a use this file except in compliance with the License. * * If you had any question, please contact me. * ************************************************************************* * * Author information * * ************************************************************************* * Email: [EMAIL&NB sp;protected] * * * * qq:29600731 * * * * weibo:http://weibo.com/gujicheng197 * * *************************************** */package Com.libra.sinvoice;import Java.util.concurrent.blockingqueue;import java.util.concurrent.linkedblockingqueue;/** * * @ClassName: Com.libra.sinvoice.Buffer * @Description: Buffer * @author Zha Okaiqiang * @date 2014-11-15 PM 1:35:46 * */public class Buffer {private final static String TAG = "Buffer";//producer Queue private blockingqueue<bufferdata> mproducerqueue;//consumer queue private blockingqueue<bufferdata> mconsumequeue;//buffer number private int mbuffercount;//buffer volume private int mbuffersize;public Buffer () {This (Common.default_buffer_count, common.default_buffer_size);} Public Buffer (int bufferCount, int buffersize) {mbuffersize = Buffersize;mbuffercount = Buffercount;mproducerqueue = new L Inkedblockingqueue<bufferdata> (Mbuffercount);//We want to put the end buffer, so need to add 1mConsumeQueue = new L Inkedblockingqueue<bufferdata> (Mbuffercount + 1);//Initialize producer queue for (int i = 0; i < Mbuffercount; ++i) {try {Mproduce Rqueue.put (New Bufferdata (Mbuffersize));} catch (Interruptedexception e) {e.printstacktrace ();}}} public void Reset () {//rejects the producer's short node int size = Mproducerqueue.size (); for (int i = 0; i < size; ++i) {Bufferdata data = m Producerqueue.peek (); if (null = = Data | | null = = data.bytedata) {Mproducerqueue.poll ()}} Increase the non-empty data in the consumer to the producer where size= Mconsumequeue.size (); for (int i = 0; i < size; ++i) {Bufferdata data = Mconsumequeue.poll (); if (null! = Data &&am P Null! = Data.bytedata) {mproducerqueue.add (data);}} LOGHELPER.D (TAG, "Reset producerqueue Size:" + mproducerqueue.size () + "Consumequeue size:" + mconsumequeue.size ());} Final public int Getemptycount () {return mproducerqueue.size ();} Final public int Getfullcount () {return mconsumequeue.size ();} Get the producer's head node, plug-in public bufferdata Getempty () {return Getimpl (mproducerqueue);} Add to producer public Boolean putempty (Bufferdata data) {return Putimpl (data, mproducerqueue);} Get the consumer's head node public bufferdata Getfull () {return Getimpl (mconsumequeue);} Added to consumer public boolean putfull (Bufferdata data) {return Putimpl (data, mconsumequeue);} Gets the header node of the queue private Bufferdata Getimpl (blockingqueue<bufferdata> queue) {if (null! = Queue) {try {return Queue.take ( );} catch (Interruptedexception e) {e.printstacktrace ();}} return null;} Add data to the queue private Boolean Putimpl (Bufferdata data, BlockIngqueue<bufferdata> queue) {if (null! = Queue && null! = data) {try {queue.put (data); return true;} catch (I Nterruptedexception e) {e.printstacktrace ();}} return false;} When Mdata was null, means it is end of inputpublic static class Bufferdata {//Data container public byte bytedata[];//fill volume privat e int mfilledsize;//Buffer maximum volume private int mmaxbuffersize;//static empty buffer private static bufferdata Semptybuffer = new Bufferdata (0); Public bufferdata (int maxBufferSize) {mmaxbuffersize = Maxbuffersize;mfilledsize = 0;if (maxBufferSize > 0) {bytedata = New Byte[mmaxbuffersize];} else {bytedata = null;}} /** * Gets the empty buffer * * @return */public static Bufferdata Getemptybuffer () {return semptybuffer;} Reset fill count final public void Reset () {mfilledsize = 0;} Final public int getmaxbuffersize () {return mmaxbuffersize;} Set number of fills final public void setfilledsize (int size) {mfilledsize = size;} Final public int getfilledsize () {return mfilledsize;}}}
Buffer uses two queues to implement the producer consumer model, which guarantees compilation good one. Play one, in the Sinvoiceplayer class inside open two threads, respectively, two queues inside the data management.
The Demohttps://github.com/zhaokaiqiang/sinvoicedemo of this project
"Android Development experience" Implementation of "Sonic Communication/verification" for mobile devices--sinvoice Open Source project Introduction (III)