Jumat, 03 Mei 2013

Membuat Piano Virtual di Android dengan Nada Frekuensi

Kode program ini penulis temukan di GitHub. Cukup menarik, karena mampu membuat Aplikasi Android yang Mampu Mengeluarkan Suara dengan Frekuensi alias tanpa file suara (tanpa mp3, ogg, midi, atau apalah itu).

Nah aplikasi ini supaya cukup bermanfaat kita buat dalam bentuk Aplikasi Piano. Frekuensi dalam piano ini sudah diset sesuai dengan frekuensi tuts-tuts atau kunci-kunci tangga nadanya.

Pertama buat peroject Android baru di Eclipse.

Pada MainActivity.java tambahkan kode program berikut :

package com.amijaya.androidpiano;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.widget.Button;

// http://cariprogram.blogspot.com
// nuramijaya@gmail.com

public class MainActivity extends Activity implements OnClickListener {

private static final int duration = 1;
private static final int sampleRate = 8000;
private static final int numSamples = duration * sampleRate;

// http://en.wikipedia.org/wiki/Piano_key_frequencies 
private final double cNote = 261.626;
private final double dFlatNote = 277.183;
private final double dNote = 293.665;
private final double eFlatNote = 311.127;
private final double eNote = 329.628;
private final double fNote = 349.228;
private final double gFlatNote = 369.994;
private final double gNote = 391.995;
private final double aFlatNote = 415.305;
private final double aNote = 440.000;
private final double bFlatNote = 466.164;
private final double bNote = 493.883;
private final double c2Note = 523.251;


private Button buttonC;
private Button buttonDflat;
private Button buttonD;
private Button buttonEflat;
private Button buttonE;
private Button buttonF;
private Button buttonGflat;
private Button buttonG;
private Button buttonAflat;
private Button buttonA;
private Button buttonBflat;
private Button buttonB;
private Button buttonC2;

private byte[] generateSound(double frequency) {
double[] soundSample = new double[numSamples];
for (int i = 0; i < numSamples; i++) {
soundSample[i] = Math.sin(2 * Math.PI * i / (sampleRate/frequency));
}

byte[] sound = new byte[2 * numSamples];
int idx = 0;
for (double dVal: soundSample) {
short val = (short) (dVal * 32767);
sound[idx++] = (byte) (val & 0x00ff);
sound[idx++] = (byte) ((val & 0xff00) >>> 8);
}

return sound;
}

private void playSound(byte[] sound) { 
final byte[] play = sound;
//Concurrency for music playing
(new Thread(new Runnable() {
public void run() {
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 8000, AudioFormat.CHANNEL_OUT_DEFAULT, AudioFormat.ENCODING_PCM_16BIT, numSamples, AudioTrack.MODE_STATIC);
audioTrack.write(play, 0, numSamples);
audioTrack.play();
}
})).start();
}


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        buttonC = (Button) findViewById(R.id.buttonC);
        buttonDflat = (Button) findViewById(R.id.buttonDflat);
        buttonD = (Button) findViewById(R.id.buttonD);
        buttonEflat = (Button) findViewById(R.id.buttonEflat);
        buttonE = (Button) findViewById(R.id.buttonE);
        buttonF = (Button) findViewById(R.id.buttonF);
        buttonGflat = (Button) findViewById(R.id.buttonGflat);
        buttonG = (Button) findViewById(R.id.buttonG);
        buttonAflat = (Button) findViewById(R.id.buttonAflat);
        buttonA = (Button) findViewById(R.id.buttonA);
        buttonBflat = (Button) findViewById(R.id.buttonBflat);
        buttonB = (Button) findViewById(R.id.buttonB);
        buttonC2 = (Button) findViewById(R.id.buttonC2);
        
        buttonC.setOnClickListener(this);
        buttonDflat.setOnClickListener(this);
        buttonD.setOnClickListener(this);
        buttonEflat.setOnClickListener(this);
        buttonE.setOnClickListener(this);
        buttonF.setOnClickListener(this);
        buttonGflat.setOnClickListener(this);
        buttonG.setOnClickListener(this);
        buttonAflat.setOnClickListener(this);
        buttonA.setOnClickListener(this);
        buttonBflat.setOnClickListener(this);
        buttonB.setOnClickListener(this);
        buttonC2.setOnClickListener(this);
        
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        //getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

@Override
public void onClick(View v) {
//TODO need create a object method that changes the pitch
if (v == buttonC) {
playSound(generateSound(cNote));
}
if (v == buttonDflat) {
playSound(generateSound(dNote));
}
if (v == buttonD) {
playSound(generateSound(dFlatNote));
}
if (v == buttonEflat) {
playSound(generateSound(eFlatNote));
}
if (v == buttonE) {
playSound(generateSound(eNote));
}
if (v == buttonF) {
playSound(generateSound(fNote));
}
if (v == buttonGflat) {
playSound(generateSound(gFlatNote));
}
if (v == buttonG) {
playSound(generateSound(gNote));
}
if (v == buttonAflat) {
playSound(generateSound(aFlatNote));
}
if (v == buttonA) {
playSound(generateSound(aNote));
}
if (v == buttonBflat) {
playSound(generateSound(bFlatNote));
}
if (v == buttonB) {
playSound(generateSound(bNote));
}
if (v == buttonC2) {
playSound(generateSound(c2Note));
}

}
}


Pada layout tampilan main.xml buat beberapa buah tombol seperti ini :


<?xml version="1.0" encoding="utf-8"?>
<ScrollView android:id="@+id/ScrollView01" android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello"
    />
<Button android:text="C" android:id="@+id/buttonC" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="D flat" android:id="@+id/buttonDflat" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="D" android:id="@+id/buttonD" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="E flat" android:id="@+id/buttonEflat" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="E" android:id="@+id/buttonE" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="F" android:id="@+id/buttonF" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="G flat" android:id="@+id/buttonGflat" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="G" android:id="@+id/buttonG" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="A flat" android:id="@+id/buttonAflat" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="A" android:id="@+id/buttonA" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="B flat" android:id="@+id/buttonBflat" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="B" android:id="@+id/buttonB" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="C 2" android:id="@+id/buttonC2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
</LinearLayout>
</ScrollView>


Sedangkan untuk AndroidManifest.xml tidak ada perubahan, tidak ada permission yang harus ditambahkan :


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.amijaya.androidpiano"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MainActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
    <uses-sdk android:minSdkVersion="8" />

</manifest> 

Hasilnya :



Project selengkapnya silakan download disini. Jika mengalami kesulitan dalam mendownload caranya seperti ini.

Semoga bermanfaat :)