모기 퇴치기 Android 모기 퇴치 앱으로 만들어 쓰자
여름철이면 어김없이 찾아오는 귀찮은 모기… 약이나 팔찌, 그리고 app까지 모기를
쫓기 위한 많은 방법들이 있다. 퇴치제를 뿌리거나 팔찌를 차도 모기에 많이 물리는
체질이고, 모기에 물리면 피부가 다른 사람들 보다 심하게 검붉게 변하고 가려워
힘들어 하는 사람이 있어 모기를 쫓는 app을 만들어 보기로 했다.
모기 퇴치 app의 원리
모기는 임신한 상태의 암컷이 주로 흡혈 활동을 한다. 그리고, 임신 상태의 암컷
모기를 수컷 모기가 또 임신시키면 암컷 모기는 죽을 수도 있다고 한다. 그래서
임신한 암컷 모기는 본능적으로 수컷 모기를 피하게 된다고 한다. 그래서 수컷
모기가 움직일 때 내는 소리를 내 암컷 모기를 속이는 것이 한 방법이다.
그리고, 모기들의 포식자중 하나는 잠자리다. 잠자리가 내는 소리를 내어 모기를
속이는 것도 또 하나의 방법이다.
잠자리 주파수를 구글에서 찾아봤더니, 아래와 같이 나왔다. 그 신문사라 내용은 안
봤다.
아래와 같이 모기 퇴치용 주파수의 pcm데이터를 만든 후 AudioTrack을 사용하여
플레이 하는 방법을 사용하기로 했다.
private void makeTone(double freqHz, int sampleRate, byte pcm[], int
sample_size)
{
int pcm_i = 0;
double sigma = 0.8;
if(pcm ==null || sample_size <= 0) return;
// make signal
for(int i = 0; i<sample_size;i++) {
double s = (i - (sample_size - 1) / 2) /
(sigma*(sample_size - 1) / 2);
short val = (short) (Math.sin(freqHz * 2 * Math.PI
* i / sampleRate)*Math.exp(-(s*s)/ 2) * 32767);
pcm[pcm_i++] = (byte) (val & 0xff);
pcm[pcm_i++] = (byte) ((val & 0xff00) >>
8);
}
}
AudioTrack 사용
생성 및 play
audioTrack = new AudioTrack.Builder()
.setAudioAttributes(new
AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build())
.setAudioFormat(new
AudioFormat.Builder().setEncoding(AudioFormat.ENCODING_PCM_16BIT).setSampleRate(sampleRate).setChannelMask(AudioFormat.CHANNEL_OUT_MONO).build())
.setBufferSizeInBytes(getMinBufferSize(sampleRate,AudioFormat.CHANNEL_OUT_MONO,AudioFormat.ENCODING_PCM_16BIT))
.build();
audioTrack.setVolume(1.0f);
audioTrack.play();
오디오 버퍼 업데이트
audioTrack.write(pcm,0,pcm.length);
stop playing
audioTrack.setVolume(0.0f);
audioTrack.pause();
audioTrack.flush();
audioTrack.release();
모기 퇴치 app source code
App 생성은 아래와 같이 했으며 Tabbed Activity를 사용했다.
NoiseSoundPlayer class를 만들어 모기를 쫓는 소리를 재생하게 하였으며, 이
class는 MainActivity의 멤버로 두어 ViewrPager에서 Page가 변할 때마다 재생을
멈추거나, 잠자리 또는 수컷 모기의 소리가 나도록 했다.
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener()
{
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
switch(position) {
case 0:
if(mNoisePlayer !=
null) mNoisePlayer.Stop();
break;
case 1:
if(mNoisePlayer !=
null)
{
mNoisePlayer.Stop();
mNoisePlayer.Start(NoiseSoundPlayer.RunMode.RUN_MODE_DRAGONFLY);
}
break;
case 2:
if(mNoisePlayer !=
null)
{
mNoisePlayer.Stop();
mNoisePlayer.Start(NoiseSoundPlayer.RunMode.RUN_MODE_MOSQUITO);
}
break;
default:
break;
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
또 각 Page는 적당한 잠자리, 모기 이미지를 골라 화면에 출력하게 하였다.
모기 퇴치 소리를 재생하는 NoiseSoundPlayer.java 소스는 아래와 같다.
package com.example.r.exterminatemosquito;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioTrack;
import android.os.SystemClock;
import static android.media.AudioTrack.*;
/**
* Created by r on 2020-06-11.
*/
public class NoiseSoundPlayer implements Runnable {
public enum RunMode
{
RUN_MODE_NON,
RUN_MODE_MOSQUITO,
RUN_MODE_DRAGONFLY,
}
private AudioTrack audioTrack = null;
private double mdduration_sec = 2;
private int sampleRate = 8000;
private int sample_count = (int)(mdduration_sec*sampleRate);
private byte pcm[] = new byte[2*sample_count]; // 16bits pcm
private RunMode mRunMode = RunMode.RUN_MODE_NON;
private Thread
mWorker = null;
public NoiseSoundPlayer()
{
mRunMode = RunMode.RUN_MODE_NON;
}
private void wait(int ms)
{
SystemClock.sleep(ms);
}
public boolean Start(RunMode runMode)
{
if(runMode == RunMode.RUN_MODE_NON) return false;
if(mRunMode == runMode) return true;
if((mRunMode != RunMode.RUN_MODE_NON) ||
(audioTrack != null))
{
Stop();
}
try {
audioTrack = new
AudioTrack.Builder()
.setAudioAttributes(new
AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build())
.setAudioFormat(new
AudioFormat.Builder().setEncoding(AudioFormat.ENCODING_PCM_16BIT).setSampleRate(sampleRate).setChannelMask(AudioFormat.CHANNEL_OUT_MONO).build())
.setBufferSizeInBytes(getMinBufferSize(sampleRate,AudioFormat.CHANNEL_OUT_MONO,AudioFormat.ENCODING_PCM_16BIT))
.build();
audioTrack.setVolume(1.0f);
audioTrack.play();
}catch (Exception e)
{
return false;
}
mRunMode = runMode;
mWorker = new Thread(this);
mWorker.start();
return true;
}
public boolean Stop()
{
if(mWorker != null && mWorker.isAlive())
{
mRunMode = RunMode.RUN_MODE_NON;
mWorker.interrupt();
}
if(audioTrack != null)
{
try {
audioTrack.setVolume(0.0f);
audioTrack.pause();
audioTrack.flush();
audioTrack.release();
audioTrack = null;
}catch (IllegalStateException e)
{
}
}
mRunMode = RunMode.RUN_MODE_NON;
mWorker = null;
return false;
}
private void makeTone(double freqHz, int sampleRate, byte pcm[],
int sample_size)
{
int pcm_i = 0;
double sigma = 0.8;
if(pcm ==null || sample_size <= 0) return;
// make signal
for(int i = 0; i<sample_size;i++) {
double s = (i - (sample_size - 1) /
2) / (sigma*(sample_size - 1) / 2);
short val = (short) (Math.sin(freqHz
* 2 * Math.PI * i / sampleRate)*Math.exp(-(s*s)/ 2) * 32767);
pcm[pcm_i++] = (byte) (val &
0xff);
pcm[pcm_i++] = (byte) ((val &
0xff00) >> 8);
}
}
@Override
public void run() {
double freqHz = 0;
double freq_hop = 1;
while(mRunMode != RunMode.RUN_MODE_NON)
{
switch (mRunMode)
{
case
RUN_MODE_MOSQUITO:
//
수컷모기 600~650hz
if(freqHz <= 600)
{
freqHz = 600;
freq_hop = 1;
}
else
if(freqHz >= 650) {
freqHz = 650;
freq_hop = -1;
}
makeTone(freqHz,sampleRate,pcm,sample_count);
try {
audioTrack.write(pcm,0,pcm.length);
}catch
(Exception e)
{
}
freqHz+=
freq_hop;
break;
case
RUN_MODE_DRAGONFLY:
//잠자리
10~170hz
if(freqHz <=10)
{
freqHz = 10;
freq_hop = 1;
}
else
if(freqHz >= 170) {
freqHz = 170;
freq_hop = -1;
}
makeTone(freqHz,sampleRate,pcm,sample_count);
try {
audioTrack.write(pcm,0,pcm.length);
}catch
(Exception e)
{
}
freqHz
+=freq_hop;
default:
break;
}
}
}
}
만들어진 app의 실행 영상이다.
댓글
댓글 쓰기