모기 퇴치기 Android 모기 퇴치 앱으로 만들어 쓰자

모기가 무서워하는 주파수의 tone을 만들어 AudioTrack으로 재생하는 안드로이드 모기 퇴치 앱 소스를 싣고 있다.

여름철이면 어김없이 찾아오는 귀찮은 모기… 약이나 팔찌, 그리고 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의 실행 영상이다.


댓글

이 블로그의 인기 게시물

간단한 cfar 알고리즘에 대해

windows에서 간단하게 크롬캐스트(Chromecast)를 통해 윈도우 화면 미러링 방법

base64 인코딩 디코딩 예제 c 소스

아두이노(arduino) 심박센서 (heart rate sensor) 심박수 측정 example code

python asyncio를 이용한 async socket server client example code