├── .gitignore
├── .settings
└── org.eclipse.jdt.core.prefs
├── AndroidManifest.xml
├── LICENSE
├── README.md
├── arduino
└── sonic2.ino
├── libs
└── android-support-v4.jar
├── proguard-project.txt
├── project.properties
├── res
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xhdpi
│ └── ic_launcher.png
├── layout
│ └── activity_main.xml
├── menu
│ └── activity_main.xml
├── values-v11
│ └── styles.xml
├── values-v14
│ └── styles.xml
└── values
│ ├── strings.xml
│ └── styles.xml
└── src
└── com
└── theultimatelabs
└── sonic
├── SonicActivity.java
├── SonicService.java
└── log.java
/.gitignore:
--------------------------------------------------------------------------------
1 | # built application files
2 | *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # generated files
12 | bin/
13 | gen/
14 |
15 | # Local configuration file (sdk path, etc)
16 | local.properties
17 |
18 | # Eclipse project files
19 | .classpath
20 | .project
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Intellij project files
26 | *.iml
27 | *.ipr
28 | *.iws
29 | .idea/
30 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
3 | org.eclipse.jdt.core.compiler.compliance=1.6
4 | org.eclipse.jdt.core.compiler.source=1.6
5 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
23 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | AndroidToArduinoSonicComms
2 | ==========================
3 |
4 | Two way serial communication between Android and Arduino using high pitched sound.
5 |
--------------------------------------------------------------------------------
/arduino/sonic2.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 |
5 | const int OUT_TONES[] = {
6 | //16700, 18100, 17400 };
7 | 20231, 21231 };
8 | const int IN_TONES[] = {
9 | //16700, 18100, 17400 };
10 | 19231, 20765 };
11 |
12 | boolean ready = 0;
13 | //sizeof(IN_TONES)/sizeof(int);
14 | #define ADC_CENTER 127
15 | #define WINDOW_SIZE 128
16 | #define BIT_PERIOD 8
17 | #define BIT_PERIOD_2 16
18 | #define BIT_PERIOD_5_2 20
19 | #define BIT_PERIOD_3_2 12
20 | #define BIT_PERIOD_1_2 4
21 | #define MSG_LENGTH 3
22 | #define NUM_BYTES 5
23 | #define NUM_MSGS 5
24 | #define THRESHOLD 25000
25 |
26 | const unsigned int ON_THRESHOLD = 50000;
27 | const unsigned int OFF_THRESHOLD = ON_THRESHOLD/2;
28 |
29 | enum {
30 | IDLE,START,HIGH_PULSE,LOW_PULSE,DONE}
31 | state = IDLE;
32 |
33 | const float SAMPLE_RATE = 76923.0769;//38461.5385 //8928.57143
34 | const float STAMP_MS = (float)WINDOW_SIZE/(SAMPLE_RATE/1000);
35 |
36 | byte bytei = 0;
37 | byte biti = 0;
38 | byte lastSmpl = 0;
39 | byte bankIn = 0;
40 | byte bankProc = 0;
41 |
42 | unsigned short samplei=0;
43 | unsigned long stamp=0;
44 | unsigned long lastStamp=0;
45 |
46 | struct msg_t {
47 | byte bytes[NUM_BYTES];
48 | boolean ready;
49 | byte len;
50 | };
51 |
52 | struct msg_t msgs[NUM_MSGS];
53 |
54 | int Q1,Q2;
55 |
56 | static const uint8_t PROGMEM crc8_table[] = {
57 | 0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
58 | 157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220,
59 | 35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98,
60 | 190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
61 | 70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7,
62 | 219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154,
63 | 101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36,
64 | 248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185,
65 | 140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
66 | 17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
67 | 175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
68 | 50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
69 | 202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139,
70 | 87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
71 | 233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
72 | 116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53};
73 |
74 | static const uint8_t PROGMEM crc4_table[] = {
75 | 5, 11, 25, 23, 26, 20, 6, 8, 28, 18, 0, 14, 3, 13, 31, 17, 16, 30, 12, 2, 15, 1, 19, 29, 9, 7, 21, 27, 22, 24, 10, 4, 8, 6, 20, 26, 23, 25, 11, 5, 17, 31, 13,
76 | 3, 14, 0, 18, 28, 29, 19, 1, 15, 2, 12, 30, 16, 4, 10, 24, 22, 27, 21, 7, 9, 31, 17, 3, 13, 0, 14, 28, 18, 6, 8, 26, 20, 25, 23, 5, 11, 10, 4, 22, 24, 21, 27, 9
77 | , 7, 19, 29, 15, 1, 12, 2, 16, 30, 18, 28, 14, 0, 13, 3, 17, 31, 11, 5, 23, 25, 20, 26, 8, 6, 7, 9, 27, 21, 24, 22, 4, 10, 30, 16, 2, 12, 1, 15, 29, 19, 22, 24,
78 | 10, 4, 9, 7, 21, 27, 15, 1, 19, 29, 16, 30, 12, 2, 3, 13, 31, 17, 28, 18, 0, 14, 26, 20, 6, 8, 5, 11, 25, 23, 27, 21, 7, 9, 4, 10, 24, 22, 2, 12, 30, 16, 29, 1,
79 | 9, 1, 15, 14, 0, 18, 28, 17, 31, 13, 3, 23, 25, 11, 5, 8, 6, 20, 26, 12, 2, 16, 30, 19, 29, 15, 1, 21, 27, 9, 7, 10, 4, 22, 24, 25, 23, 5, 11, 6, 8, 26, 20, 0,
80 | 14, 28, 18, 31, 17, 3, 13, 1, 15, 29, 19, 30, 16, 2, 12, 24, 22, 4, 10, 7, 9, 27, 21, 20, 26, 8, 6, 11, 5, 23, 25, 13, 3, 17, 31, 18, 28, 14, 0};
81 |
82 | uint8_t crc8(const uint8_t *addr, uint8_t len)
83 | {
84 | uint8_t crc = 0xff;
85 | while (len--) {
86 | crc = pgm_read_byte(crc8_table + (crc ^ *addr++));
87 | }
88 | return crc;
89 | }
90 |
91 | /* table of Hamming codes hammingCodes[x] is the x encoded */
92 | static const uint8_t PROGMEM hammingEncode[16] =
93 | {
94 | 0x00, /* 0 */
95 | 0x71, /* 1 */
96 | 0x62, /* 2 */
97 | 0x13, /* 3 */
98 | 0x54, /* 4 */
99 | 0x25, /* 5 */
100 | 0x36, /* 6 */
101 | 0x47, /* 7 */
102 | 0x38, /* 8 */
103 | 0x49, /* 9 */
104 | 0x5A, /* A */
105 | 0x2B, /* B */
106 | 0x6C, /* C */
107 | 0x1D, /* D */
108 | 0x0E, /* E */
109 | 0x7F /* F */
110 | };
111 |
112 | /* table convering encoded value (with error) to original data */
113 | /* hammingDecodeValues[code] = original data */
114 | static const uint8_t PROGMEM hammingDecode[128] =
115 | {
116 | 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x0E, 0x07, /* 0x00 to 0x07 */
117 | 0x00, 0x09, 0x0E, 0x0B, 0x0E, 0x0D, 0x0E, 0x0E, /* 0x08 to 0x0F */
118 | 0x00, 0x03, 0x03, 0x03, 0x04, 0x0D, 0x06, 0x03, /* 0x10 to 0x17 */
119 | 0x08, 0x0D, 0x0A, 0x03, 0x0D, 0x0D, 0x0E, 0x0D, /* 0x18 to 0x1F */
120 | 0x00, 0x05, 0x02, 0x0B, 0x05, 0x05, 0x06, 0x05, /* 0x20 to 0x27 */
121 | 0x08, 0x0B, 0x0B, 0x0B, 0x0C, 0x05, 0x0E, 0x0B, /* 0x28 to 0x2F */
122 | 0x08, 0x01, 0x06, 0x03, 0x06, 0x05, 0x06, 0x06, /* 0x30 to 0x37 */
123 | 0x08, 0x08, 0x08, 0x0B, 0x08, 0x0D, 0x06, 0x0F, /* 0x38 to 0x3F */
124 | 0x00, 0x09, 0x02, 0x07, 0x04, 0x07, 0x07, 0x07, /* 0x40 to 0x47 */
125 | 0x09, 0x09, 0x0A, 0x09, 0x0C, 0x09, 0x0E, 0x07, /* 0x48 to 0x4F */
126 | 0x04, 0x01, 0x0A, 0x03, 0x04, 0x04, 0x04, 0x07, /* 0x50 to 0x57 */
127 | 0x0A, 0x09, 0x0A, 0x0A, 0x04, 0x0D, 0x0A, 0x0F, /* 0x58 to 0x5F */
128 | 0x02, 0x01, 0x02, 0x02, 0x0C, 0x05, 0x02, 0x07, /* 0x60 to 0x67 */
129 | 0x0C, 0x09, 0x02, 0x0B, 0x0C, 0x0C, 0x0C, 0x0F, /* 0x68 to 0x6F */
130 | 0x01, 0x01, 0x02, 0x01, 0x04, 0x01, 0x06, 0x0F, /* 0x70 to 0x77 */
131 | 0x08, 0x01, 0x0A, 0x0F, 0x0C, 0x0F, 0x0F, 0x0F /* 0x78 to 0x7F */
132 | };
133 |
134 | boolean processBytes();
135 |
136 | void setup() {
137 | Serial.begin(115200);
138 | Serial.println("Setup");
139 | Serial.println(STAMP_MS);
140 | int x = -10;
141 | Serial.println(x>>2);
142 | x = 10;
143 | Serial.println(x>>2);
144 | Serial.println(sizeof(byte));
145 | Serial.println(sizeof(short));
146 | Serial.println(sizeof(int));
147 | Serial.println(sizeof(long));
148 | initADC();
149 |
150 | /*for(int i=0; i<1000j; i++) {
151 | for(int f=0; f<10; f++) {
152 | toneAC(OUT_TONES[0] + ((OUT_TONES[1]-OUT_TONES[0])*f)/10);
153 | delay(1);
154 | }
155 | for(int f=0; f<10; f++) {
156 | toneAC(OUT_TONES[1] - ((OUT_TONES[1]-OUT_TONES[0])*f)/10);
157 | delay(1);
158 | }
159 | }*/
160 |
161 |
162 | byte msg[] = {
163 | (byte)0x00, (byte)0xff, (byte) 0x55};
164 | while(0){
165 | FMSimpleSend(msg,sizeof(msg));
166 | delay(2000);
167 | }
168 | }
169 |
170 | int tcount = 0;
171 | void loop()
172 | {
173 | if(bankProc != bankIn) {
174 | if(msgs[bankProc].ready) {
175 | //if(validate(msgs[bankProc])) {
176 |
177 | //}
178 | if(msgs[bankProc].len>=1) {
179 | msgs[bankProc].bytes[0] ^= 0x20;
180 | delay(200);
181 | FMSimpleSend(msgs[bankProc].bytes,1);
182 | }
183 | bankProc = (bankProc+1)%NUM_MSGS;
184 | }
185 | msgs[bankProc].ready = false;
186 | }
187 | }
188 | enum TYPE {
189 | RESERVED_TYPE,LIGHT1,LIGHT2,ALPHA}
190 | ;
191 | enum ACTION {
192 | RESERVED_ACTION,ON,OFF,TOGGLE,SET,GET}
193 | ;
194 |
195 | boolean validate(struct msg_t msg) {
196 | if(msg.len <= 1) return false;
197 | if(crc8(msg.bytes,msg.len-1) == msg.bytes[msg.len-1]) return true;
198 | else return false;
199 | }
200 |
201 | #if 0
202 | boolean processBytes(struct msg_t msg) {
203 |
204 | TYPE type = (TYPE)(msg.bytes[0]>>4);
205 | ACTION action = (ACTION)(msg.bytes[0]&0xF);
206 |
207 | switch(action) {
208 | case ON:
209 | break;
210 | case OFF:
211 |
212 | break;
213 | case TOGGLE:
214 | if(type==ALPHA && msg.len == 3) {
215 | msg.bytes[1] ^= 0x20;
216 | delay(200);
217 | FMSimpleSend(msg.bytes[1] ^ 0x20,1);
218 | }
219 | break;
220 | case SET:
221 | break;
222 | case GET:
223 | break;
224 | case TEST:
225 | break;
226 | /*case TOGGLE_RELAY2:
227 | case ON_RELAY1:
228 | case ON_RELAY2:
229 | case OFF_RELAY1:
230 | case OFF_RELAY2:
231 | case SET_RELAY1:
232 | case SET_RELAY2:
233 | case GET_RELAY1:
234 | case GET_RELAY2:
235 |
236 | case GET_TEMP:
237 | case GET_HUMID:
238 | case GET_GAS:
239 | case GET_CURRENT:
240 |
241 | case SET_TEMP:
242 | case SET_HUMID:
243 | case SET_GAS:
244 | case SET_*/
245 |
246 | }
247 | bytej = (bytej+1)%BYTES_LEN;
248 | }
249 | #endif
250 |
251 | void initADC() {
252 | cli();//diable interrupts
253 |
254 | //set up continuous sampling of analog pin 0
255 |
256 | //clear ADCSRA and ADCSRB registers
257 | ADCSRA = 0;
258 | ADCSRB = 0;
259 |
260 | ADMUX |= (1 << REFS0); //set reference voltage
261 | ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only
262 |
263 | ADCSRA |= (1 << ADPS2) ;//| (1 << ADPS0); //set ADC clock with 32 prescaler- 16mHz/32=500kHz
264 | ADCSRA |= (1 << ADATE); //enabble auto trigger
265 | enableADCInterrupt();//ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
266 | ADCSRA |= (1 << ADEN); //enable ADC
267 | ADCSRA |= (1 << ADSC); //start ADC measurements
268 |
269 | sei();//enable interrupts
270 | }
271 |
272 | void enableADCInterrupt() {
273 | ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
274 | }
275 | void disableADCInterrupt() {
276 | ADCSRA &= ~(1 << ADIE); //enable interrupts when measurement complete
277 | }
278 |
279 |
280 | ISR(ADC_vect) {
281 | int Q0 = -Q2 + ((int)ADCH-ADC_CENTER);
282 | Q2 = Q1;
283 | Q1 = Q0;
284 |
285 | samplei++;
286 | if(samplei == WINDOW_SIZE) {
287 | samplei = 0;
288 | unsigned long mag = (unsigned long)((long)Q1)*Q1+(unsigned long)((long)Q2)*Q2;
289 | Q1=Q2=0;
290 |
291 | if(mag > ON_THRESHOLD) {
292 | FMSimpleRecv(1);
293 | }
294 | else if (mag < OFF_THRESHOLD) {
295 | FMSimpleRecv(0);
296 | }
297 | }
298 | }
299 |
300 | byte block;
301 | byte *bytes;
302 | void FMSimpleRecv(byte smpl) {
303 | byte newBit;
304 | unsigned long stamp = millis();
305 | unsigned int diff = stamp - lastStamp;
306 |
307 | if(state != IDLE && diff > BIT_PERIOD*4) {
308 | state = IDLE;
309 | if(bytei>0) {
310 | msgs[bankIn].len = bytei;
311 | msgs[bankIn].ready = true;
312 | Serial.print("Block ready:");
313 | Serial.print(bankIn);
314 | Serial.print(" ");
315 | Serial.println(bytei);
316 |
317 | bankIn = (bankIn + 1)%NUM_MSGS;
318 | if(bankIn == bankProc || msgs[bankIn].ready==true) {
319 | Serial.println("TOO SLOW!");
320 | }
321 | msgs[bankIn].ready = false;
322 | }
323 | bytei = block = biti = 0;
324 | }
325 |
326 | if(lastSmpl!=smpl){
327 | lastSmpl = smpl;
328 | //unsigned long stamp = millis();
329 | //Serial.print(smpl);
330 | //Serial.print(" ");
331 | //Serial.println(diff);
332 | //Serial.println(diff);
333 | switch(state) {
334 | case IDLE:
335 | if(smpl == 1) {
336 | lastStamp = stamp;
337 | state = HIGH_PULSE;
338 | }
339 | break;
340 | case LOW_PULSE:
341 | //newBit = 0;
342 | if(diff > BIT_PERIOD_2) {
343 | //newBit = 1;
344 | block |= 1<> b) & 0x1)) delay(BIT_PERIOD);
389 | }
390 | }
391 |
392 | for(int f=INC_SIZE; f<=BIT_PERIOD; f+=INC_SIZE) {
393 | toneAC(OUT_TONES[1] - ((OUT_TONES[1]-OUT_TONES[0])*f)/BIT_PERIOD);
394 | delay(INC_SIZE);
395 | }
396 | delay(BIT_PERIOD);
397 | noToneAC();
398 |
399 | enableADCInterrupt();
400 |
401 | }
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rbbrns/AndroidToArduinoSonicComms/b42815b0ce6ff00e7c96e88b58ae7165604034f1/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-17
15 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rbbrns/AndroidToArduinoSonicComms/b42815b0ce6ff00e7c96e88b58ae7165604034f1/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rbbrns/AndroidToArduinoSonicComms/b42815b0ce6ff00e7c96e88b58ae7165604034f1/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rbbrns/AndroidToArduinoSonicComms/b42815b0ce6ff00e7c96e88b58ae7165604034f1/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
14 |
15 |
16 |
17 |
18 |
27 |
28 |
37 |
38 |
47 |
48 |
--------------------------------------------------------------------------------
/res/menu/activity_main.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | theUltimateSonic
5 | Settings
6 | Hello world!
7 |
8 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/src/com/theultimatelabs/sonic/SonicActivity.java:
--------------------------------------------------------------------------------
1 | package com.theultimatelabs.sonic;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.hardware.Camera.Size;
6 | import android.media.AudioFormat;
7 | import android.media.AudioManager;
8 | import android.media.AudioRecord;
9 | import android.media.AudioTrack;
10 | import android.media.MediaRecorder.AudioSource;
11 | import android.os.AsyncTask;
12 | import android.os.Bundle;
13 | import android.text.Editable;
14 | import android.text.TextWatcher;
15 | import android.view.Menu;
16 | import android.view.View;
17 | import android.view.View.OnClickListener;
18 | import android.widget.Button;
19 | import android.widget.EditText;
20 | import android.widget.TextView;
21 |
22 | public class SonicActivity extends Activity {
23 |
24 | // private AudioTrack track;
25 |
26 | private int sampleRate;
27 |
28 | private int minBufSize;
29 |
30 | private double period;
31 |
32 | private int samples;
33 |
34 | final int SAMPLE_RATE = 44100;
35 |
36 | private TextView textView;
37 |
38 | private EditText editText;
39 |
40 | final double IN_TONES[] = { 20231, 18000 };// { 16700, 18100,
41 | // 17400 };
42 | final double OUT_TONES[] = { 19231, 18000 };// { 16700, 18100,
43 | // 17400 };
44 | final double OUT_DIFF = (OUT_TONES[1] - OUT_TONES[0]);
45 |
46 | final double SAMPLES_MS = (SAMPLE_RATE / 1000);
47 |
48 | public final int STREAM = AudioManager.STREAM_ALARM;
49 |
50 | // private AudioTrack track;
51 |
52 | @Override
53 | protected void onCreate(Bundle savedInstanceState) {
54 | super.onCreate(savedInstanceState);
55 | setContentView(R.layout.activity_main);
56 |
57 | this.period = 1.0;
58 | this.sampleRate = 44100; // AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);
59 | log.v("SampleRate: %d", this.sampleRate);
60 |
61 | this.setVolumeControlStream(this.STREAM);
62 |
63 | this.minBufSize = AudioTrack.getMinBufferSize(this.sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
64 | log.v("minBufsize=%d minPeriod=%f", this.minBufSize, ((float) this.minBufSize) / this.sampleRate);
65 |
66 | this.samples = (int) Math.max(this.period * this.sampleRate, this.minBufSize);
67 |
68 | /*
69 | * this.track = new AudioTrack(this.STREAM, this.sampleRate,
70 | * AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT,
71 | * this.samples* 2, AudioTrack.MODE_STATIC);
72 | */
73 |
74 | new AudioInTask().execute();
75 |
76 | textView = (TextView) findViewById(R.id.textView1);
77 | textView.setText("");
78 | editText = (EditText) findViewById(R.id.editText1);
79 | editText.addTextChangedListener(new TextWatcher() {
80 | public void afterTextChanged(Editable s) {
81 | if(s.length() == 0) return;
82 | char l = s.subSequence(s.length() - 1, s.length()).charAt(0);
83 | byte[] msg = new byte[] { (byte) l };
84 | FMSimpleSend(msg);// (byte) l, (byte) l, (byte) l, (byte) l);
85 | log.i("send done");
86 | }
87 |
88 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
89 | }
90 |
91 | public void onTextChanged(CharSequence s, int start, int before, int count) {
92 | }
93 | });
94 |
95 | ((Button) findViewById(R.id.button1)).setOnClickListener(new OnClickListener() {
96 |
97 | @Override
98 | public void onClick(View v) {
99 | // sineWave(100,size);
100 | // sineWave(200,size);
101 | // DTMF((byte)0xA4);
102 | // DTMFTest(50);
103 | byte[] msg = new byte[] { 0x55 };
104 | FMSimpleSend(msg);
105 | textView.setText("");
106 | editText.setText("");
107 | // 0x78);
108 | // FMTest();
109 | // sleep(3);
110 | // FMSend((byte) 0x9A, (byte) 0xBC, (byte) 0xDE, (byte) 0xF0);
111 | // sleep(3);
112 | // FMSend((byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78);
113 | // sleep(3);
114 | // sineWaveTest2();
115 | // sineWave(400,size);
116 | // sineWave(500,size);
117 | // sineWave(600,size);
118 | }
119 | });
120 | }
121 |
122 | @Override
123 | public boolean onCreateOptionsMenu(Menu menu) {
124 | // Inflate the menu; this adds items to the action bar if it is present.
125 | getMenuInflater().inflate(R.menu.activity_main, menu);
126 | return true;
127 | }
128 |
129 | public void setVolume(int volume) {
130 | AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
131 | int maxVolume = am.getStreamMaxVolume(this.STREAM);
132 | am.setStreamVolume(this.STREAM, (int) (maxVolume * ((float) volume / 100)), 0);
133 | }
134 |
135 | private void sleep(int period) {
136 | try {
137 | Thread.sleep(period);
138 | } catch (InterruptedException e) {
139 | // TODO Auto-generated catch block
140 | e.printStackTrace();
141 | }
142 | }
143 |
144 | Runnable audioOutRunnable = new Runnable() {
145 | @Override
146 | public void run() {
147 | // TODO Auto-generated method stub
148 |
149 | }
150 |
151 | };
152 |
153 | /* table of Hamming codes hammingCodes[x] is the x encoded */
154 | final byte hammingEncode[] = { 0x00, /* 0 */
155 | 0x71, /* 1 */
156 | 0x62, /* 2 */
157 | 0x13, /* 3 */
158 | 0x54, /* 4 */
159 | 0x25, /* 5 */
160 | 0x36, /* 6 */
161 | 0x47, /* 7 */
162 | 0x38, /* 8 */
163 | 0x49, /* 9 */
164 | 0x5A, /* A */
165 | 0x2B, /* B */
166 | 0x6C, /* C */
167 | 0x1D, /* D */
168 | 0x0E, /* E */
169 | 0x7F /* F */
170 | };
171 |
172 | static final int crc_table[] = { 0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65, 157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227,
173 | 189, 62, 96, 130, 220, 35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192,
174 | 158, 29, 67, 161, 255, 70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, 219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165,
175 | 251, 120, 38, 196, 154, 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36, 248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134,
176 | 216, 91, 5, 231, 185, 140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205, 17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111,
177 | 49, 178, 236, 14, 80, 175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76,
178 | 18, 145, 207, 45, 115, 202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139, 87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41,
179 | 119, 244, 170, 72, 22, 233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168, 116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10,
180 | 84, 215, 137, 107, 53 };
181 |
182 | byte crc8(byte msg[]) {
183 | byte crc = 0;
184 | for (byte b : msg) {
185 | crc = (byte) crc_table[(crc ^ b)];
186 | }
187 | return crc;
188 | }
189 |
190 | void FMTest() {
191 | short buffer[] = new short[SAMPLE_RATE * 4];
192 | final float SAMPLES_MS = (SAMPLE_RATE / 1000);
193 | int bufi = 0;
194 | double angle = 0;
195 |
196 | for (int s = 0; s < 1000 * SAMPLES_MS; s++) {
197 | angle += 2 * Math.PI * (OUT_TONES[0] / (double) SAMPLE_RATE);
198 | buffer[bufi++] = (short) (Math.sin(angle) * (Short.MAX_VALUE));
199 | }
200 |
201 | for (int f = 0; f < 100; f++) {
202 | for (int s = 0; s < 10 * SAMPLES_MS; s++) {
203 | if ((f % 2) == 0) {
204 | angle += 2 * Math.PI * (OUT_TONES[0] + (OUT_DIFF * s / (10 * SAMPLES_MS))) / SAMPLE_RATE;
205 | } else {
206 | angle += 2 * Math.PI * (OUT_TONES[1] - (OUT_DIFF * s / (10 * SAMPLES_MS))) / SAMPLE_RATE;
207 | }
208 |
209 | buffer[bufi++] = (short) (Math.sin(angle) * (Short.MAX_VALUE));
210 | }
211 | }
212 |
213 | for (int s = 0; s < 1000 * SAMPLES_MS; s++) {
214 | angle += 2 * Math.PI * (OUT_TONES[1] / (double) SAMPLE_RATE);
215 | buffer[bufi++] = (short) (Math.sin(angle) * (Short.MAX_VALUE));
216 | }
217 |
218 | AudioTrack track = new AudioTrack(this.STREAM, this.sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, bufi * 2,
219 | AudioTrack.MODE_STATIC);
220 | track.write(buffer, 0, bufi); // write to the audio buffer....
221 |
222 | // and start all over again!
223 | track.play();
224 |
225 | while (track.getPlaybackHeadPosition() < bufi) {
226 | sleep(1);
227 | }
228 | track.stop();
229 | // this.track.flush();
230 | track.release();
231 | }
232 |
233 | void FMSimpleSend(byte msg[]) {
234 | short buffer[] = new short[SAMPLE_RATE * 6];
235 |
236 | // byte crc = (byte) ((id + cmd + d1 + d2) & 0xff);
237 | // byte msg[] = { id, cmd, d1, d2, crc };
238 | final int BIT_PERIOD = 8;
239 | final int START_FADE = 1;
240 | final int STOP_FADE = 1;
241 | double angle = 0;
242 | int bufi = 0;
243 |
244 | for (int s = 0; s < START_FADE * SAMPLES_MS * BIT_PERIOD; s++) {
245 | angle += 2 * Math.PI * (OUT_TONES[1] / SAMPLE_RATE);
246 | buffer[bufi++] = (short) (Math.sin(angle) * (Short.MAX_VALUE * ((double) s / (START_FADE * SAMPLES_MS * BIT_PERIOD))));
247 | }
248 |
249 | for (byte m : msg) {
250 | for (int b = 0; b < 8; b++) {
251 | boolean bit = ((m >> b) & 0x1) != 0;
252 | int periodHigh = (BIT_PERIOD / 2);
253 | int periodLow = bit ? BIT_PERIOD * 2 : (BIT_PERIOD / 2);
254 |
255 | for (int s = 0; s < SAMPLES_MS * periodHigh; s++) {
256 | angle += 2 * Math.PI * (OUT_TONES[0] / SAMPLE_RATE);
257 | buffer[bufi++] = (short) (Math.sin(angle) * (Short.MAX_VALUE));
258 | }
259 | for (int s = 0; s < SAMPLES_MS * periodLow; s++) {
260 | angle += 2 * Math.PI * (OUT_TONES[1] / SAMPLE_RATE);
261 | buffer[bufi++] = (short) (Math.sin(angle) * (Short.MAX_VALUE));
262 | }
263 | }
264 | }
265 |
266 | for (int s = 0; s < STOP_FADE * SAMPLES_MS * BIT_PERIOD; s++) {
267 | angle += 2 * Math.PI * (OUT_TONES[0] / SAMPLE_RATE);
268 | buffer[bufi++] = (short) (Math.sin(angle) * (Short.MAX_VALUE * (1.0 - (double) s / (STOP_FADE * SAMPLES_MS * BIT_PERIOD))));
269 | }
270 |
271 | AudioTrack track = new AudioTrack(this.STREAM, this.sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, bufi * 2,
272 | AudioTrack.MODE_STATIC);
273 | track.write(buffer, 0, bufi); // write to the audio buffer....
274 |
275 | // and start all over again!
276 | track.play();
277 |
278 | while (track.getPlaybackHeadPosition() < bufi) {
279 | sleep(1);
280 | }
281 | track.stop();
282 | // this.track.flush();
283 | track.release();
284 |
285 | }
286 |
287 | private class AudioInTask extends AsyncTask {
288 |
289 | protected void onProgressUpdate(Character... progress) {
290 | log.i("Progress: %c",progress[0]);
291 | textView.append(String.valueOf(progress[0]));
292 | }
293 |
294 | protected void onPostExecute(Void... arg0) {
295 |
296 | // showDialog("Downloaded " + result + " bytes");
297 | }
298 |
299 | byte biti = 0;
300 | long stamp = 0;
301 |
302 | final int IDLE = 0;
303 | final int START = 1;
304 | final int HIGH_PULSE = 2;
305 | final int LOW_PULSE = 3;
306 | final int DONE = 4;
307 | int state = IDLE;
308 |
309 | /* table of Hamming codes hammingCodes[x] is the x encoded */
310 | final byte hammingEncode[] = { 0x00, /* 0 */
311 | 0x71, /* 1 */
312 | 0x62, /* 2 */
313 | 0x13, /* 3 */
314 | 0x54, /* 4 */
315 | 0x25, /* 5 */
316 | 0x36, /* 6 */
317 | 0x47, /* 7 */
318 | 0x38, /* 8 */
319 | 0x49, /* 9 */
320 | 0x5A, /* A */
321 | 0x2B, /* B */
322 | 0x6C, /* C */
323 | 0x1D, /* D */
324 | 0x0E, /* E */
325 | 0x7F /* F */
326 | };
327 |
328 | /* table convering encoded value (with error) to original data */
329 | /* hammingDecodeValues[code] = original data */
330 | final byte hammingDecode[] = { 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x0E, 0x07, /*
331 | * 0x00
332 | * to
333 | * 0x07
334 | */
335 | 0x00, 0x09, 0x0E, 0x0B, 0x0E, 0x0D, 0x0E, 0x0E, /* 0x08 to 0x0F */
336 | 0x00, 0x03, 0x03, 0x03, 0x04, 0x0D, 0x06, 0x03, /* 0x10 to 0x17 */
337 | 0x08, 0x0D, 0x0A, 0x03, 0x0D, 0x0D, 0x0E, 0x0D, /* 0x18 to 0x1F */
338 | 0x00, 0x05, 0x02, 0x0B, 0x05, 0x05, 0x06, 0x05, /* 0x20 to 0x27 */
339 | 0x08, 0x0B, 0x0B, 0x0B, 0x0C, 0x05, 0x0E, 0x0B, /* 0x28 to 0x2F */
340 | 0x08, 0x01, 0x06, 0x03, 0x06, 0x05, 0x06, 0x06, /* 0x30 to 0x37 */
341 | 0x08, 0x08, 0x08, 0x0B, 0x08, 0x0D, 0x06, 0x0F, /* 0x38 to 0x3F */
342 | 0x00, 0x09, 0x02, 0x07, 0x04, 0x07, 0x07, 0x07, /* 0x40 to 0x47 */
343 | 0x09, 0x09, 0x0A, 0x09, 0x0C, 0x09, 0x0E, 0x07, /* 0x48 to 0x4F */
344 | 0x04, 0x01, 0x0A, 0x03, 0x04, 0x04, 0x04, 0x07, /* 0x50 to 0x57 */
345 | 0x0A, 0x09, 0x0A, 0x0A, 0x04, 0x0D, 0x0A, 0x0F, /* 0x58 to 0x5F */
346 | 0x02, 0x01, 0x02, 0x02, 0x0C, 0x05, 0x02, 0x07, /* 0x60 to 0x67 */
347 | 0x0C, 0x09, 0x02, 0x0B, 0x0C, 0x0C, 0x0C, 0x0F, /* 0x68 to 0x6F */
348 | 0x01, 0x01, 0x02, 0x01, 0x04, 0x01, 0x06, 0x0F, /* 0x70 to 0x77 */
349 | 0x08, 0x01, 0x0A, 0x0F, 0x0C, 0x0F, 0x0F, 0x0F /* 0x78 to 0x7F */
350 | };
351 |
352 | final int WINDOW_SIZE = 128;
353 | final int BIT_PERIOD = 8;
354 | final double STAMP_MS = (float) WINDOW_SIZE / (SAMPLE_RATE / 1000);
355 | long lastStamp = 0;
356 | int bank = 0;
357 | int bytei = 0;
358 |
359 | class Msg {
360 | boolean ready = false;
361 | byte[] bytes = new byte[10];
362 | long stamp = 0;
363 | int len = 0;
364 | }
365 |
366 | Msg[] msgs = new Msg[10];
367 |
368 | double goertzelSimple(final short[] buffer, final int bufferOffset, final double COEFF) {
369 |
370 | double Q1 = 0, Q2 = 0;
371 | for (int s = 0; s < WINDOW_SIZE; s++) {
372 | double Q0 = COEFF * Q1 - Q2 + buffer[bufferOffset + s];
373 | Q2 = Q1;
374 | Q1 = Q0;
375 | }
376 | return Math.sqrt(Q1 * Q1 + Q2 * Q2 - COEFF * Q1 * Q2);
377 |
378 | }
379 |
380 | boolean lastSmpl = false;
381 | byte block = 0;
382 |
383 | void FMSimpleRecv(boolean smpl) {
384 |
385 | double diff = (stamp - lastStamp) * STAMP_MS;
386 |
387 | if (state != IDLE && diff > BIT_PERIOD * 4) {
388 | state = IDLE;
389 | if(bytei>0) {
390 | msgs[bank].len = bytei;
391 | msgs[bank].ready = true;
392 | log.v("Block ready | len=%d bank=%d", bytei, bank);
393 | this.publishProgress(new Character((char)msgs[bank].bytes[0]));
394 | bank = (bank + 1) % msgs[bank].bytes.length;
395 | msgs[bank].ready = false;
396 | }
397 | log.w("reset");
398 | bytei = block = biti = 0;
399 | }
400 |
401 | if (lastSmpl != smpl) {
402 | //log.v("smpl=%d diff=%f biti=%d block=%x", smpl ? 1 : 0, diff, biti, block);
403 | lastSmpl = smpl;
404 | switch (state) {
405 | case IDLE:
406 | if (smpl == true) {
407 | lastStamp = stamp;
408 | state = HIGH_PULSE;
409 | }
410 | break;
411 | case LOW_PULSE:
412 | // newBit = 0;
413 | log.v("diff=%f biti=%d magRng=%f-%f", diff, biti,maxMag,minMag);
414 | maxMag = 0;
415 | minMag = 99999999999999.9;
416 | if (diff > BIT_PERIOD * 2.75) {
417 | // newBit = 1;
418 | block |= 1 << biti;
419 | //log.v("+1 %x", block);
420 | }
421 | lastStamp = stamp;
422 | state = HIGH_PULSE;
423 |
424 | if (++biti == 8) {
425 | state = HIGH_PULSE;
426 | log.i("Byte:%x", block);
427 | msgs[bank].bytes[bytei++] = block;
428 | block = biti = 0;
429 | // if(bytei == bytej) Serial.println("TOO SLOW");
430 | }
431 | break;
432 | case HIGH_PULSE:
433 | state = LOW_PULSE;
434 | break;
435 | }
436 | }
437 | }
438 |
439 | double maxMag=0;
440 | double minMag=9999999999999.9;
441 | @Override
442 | protected Void doInBackground(Void... arg0) {
443 |
444 | log.v("audioIn thread started");
445 |
446 | final int N = 4096;// AudioRecord.getMinBufferSize(SAMPLE_RATE,
447 | // AudioFormat.CHANNEL_IN_MONO,
448 | // AudioFormat.ENCODING_PCM_16BIT);
449 | short buffer[] = new short[N];
450 |
451 | log.v("N=%d", N);
452 |
453 | AudioRecord recorder = new AudioRecord(AudioSource.MIC, SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, N * 2);
454 | recorder.startRecording();
455 |
456 | final double COEFF[] = new double[IN_TONES.length];
457 |
458 | for (int i = 0; i < IN_TONES.length; i++) {
459 | COEFF[i] = 2.0 * Math.cos(2 * Math.PI * IN_TONES[i] / SAMPLE_RATE);
460 | }
461 |
462 | for (int i = 0; i < msgs.length; i++) {
463 | msgs[i] = new Msg();
464 | }
465 |
466 | final double ON_THRESHOLD = 8000;//16384;
467 | final double OFF_THRESHOLD = ON_THRESHOLD * 1 / 2;
468 |
469 | while (true) {
470 |
471 | int read = 0;
472 | while (read < N) {
473 | read += recorder.read(buffer, read, N - read);
474 | }
475 |
476 | for (int j = 0; j < read / WINDOW_SIZE; j++) {
477 | stamp++;
478 | double mag = goertzelSimple(buffer, j * WINDOW_SIZE, COEFF[0]);
479 | if (mag > maxMag) maxMag = mag;
480 | if(mag < minMag) minMag = mag;
481 | if (mag > ON_THRESHOLD) {
482 | // log.v("mag = %f",mag);
483 | FMSimpleRecv(true);
484 | } else if (mag < OFF_THRESHOLD) {
485 | FMSimpleRecv(false);
486 | }
487 |
488 | }
489 | }
490 |
491 | // recorder.stop();
492 | // recorder.release();
493 | // return null;
494 | }
495 | }
496 | }
--------------------------------------------------------------------------------
/src/com/theultimatelabs/sonic/SonicService.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2012 rob@theultimatelabs.com.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the GNU Public License v3.0
5 | * which accompanies this distribution, and is available at
6 | * http://www.gnu.org/licenses/gpl.html
7 | *
8 | * Contributors:
9 | * rob@theultimatelabs.com - initial API and implementation
10 | ******************************************************************************/
11 | package com.theultimatelabs.sonic;
12 |
13 | import java.io.IOException;
14 | import java.io.InputStream;
15 | import java.net.DatagramPacket;
16 | import java.net.InetAddress;
17 | import java.net.MulticastSocket;
18 | import java.net.UnknownHostException;
19 | import java.util.HashMap;
20 |
21 | import android.app.Service;
22 | import android.content.Context;
23 | import android.content.Intent;
24 | import android.content.SharedPreferences;
25 | import android.media.AudioFormat;
26 | import android.media.AudioManager;
27 | import android.media.AudioRecord;
28 | import android.media.AudioTrack;
29 | import android.media.MediaPlayer;
30 | import android.media.MediaRecorder.AudioSource;
31 | import android.media.RingtoneManager;
32 | import android.net.DhcpInfo;
33 | import android.net.Uri;
34 | import android.net.wifi.WifiManager;
35 | import android.net.wifi.WifiManager.MulticastLock;
36 | import android.net.wifi.WifiManager.WifiLock;
37 | import android.os.Handler;
38 | import android.os.IBinder;
39 | import android.os.PowerManager;
40 | import android.os.PowerManager.WakeLock;
41 | import android.util.Log;
42 |
43 | //import com.theultimatelabs.intercom.TimeoutRunnable.ResponseDelayRunnable;
44 |
45 | public class SonicService extends Service {
46 |
47 | private AudioRecord record;
48 | private AudioTrack track;
49 | //private final static int BUF_SIZE = 4096;
50 | //private Handler mTimeoutHandler;
51 | //private Handler mResponseDelayHandler;
52 | //private TimeoutRunnable mTimeoutRunnable;
53 | //private ResponseDelayRunnable mResponseDelayRunnable;
54 | //private PowerManager mPowerManager;
55 |
56 | //private final int STREAM_TYPE = AudioManager.STREAM_MUSIC;
57 | //private AudioManager audioManager;
58 | //private MulticastLock mMulticastLock;
59 | //private MediaPlayer mStartTalkBeep;
60 | //private MediaPlayer mStartListenBeep;
61 | //private boolean mStartTalkBeepSent = true;
62 | //private boolean mStopTalkBeepSent = true;
63 | //private static final double THRESHOLD = 0.75;
64 | //public final static String TAG = "IntercomService";
65 | //private static final long TIMEOUT = 20000; // 10 seconds
66 | //private static final long RESPONSE_DELAY = 100;
67 |
68 | @Override
69 | public void onCreate() {
70 | MyLog.v("onCreate");
71 |
72 | this.audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
73 | this.audioOut = initializeAudioOut();
74 | this.audioIn = initializeAudioIn();
75 |
76 | /*mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
77 | mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK
78 | | PowerManager.ACQUIRE_CAUSES_WAKEUP, "Intercom WakeLock");
79 | mWakeLock.setReferenceCounted(false);*/
80 |
81 | new AudioIn().start();
82 | new AudioOut().start();
83 |
84 | /*mTimeoutHandler = new Handler();
85 | mResponseDelayHandler = new Handler();
86 | mTimeoutRunnable = new TimeoutRunnable();
87 | mResponseDelayRunnable = new ResponseDelayRunnable();
88 |
89 | mStartTalkBeep = MediaPlayer.create(getApplicationContext(),
90 | R.raw.beep3);
91 | mStartListenBeep = MediaPlayer.create(getApplicationContext(),
92 | R.raw.opbeep);
93 |
94 | onStatusAll();*/
95 |
96 | /*
97 | * Log.v(TAG, "Setup RTP"); AudioManager audioManager = (AudioManager)
98 | * getApplicationContext() .getSystemService(Context.AUDIO_SERVICE);
99 | * audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
100 | *
101 | *
102 | * mAudioStream = new AudioStream(mLocalAddress);
103 | * Log.v(TAG,mAudioStream.toString()); //AudioCodec codec =
104 | * AudioCodec.getCodec(100, "AMR/8000", "mode-set=1");
105 | * //mAudioStream.setCodec(codec);
106 | * mAudioStream.setCodec(AudioCodec.PCMU);
107 | * mAudioStream.setMode(AudioStream.MODE_NORMAL);
108 | *
109 | * mAudioGroup = new AudioGroup(); Log.v(TAG,mAudioGroup.toString());
110 | * mAudioGroup.setMode(AudioGroup.MODE_NORMAL);
111 | * Log.v(TAG,mAudioStream.toString());
112 | * Log.v(TAG,mAudioGroup.toString());
113 | *
114 | * mAudioStream.join(mAudioGroup); Log.v(TAG,"RTP setup");
115 | *
116 | *
117 | * // AudioManager AudioManager audioManager = (AudioManager)
118 | * getSystemService(AUDIO_SERVICE);
119 | * audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
120 | *
121 | *
122 | * try {
123 | * Log.v(TAG,"Creating audio stream on "+mLocalAddress.toString());
124 | * mAudioStream = new AudioStream(mLocalAddress); } catch
125 | * (SocketException e) { Log.d("Quit", "Socket Error"); System.exit(1);
126 | * } mAudioStream.setMode(RtpStream.MODE_NORMAL);
127 | * mAudioStream.setCodec(AudioCodec.PCMU);
128 | * mAudioStream.associate(mBroadcastAddress
129 | * ,mAudioStream.getLocalPort());//8585);//this.createInet(192,168,0,7),
130 | * 8585);
131 | *
132 | * // Initialize an AudioGroup and attach an AudioStream AudioGroup
133 | * main_grp = new AudioGroup();
134 | * main_grp.setMode(AudioGroup.MODE_ECHO_SUPPRESSION);
135 | * mAudioStream.join(main_grp); Log.d("Log","PORT: "+
136 | * mAudioStream.getLocalPort());
137 | */
138 |
139 | }
140 |
141 | @Override
142 | public void onStart(Intent intent, int startId) {
143 |
144 | log.i("onStart");
145 | if (intent == null || intent.getAction() == null)
146 | return;
147 | log.i(intent.getAction());
148 |
149 | if (intent.getAction().equals("msg")) {
150 | DTMF()
151 |
152 | }
153 |
154 | }
155 |
156 |
157 | /*private void onTalk(boolean talkRequest) {
158 |
159 | mServerTalk = talkRequest;
160 |
161 | if (mServerTalk) {
162 | Log.i(TAG, "startTalk");
163 |
164 | synchronized (mState) {
165 |
166 | mStartTalkBeepSent = false;
167 |
168 | if (mState == SLEEP) {
169 |
170 | mWifiLock.acquire();
171 | } else if (mState == CLIENT) {
172 | Log.w(TAG, "Trying to talk while client");
173 | }
174 | mPrivate = false;
175 | mState = SERVER;
176 | mAudioRecord.startRecording();
177 | mTimeoutHandler.removeCallbacks(mTimeoutRunnable);
178 | // beep();
179 |
180 | }
181 | } else {
182 | Log.i(TAG, "stopTalk");
183 |
184 | synchronized (mState) {
185 | mStopTalkBeepSent = false;
186 | mAudioRecord.stop();
187 | mTimeoutHandler.removeCallbacks(mTimeoutRunnable);
188 | mPrivate = getSharedPreferences(PREFS, 0).getBoolean("private",
189 | false);
190 | if (mServerListen == false) {
191 | mState = SLEEP;
192 | mWifiLock.release();
193 | new Dismiss().start();
194 | }
195 | }
196 |
197 | }
198 |
199 | Intent status = new Intent(IntercomWidget.ACTION_STATUS);
200 | status.putExtra("talk", mServerTalk);
201 | status.putExtra("private", mPrivate);
202 | Log.v(TAG, "SendBroadcast");
203 | sendBroadcast(status);
204 |
205 | }*/
206 |
207 | /*private void stopAudio() {
208 | // mBroadcastAudioTrack.flush();
209 | // mBroadcastAudioTrack.pause();
210 | for (AudioTrack track : mAudioTracks.values()) {
211 | track.flush();
212 | track.pause();
213 | }
214 | }
215 |
216 | private void closeAudio() {
217 | mBroadcastAudioTrack.pause();
218 | mBroadcastAudioTrack.flush();
219 | stopAudio();
220 | mAudioTracks = new HashMap();
221 | }*/
222 |
223 | /*private void onListen(boolean listenRequest) {
224 | mServerListen = listenRequest;
225 |
226 | if (mServerListen) {
227 | Log.i(TAG, "startListen");
228 |
229 | synchronized (mState) {
230 |
231 | if (mState == SLEEP) {
232 | mWifiLock.acquire();
233 | new Wakeup().start();
234 | // byte buf[] = new byte[4096];
235 | // try {
236 | // mServerSocket.send(new DatagramPacket(buf, buf.length,
237 | // mBroadcastAddress, DST_PORT));
238 | // } catch (IOException e) {
239 | // e.printStackTrace();
240 | // }
241 | }
242 |
243 | mSilent = false;
244 |
245 | }
246 | } else {
247 | Log.i(TAG, "stopListen");
248 | synchronized (mState) {
249 | stopAudio();
250 | mSilent = getSharedPreferences(PREFS, 0).getBoolean("silent",
251 | false);
252 | if (mServerTalk == false) {
253 | mState = SLEEP;
254 | mWifiLock.release();
255 | new Dismiss().start();
256 | }
257 |
258 | }
259 |
260 | }
261 | Intent status = new Intent(IntercomWidget.ACTION_STATUS);
262 | status.putExtra("listen", mServerListen);
263 | status.putExtra("silent", mSilent);
264 | Log.v(TAG, "SendBroadcast");
265 | sendBroadcast(status);
266 |
267 | }*/
268 |
269 |
270 |
271 |
272 | private class Wakeup extends Thread {
273 | public final static String TAG = "Wakeup";
274 |
275 | // Some devices, like the Nexus Galaxy, won't wakeup to broadcast
276 | // packets,
277 | // so we send a single empty packet to every client on the subnet.
278 | @Override
279 | public void run() {
280 | Log.i(TAG, "Running wifi wakeup!");
281 | byte buf[] = new byte[2];
282 | buf[0] = buf[1] = 1;
283 | byte[] subnet = mBroadcastAddress.getAddress();
284 | // Log.i(TAG,String.format("subnet[0] = %x subnet[4] = %x",
285 | // subnet[0], subnet[3]));
286 | for (int i = 1; i < 254; i++) {
287 | subnet[3] = (byte) i;// i;
288 | Log.v(TAG, String.format("Wakeup %d", i));
289 | try {
290 | mServerSocket.send(new DatagramPacket(buf, 2, InetAddress
291 | .getByAddress(subnet), DATA_DST_PORT));
292 | } catch (IOException e) {
293 | e.printStackTrace();
294 | }
295 | }
296 | Log.d(TAG, "Running wifi wakeup done");
297 | }
298 | }
299 |
300 | private class Dismiss extends Thread {
301 | public final static String TAG = "Dismiss";
302 |
303 | @Override
304 | public void run() {
305 | Log.i(TAG, "Running wifi dismiss");
306 | byte buf[] = new byte[2];
307 | buf[0] = buf[1] = 0;
308 | try {
309 | mServerSocket.send(new DatagramPacket(buf, 2,
310 | mBroadcastAddress, DATA_DST_PORT));
311 | } catch (IOException e) {
312 | e.printStackTrace();
313 | }
314 | }
315 | }
316 |
317 | public class ControlIn extends Thread {
318 |
319 | public final static String TAG = "ControlIn";
320 |
321 | public void run() {
322 |
323 | byte[] buf = new byte[128];
324 |
325 | DatagramPacket pkt = new DatagramPacket(buf, 128);
326 |
327 | while (!this.isInterrupted()) {
328 |
329 | try {
330 | mCtrlReceiveSocket.receive(pkt);
331 | } catch (IOException e) {
332 | // TODO Auto-generated catch block
333 | e.printStackTrace();
334 | Log.e(TAG, "Error receiving packet");
335 | return;
336 | }
337 | }
338 | }
339 | }
340 |
341 | private void serverAudioIn(byte[] buf, int N, double rms) throws UnknownHostException, IOException {
342 |
343 | if (mStartTalkBeepSent == false) {
344 | try {
345 | InputStream beepStream = getResources().openRawResource(
346 | R.raw.beep3);
347 | int len = beepStream.available();
348 | byte[] beepBuf = new byte[len];
349 |
350 | beepStream.read(beepBuf);
351 | mServerSocket.send(new DatagramPacket(beepBuf, len, InetAddress
352 | .getByName("224.0.0.10"), DATA_DST_PORT));
353 | } catch (Exception e) {
354 | e.printStackTrace();
355 | Log.e(TAG, "Error sending start talk beep");
356 | }
357 |
358 | mStartTalkBeepSent = true;
359 |
360 | } else if (mStopTalkBeepSent == false) {
361 | try {
362 | InputStream beepStream = getResources().openRawResource(
363 | R.raw.beep2);
364 | int len = beepStream.available();
365 | byte[] beepBuf = new byte[len];
366 |
367 | beepStream.read(beepBuf);
368 | mServerSocket.send(new DatagramPacket(beepBuf, len, InetAddress
369 | .getByName("224.0.0.10"), DATA_DST_PORT));
370 | } catch (Exception e) {
371 | e.printStackTrace();
372 | Log.e(TAG, "Error sending start talk beep");
373 | }
374 |
375 | mStopTalkBeepSent = true;
376 | }
377 |
378 | if (rms > THRESHOLD && mServerTalk) {
379 | mServerSocket.send(new DatagramPacket(buf,
380 | N, InetAddress
381 | .getByName("224.0.0.10"),
382 | DATA_DST_PORT));
383 | }
384 |
385 | }
386 |
387 | private void clientAudioIn(byte[] buf, int N, double rms) throws IOException {
388 | if (rms > THRESHOLD && !mPrivate) {
389 | // Only send small packets
390 | for (int i = 0; i < N; i += 256) {
391 | int len = Math.min(N - i, 256);
392 | mClientSocket.send(new DatagramPacket(
393 | buf, i, len, mServerAddress,
394 | DATA_DST_PORT));
395 | }
396 | }
397 | }
398 |
399 | public class AudioIn extends Thread {
400 |
401 | public final static String TAG = "AudioIn";
402 |
403 | @Override
404 | public void run() {
405 |
406 | try {
407 |
408 | while (!this.isInterrupted()) {
409 |
410 | byte[] buf = new byte[BUF_SIZE];
411 | int N = mAudioRecord.read(buf, 0, buf.length);
412 |
413 | if (N > 0) {
414 |
415 | double rms = 0;
416 | for (int i = 0; i < N; i += 2) {
417 | /*
418 | * ByteBuffer bb = ByteBuffer.allocate(2);
419 | * bb.order(ByteOrder.LITTLE_ENDIAN);
420 | * bb.put(buf[i]); bb.put(buf[i+1]); short pcm =
421 | * bb.getShort(0); rms += Math.pow(pcm, 2);
422 | */
423 | rms += (double) buf[i + 1] * buf[i + 1];
424 | }
425 | rms = Math.sqrt(rms / N);
426 |
427 | Log.v(TAG, String.format(
428 | "Got mic buffer of %d bytes with rms = %f", N,
429 | rms));
430 |
431 | if(mState==SERVER) {
432 | serverAudioIn(buf, N, rms);
433 | }
434 | else if (mState==CLIENT){
435 | clientAudioIn(buf, N, rms);
436 | }
437 | else {
438 | Log.e(TAG, "Recording while sleeping");
439 | gotoSleep();
440 | }
441 | } else {
442 | try {
443 | Thread.sleep(100);
444 | } catch (InterruptedException e) {
445 | e.printStackTrace();
446 | }
447 | }
448 |
449 | }
450 | } catch (IOException e) {
451 | // TODO Auto-generated catch block
452 | e.printStackTrace();
453 | Log.e(TAG, "Error recording audio");
454 | } finally {
455 | mAudioRecord.stop();
456 | }
457 | }
458 | }
459 |
460 | private void resetTimeout() {
461 | mTimeoutHandler.removeCallbacks(mTimeoutRunnable);
462 | mTimeoutHandler.postDelayed(mTimeoutRunnable, TIMEOUT);
463 | }
464 |
465 | private void wakeupAudioOut(DatagramPacket pkt) {
466 | if (pkt.getPort() == SERVER_DATA_DST_PORT) {
467 | Log.i(TAG, "Now in CLIENT mode");
468 |
469 | mState = CLIENT;
470 |
471 | mWakeLock.acquire();
472 | mWifiLock.acquire();
473 | mMulticastLock.acquire();
474 |
475 | Intent status = new Intent(IntercomWidget.ACTION_STATUS);
476 | mServerAddress = pkt.getAddress();
477 |
478 | if (!mSilent) {
479 | mBroadcastAudioTrack.flush();
480 | status.putExtra("listen", true);
481 | }
482 |
483 | if (!mPrivate) {
484 | mAudioRecord.startRecording();
485 | status.putExtra("talk", true);
486 | }
487 |
488 | mAudioManager.setStreamVolume(STREAM_TYPE,
489 | mAudioManager.getStreamMaxVolume(STREAM_TYPE),
490 | 0);
491 |
492 | mStartListenBeep.start();
493 |
494 | sendBroadcast(status);
495 |
496 | mBroadcastAudioTrack.play();
497 |
498 | }
499 | else if (pkt.getPort() == CLIENT_DATA_SRC_PORT) {
500 | Log.e(TAG, "Got packet from client while sleeping");
501 | }
502 | }
503 |
504 | private void serverAudioOut(DatagramPacket pkt) {
505 | if(pkt.getPort() == SERVER_DATA_DST_PORT) {
506 | Log.e(TAG,"Server got packet from server\n");
507 | //Ignore it
508 | }
509 | else if (pkt.getPort() == CLIENT_DATA_SRC_PORT) {
510 |
511 | if (!mAudioTracks.containsKey(pkt.getAddress())) {
512 | Log.i(TAG, "open new track for "
513 | + pkt.getAddress().toString());
514 | AudioTrack track = initializeAudioOut();
515 | track.play();
516 | mAudioTracks.put(pkt.getAddress(), track);
517 | }
518 | if (mServerListen) {
519 | // Log.i(TAG,mAudioTracks.get(pkt.getAddress()).getS)
520 | AudioTrack track = mAudioTracks.get(pkt.getAddress());
521 | track.write(pkt.getData(), 0, pkt.getLength());
522 | }
523 | }
524 |
525 | }
526 |
527 | private void clientAudioOut(DatagramPacket pkt){
528 |
529 | if (pkt.getPort() == SERVER_DATA_DST_PORT) {
530 |
531 | if (mState == CLIENT) {
532 | Log.v(TAG, "In client mode, got packet from server");
533 |
534 | resetTimeout();
535 |
536 | // Delay response to cut down on traffic
537 | mAudioRecord.stop();
538 | mResponseDelayHandler
539 | .removeCallbacks(mResponseDelayRunnable);
540 | mResponseDelayHandler.postDelayed(
541 | mResponseDelayRunnable, RESPONSE_DELAY);
542 |
543 | if (!mSilent && pkt.getLength() > 2) {
544 | mBroadcastAudioTrack.write(pkt.getData(), 0,
545 | pkt.getLength());
546 | // the last buffer
547 | } else if (pkt.getLength() == 2) {
548 | if (pkt.getData()[0] == 0) {
549 | gotoSleep();
550 | } else if (pkt.getData()[0] == 1) {
551 |
552 | }
553 | }
554 | }
555 |
556 | }
557 | else if (pkt.getPort() == CLIENT_DATA_SRC_PORT) {
558 | Log.w(TAG,
559 | "Received client packets while in client mode, ignoring");
560 | }
561 | }
562 | public class AudioOut extends Thread {
563 |
564 | public final static String TAG = "AudioOut";
565 |
566 | @Override
567 | public void run() {
568 |
569 | byte[] buf = new byte[BUF_SIZE];
570 |
571 | DatagramPacket pkt = new DatagramPacket(buf, BUF_SIZE);
572 |
573 | while (!this.isInterrupted()) {
574 |
575 | try {
576 | mDataReceiveSocket.receive(pkt);
577 | } catch (IOException e) {
578 | // TODO Auto-generated catch block
579 | e.printStackTrace();
580 | Log.e(TAG, "Error receiving packet");
581 | return;
582 | }
583 |
584 | if (pkt.getAddress().equals(mLocalAddress))
585 | continue;
586 |
587 | Log.v(TAG, String.format(
588 | "Received %d byte udp packet from %s local=%s port=%d",
589 | pkt.getLength(), pkt.getAddress().toString(),
590 | mLocalAddress.toString(), pkt.getPort()));
591 |
592 | if(mState==SERVER) {
593 | serverAudioOut(pkt);
594 | }
595 | else if(mState==CLIENT) {
596 | clientAudioOut(pkt);
597 | }
598 | else if (mState==SLEEP) {
599 | wakeupAudioOut(pkt);
600 | }
601 | // synchronized (mState) {
602 | }
603 | }
604 | }
605 |
606 | class TimeoutRunnable implements Runnable {
607 |
608 | public void run() {
609 | Log.i(TAG, "TimeOut");
610 | gotoSleep();
611 | }
612 |
613 | };
614 |
615 | void gotoSleep() {
616 | synchronized (mState) {
617 | // if (mState != SLEEP) {
618 | mState = SLEEP;
619 | closeAudio();
620 | mAudioRecord.stop();
621 | if (mWakeLock.isHeld()) {
622 | mWakeLock.release();
623 | }
624 | if (mWifiLock.isHeld()) {
625 | mWifiLock.release();
626 | }
627 | if (mMulticastLock.isHeld()) {
628 | mMulticastLock.release();
629 | }
630 | Intent status = new Intent(IntercomWidget.ACTION_STATUS);
631 | mServerTalk = mServerListen = false;
632 | status.putExtra("talk", mServerTalk);
633 | status.putExtra("listen", mServerListen);
634 | sendBroadcast(status);
635 | }
636 | }
637 |
638 | class ResponseDelayRunnable implements Runnable {
639 |
640 | public void run() {
641 | Log.i(TAG, "ResponseDelayRunnable");
642 | synchronized (mState) {
643 | mAudioRecord.startRecording();
644 | }
645 | }
646 | };
647 |
648 | public InetAddress getLocalAddress() throws IOException {
649 | WifiManager wifi = (WifiManager) this
650 | .getSystemService(Context.WIFI_SERVICE);
651 | int local = wifi.getDhcpInfo().ipAddress;
652 | byte[] quads = new byte[4];
653 | for (int k = 0; k < 4; k++)
654 | quads[k] = (byte) ((local >> k * 8) & 0xFF);
655 | return InetAddress.getByAddress(quads);
656 | }
657 |
658 | public InetAddress getBroadcastAddress() throws IOException {
659 | WifiManager wifi = (WifiManager) this
660 | .getSystemService(Context.WIFI_SERVICE);
661 | DhcpInfo dhcp = wifi.getDhcpInfo();
662 | // handle null somehow
663 |
664 | int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
665 | byte[] quads = new byte[4];
666 | for (int k = 0; k < 4; k++)
667 | quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
668 | return InetAddress.getByAddress(quads);
669 | }
670 |
671 | public void beep() {
672 | Uri soundUri = RingtoneManager
673 | .getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
674 | MediaPlayer mMediaPlayer = new MediaPlayer();
675 | try {
676 | mMediaPlayer.setDataSource(getApplicationContext(), soundUri);
677 | } catch (IllegalArgumentException e) {
678 | // TODO Auto-generated catch block
679 | e.printStackTrace();
680 | } catch (SecurityException e) {
681 | // TODO Auto-generated catch block
682 | e.printStackTrace();
683 | } catch (IllegalStateException e) {
684 | // TODO Auto-generated catch block
685 | e.printStackTrace();
686 | } catch (IOException e) {
687 | // TODO Auto-generated catch block
688 | e.printStackTrace();
689 | }
690 | final AudioManager audioManager = (AudioManager) getApplicationContext()
691 | .getSystemService(Context.AUDIO_SERVICE);
692 | if (audioManager.getStreamVolume(AudioManager.STREAM_NOTIFICATION) != 0) {
693 | mMediaPlayer.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);
694 | mMediaPlayer.setLooping(false);
695 | try {
696 | mMediaPlayer.prepare();
697 | } catch (IllegalStateException e) {
698 | // TODO Auto-generated catch block
699 | e.printStackTrace();
700 | } catch (IOException e) {
701 | // TODO Auto-generated catch block
702 | e.printStackTrace();
703 | }
704 | mMediaPlayer.start();
705 | }
706 | }
707 |
708 | private AudioTrack initializeAudioOut() {
709 | int minBufSize = AudioTrack.getMinBufferSize(8000,
710 | AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
711 |
712 | Log.w(TAG, String.format("AudioOut minBufSize %d", minBufSize));
713 | assert BUF_SIZE >= minBufSize;
714 |
715 | return new AudioTrack(STREAM_TYPE, 8000, AudioFormat.CHANNEL_OUT_MONO,
716 | AudioFormat.ENCODING_PCM_16BIT, minBufSize,
717 | AudioTrack.MODE_STREAM);
718 |
719 | }
720 |
721 | private boolean initializeAudioIn() {
722 | int minBufSize = AudioRecord.getMinBufferSize(8000,
723 | AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
724 |
725 | Log.w(TAG, String.format("AudioIn minBufSize %d", minBufSize));
726 | assert BUF_SIZE >= minBufSize;
727 |
728 | mAudioRecord = new AudioRecord(AudioSource.VOICE_COMMUNICATION, 8000,
729 | AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT,
730 | minBufSize);
731 |
732 | return true;
733 | }
734 |
735 | @Override
736 | public IBinder onBind(Intent arg0) {
737 | // TODO Auto-generated method stub
738 | return null;
739 | }
740 |
741 | }
742 |
--------------------------------------------------------------------------------
/src/com/theultimatelabs/sonic/log.java:
--------------------------------------------------------------------------------
1 | package com.theultimatelabs.sonic;
2 |
3 | import android.util.Log;
4 |
5 | public class log {
6 | private static String makeTag() {
7 | StackTraceElement caller = Thread.currentThread().getStackTrace()[4];
8 | String className = caller.getFileName().substring(0, caller.getFileName().length()-5);
9 | return String.format("%s:%d %s",className,caller.getLineNumber(),caller.getMethodName()) ;
10 | }
11 |
12 | public static void v(String tag, String format, Object...args)
13 | {
14 | Log.v(tag, String.format(format, args));
15 | }
16 | public static void v(String format, Object...args)
17 | {
18 | v(makeTag(),format,args);
19 | }
20 | public static void v(String format)
21 | {
22 | v(makeTag(),format);
23 | }
24 | public static void i(String tag, String format, Object...args)
25 | {
26 | Log.i(tag, String.format(format, args));
27 | }
28 | public static void i(String format, Object...args)
29 | {
30 | i(makeTag(),format,args);
31 | }
32 | public static void i(String format)
33 | {
34 | i(makeTag(),format);
35 | }
36 | public static void w(String tag, String format, Object...args)
37 | {
38 | Log.w(tag, String.format(format, args));
39 | }
40 | public static void w(String format, Object...args)
41 | {
42 | w(makeTag(),format,args);
43 | }
44 | public static void w(String format)
45 | {
46 | w(makeTag(),format);
47 | }
48 | public static void e(String tag, String format, Object...args)
49 | {
50 | Log.e(tag, String.format(format, args));
51 | }
52 | public static void e(String format, Object...args)
53 | {
54 | e(makeTag(),format,args);
55 | }
56 | public static void e(String format)
57 | {
58 | e(makeTag(),format);
59 | }
60 | public static void d(String tag, String format, Object...args)
61 | {
62 | Log.d(tag, String.format(format, args));
63 | }
64 | public static void d(String format, Object...args)
65 | {
66 | d(makeTag(),format,args);
67 | }
68 | public static void d(String format)
69 | {
70 | d(makeTag(),format);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------