Reprint Please specify Source: http://blog.csdn.net/zhaokaiqiang1992
The first two introduces the principle of sonic verification/communication and the implementation of sound playback, this article will introduce the most important, but also the most difficult to understand, is how the sinvoice of these numbers are encoded transmission.
Since the source code has a large number of difficult to distinguish callback functions, for easy reading, I did some of the renaming and code collation, 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, and then to parse. In Sinvoiceplayer, the playback operation is performed by invoking the start () method of Pcmplayer, while in Pcmplayer the audiotrace is called to achieve the final audio playback function. Through a layer of encapsulation of the audiotrace, a simple call to the Sinvoiceplayer is finally realized.
Since Audiotrace is the ultimate class for audio playback, 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 the Singenerator class, the interface is the main completion of the data acquisition and release, in the following code I will explain
2. The numbers stored in the array code_frequency represent frequencies corresponding to 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 number of samples of the sine wave corresponding to the frequency in one cycle, the 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 duration of the audio, 100 for 100 milliseconds, that is, 0.1 seconds. We have said that the default sample rate is 44.1KHZ, which is 44,100 samples in 1s, if you need to play 100 milliseconds, then you only 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, then we know that 0 corresponds to the vibration frequency is 1422HZ, this is a second, if it is 100ms? Is 142.2HZ, we use, 142.2x31=4408.2, nearly 4,410 times, so we can see the frequency of the audio we want to generate, we know how many times each cycle needs to be sampled, we can calculate the sampling interval. Because encoder is just a wrapper class, the real implementation of the code is singenerator, in this tired inside, we can see a lot of encryption details.
Here 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;//default is 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;//If 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;}} transcoding to byte type and saving,& 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 Main method is Gen (), which we analyze in detail.
1int n = mBits /2; In this definition of N, in the following code to participate in the operation, n refers to the peak of the sine function we are creating, is the value of the highest point, the value of Mbits is 2^16/2=32768, where the peak is divided by two, it should be considered for the recognition rate, because when n is directly assigned to Mbits, The sound is more sharp, the recognition rate is much lower, so the MBITS/2 peak is chosen here.
2.int totalcount = (Duration * msamplerate)/1000; This is the number of times to loop, because duration=100, so the total number of samples is 4410, Loop Execution 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. We take the number 5 as an example, from the Encoder class, we know that 5 corresponds to the frequency of 2940HZ, if we want to play 100ms sound, then we need to shake 294 times, that is, 294 sine cycle. And these 294 times, according to the frequency of 44.1KHZ, that is 100ms sampling frequency of 4,410 times, it can be calculated in each cycle, need to sample 4410/294=15, so a period of sampling quantity is 15 times, and a sine period of length is 2PI, so, using 2pi/ 15=0.4186, this value is the per value here.
4.int out = (int) (Math.sin (d) * N) + 128; After calculating per, use variable d in the loop to accumulate each per, and then use the previous calculation formula, you can calculate the corresponding function at the sampling point value, after the completion of the following operation, the implementation of the number of the code
transcoding to byte type and saving,& 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, which is used to save the byte data after the completion of the encoding, so let's take a look at the code of 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 ()}} Add non-empty data from the consumer to the producer 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 ();} Gets the head node of the producer, the blocking 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);} Add to the 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 the compilation good one, plays one, two threads are opened in the Sinvoiceplayer class, respectively, the data within the two queues is managed.
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)