Minggu, 11 Januari 2015

Menghitung Time Remaining dan Time Elapsed pada MP3 Player


Tidak terasa, rasa semangat ini ternyata telah berhasil menuntun saya dalam menulis tutorial pembuatan MP3 Player yang ketiga. Mana tepuk tangannya? :v

Jika anda benar-benar paham penjelasan pada kedua tutorial sebelumnya, maka harusnya Anda sudah bisa membayangkan bagaimana membuat program MP3 player dengan beberapa kemampuan dasar, yaitu Play, Pause, Seek dan Stop. Sabar... Sabar... Saya memilih menggunakan interface teks karena banyak hal yang harus saya pertimbangkan. Pemrograman GUI membutuhkan banyak kode, takutnya jika saya membuat tutorial dengan GUI, banyak yang semakin bingung. Banyak istilah dan hal baru yang harus dijelaskan secara detail, dengan konsol error mudah untuk ditampilkan. Tapi tenang saja, saya sudah mencoba mengusahakan tutorial perancangan GUI untuk MP3 player. :D


Tutorial ini akan menyempurnakan materi dari tutorial kedua, tentang seeking. Teknik seeking yang kita gunakan dilakukan berdasarkan durasi, jadi, tentu saja ini penting untuk mengetahui sudah berapa lamakah durasi pemutaran MP3 berlangsung. Dengan mengetahui informasi durasi, user yang menggunakan MP3 player kita dapat melakukan navigasi dengan tepat dan mudah.

Secara sederhana durasi terlewat(time elapsed) dan Durasi tersisa(time remaining) dapat dirumuskan sebagai berikut:

Durasi terlewat = Posisi frame sekarang * Durasi per frame

Durasi tersisa = Total durasi - Durasi terlewat


Dari fungsi di atas, ada 2 hal yang sama sekali belum pernah kita bahas, yaitu cara mengetahui Posisi frame sekarang dan Durasi per frame. Total durasi sudah kita bahas pada tutorial kemarin, jadi pada tutorial ini, implementasinya merupakan pengembangan dari kode sebelumnya.

Pertama-tama kita pelajari bagaimana mengetahui maksud dari yang saya tulis “Posisi frame sekarang”.

Kode yang erat kaitannya dengan proses dekode frame adalah sebagai berikut.
 
while(mpg123_read(handle, buffer, ukuran_buffer, &ukuran_samples) == 0)
{
    ao_play(device, buffer, ukuran_samples);
}


Jika seandainya komputer dapat berbicara, maka yang ia katakan adalah, “libmpg123 membaca dan mendekode frame 0 - 15 lalu menyimpan hasilnya pada buffer; libao membaca buffer tersebut dan mengirimkannya ke audio device, sehingga muncul suara. Berikutnya, libmpg123 membaca dan mendekode frame 16 - 31 lalu menyimpan hasilnya pada buffer; libao membaca buffer tersebut dan mengirimkannya ke audio device, sehingga muncul suara........”. Begitu seterusnya hingga libmpg123 sampai pada frame terakhir. Dari sini kita tahu, semua frame punya nomor indeks/urutan. Inilah yang saya sebut posisi frame.

Libmpg123 menyediakan fungsi API untuk mengetahui posisi frame sekarang/current frame. Yang dikatakan current frame adalah posisi frame tepat setelah frame yang baru saja di dekode. Misalnya mpg123_read() mendekode frame 16-31, maka current frame nya adalah frame 32.

off_t mpg123_tellframe(mpg123_handle *mh);

Fungsi di atas mengambil parameter berupa handle. Hasil pemanggilan fungsi berupa posisi frame sekarang. Lalu, untuk mengetahui durasi per frame, libmpg123 juga telah menyediakan fungsinya..

double mpg123_tpf(mpg123_handle *mh);

Parameter yang diperlukan sama seperti fungsi yang dibahas sebelumnya. Hasil fungsi berupa durasi suara yang berlangsung dari setiap frame yang didekode. Umumnya panjang durasi setiap 1 frame tidak lebih dari 0,1 detik, jadi kita menggunakan variabel pecahan untuk mendapatkan nilai yang tepat.


Kode Sepenuhnya
 Berikut ini adalah source codenya. Tidak beda jauh dengan yang sebelumnya.
#include <mpg123.h>
#include <ao/ao.h>

#define MP3_FILE "/home/irvan/test.mp3"
int main(int argc, char *argv[])
{
    mpg123_handle       *handle;
    ao_sample_format    sample_format;
    ao_device           *device;
    int                    driver_id;
    char               *buffer;
    unsigned int        ukuran_buffer;
    long                rate; //sample rate
    int                    channels;
    int                    encoding;

      
//inisialisasi MPG123
    printf("Komputoo MP3 player\nMemutar %s...\n", MP3_FILE);
    printf("Menyiapkan libmpg123\n");
    mpg123_init();
    handle = mpg123_new(0, 0);
    if(handle == 0)
    {
        printf("Gagal membuat handle baru!\n");
        exit(EXIT_FAILURE);
    }
   
//inisialisasi AO
    printf("Menyiapkan libao\n");
    ao_initialize();
    driver_id = ao_default_driver_id();
    if(driver_id == -1)
    {
        printf("Driver ID tidak diketahui\n");
        exit(EXIT_FAILURE);
    }

//buka mp3
    if(mpg123_open(handle,MP3_FILE) != 0)
    {
        printf("Gagal membuka file!\n");
        exit(EXIT_FAILURE);
    }

//inisialisasi memori buffer untuk menyimpan hasil decode mp3
    ukuran_buffer = mpg123_outblock(handle);
    buffer = (char*) malloc(ukuran_buffer);

//inisialisasi struktur sample_format dari MP3 yang terbuka
    printf("Mengolah informasi MP3...\n");
    mpg123_getformat(handle, &rate, &channels, &encoding);
    sample_format.bits = mpg123_encsize(encoding) * 8;
    sample_format.rate = rate;
    sample_format.channels = channels;
    sample_format.byte_format = AO_FMT_NATIVE;
    sample_format.matrix = 0;
   
//inisialisasi ao sebelum playing
    device = ao_open_live(driver_id, &sample_format, 0);
    if(device == 0)
    {
        printf("ao_open_live error!\n");
        exit(EXIT_FAILURE);
    }

//dapatkan durasi total mp3
    printf("Mencari durasi total...\n");
    off_t    sample_total;
    double    durasi_total;
    sample_total = mpg123_length(handle);
    durasi_total = (double)sample_total / (double)rate;
    printf("- Total sample: %d\n", (int)sample_total);
    printf("- Sample rate: %d\n", (int)rate);
    printf("- Durasi total: %f detik\n", durasi_total);
   
//coba seeking ke 0:20
    //printf("Meloncat ke 0:20\n");
    //off_t frame_baru;
    //frame_baru = mpg123_timeframe(handle, 20);
    //mpg123_seek_frame(handle, frame_baru, SEEK_SET);

//decode dan play mp3
    printf("Memutar MP3...\n");
    off_t posisi_frame;
    double durasi_per_frame;
    double durasi_terlewat;
    double durasi_tersisa;

   
    size_t ukuran_samples;
    while(mpg123_read(handle, buffer, ukuran_buffer, &ukuran_samples) == 0)
    {
        ao_play(device, buffer, ukuran_samples);
        //Cetak time elapsed dan time remaining
        posisi_frame = mpg123_tellframe(handle);
        durasi_per_frame = mpg123_tpf(handle);
        durasi_terlewat = (double)posisi_frame * durasi_per_frame;
        durasi_tersisa = durasi_total - durasi_terlewat;
        printf("Pos. frame: %6d, Dur. lewat: %10f d, Dur. sisa: %10f d\n", (int)posisi_frame, durasi_terlewat, durasi_tersisa);

    }
       
//akhir
    printf("Selesai.\n");
    free(buffer);
    mpg123_close(handle);
    mpg123_delete(handle);
    mpg123_exit();
    ao_close(device);
    ao_shutdown();
   
}



Hasil



Di atas ini merupakan screenshot MP3 yang sedang di play mulai dari Awal. Setelah saya bandingkan dengan durasi yang ditunjukkan oleh MP3 player lain, ternyata durasinya tepat, dan sama persis. Jadi saya yakin, insyaAllah kode ini bebas dari bug :)
Terus ikuti Update blog ini ya, saya akan usahakan tutorial yang terbaik. Heheheh :v
Read more

Mengetahui Durasi MP3 dan Cara Seeking MP3 Player


Sudah paham kan tutorial yang saya jelaskan sebelumnya?
Saya harap begitu, karena pada kesempatan ini, kita akan mencoba bereksperimen menggunakan libmpg123 untuk memutar MP3 secara lebih spesifik lagi. Jika kemarin kita hanya membuat program memutar dari awal sampai akhir, maka hari ini kita akan mencoba memutar MP3 pada titik durasi tertentu, misalnya memutar maju pada durasi 1:10, atau mencoba mundur ke 0:50.

Masih ingin lanjut kan?

Perlu diketahui, meskipun penulisan program dengan C lebih sulit dibanding bahasa pemrograman tingkat tinggi, hasil program yang dihasilkan bersifat portabel, dan juga cepat. Program yang dibuat dengan bahasa tingkat tinggi umumnya dijalankan dengan interpreter. Memang benar perbeaannya tidak nampak. Tapi jika anda menekuni program ini semakin serius dan semakin kompleks, perbedaan yang signifikan itu akan segera muncul. Mungkin ini juga alasan kenapa MP3 player tidak ada yang ditulis dengan bahasa tingkat tinggi.
Bukan bermaksud menyinggung, saya hanya ingin memotivasi Anda. LOL :v

Kode Sepenuhnya
Sebelum mempelajari, saya izinkan agan untuk mencoba dan mengamati source codenya. Berikut ini hanyalah pengembangan kode dari tutorial sebelumnya.  Cara kompilasinya juga tetap sama. Bagian tambahan kode sudah saya tandai agar agan-agan lebih mudah fokus untuk mempelajari API yang baru saja.

#include <mpg123.h>
#include <ao/ao.h>

#define MP3_FILE "/home/irvan/test.mp3"
int main(int argc, char *argv[])
{
    mpg123_handle       *handle;
    ao_sample_format    sample_format;
    ao_device           *device;
    int                    driver_id;
    char               *buffer;
    unsigned int        ukuran_buffer;
    long                rate; //sample rate
    int                    channels;
    int                    encoding;

      
//inisialisasi MPG123
    printf("Komputoo MP3 player\nMemutar %s...\n", MP3_FILE);
    printf("Menyiapkan libmpg123\n");
    mpg123_init();
    handle = mpg123_new(0, 0);
    if(handle == 0)
    {
        printf("Gagal membuat handle baru!\n");
        exit(EXIT_FAILURE);
    }
   
//inisialisasi AO
    printf("Menyiapkan libao\n");
    ao_initialize();
    driver_id = ao_default_driver_id();
    if(driver_id == -1)
    {
        printf("Driver ID tidak diketahui\n");
        exit(EXIT_FAILURE);
    }

//buka mp3
    if(mpg123_open(handle,MP3_FILE) != 0)
    {
        printf("Gagal membuka file!\n");
        exit(EXIT_FAILURE);
    }

//inisialisasi memori buffer untuk menyimpan hasil decode mp3
    ukuran_buffer = mpg123_outblock(handle);
    buffer = (char*) malloc(ukuran_buffer);

//inisialisasi struktur sample_format dari MP3 yang terbuka
    printf("Mengolah informasi MP3...\n");
    mpg123_getformat(handle, &rate, &channels, &encoding);
    sample_format.bits = mpg123_encsize(encoding) * 8;
    sample_format.rate = rate;
    sample_format.channels = channels;
    sample_format.byte_format = AO_FMT_NATIVE;
    sample_format.matrix = 0;
   
//inisialisasi ao sebelum playing
    device = ao_open_live(driver_id, &sample_format, 0);
    if(device == 0)
    {
        printf("ao_open_live error!\n");
        exit(EXIT_FAILURE);
    }

//dapatkan durasi total mp3
    printf("Mencari durasi total...\n");
    off_t    sample_total;
    double    durasi_total;
    sample_total = mpg123_length(handle);
    durasi_total = (double)sample_total / (double)rate;
    printf("- Total sample: %d\n", (int)sample_total);
    printf("- Sample rate: %d\n", (int)rate);
    printf("- Durasi total: %f detik\n", durasi_total);
   
//coba seeking ke 0:20
    printf("Meloncat ke 0:20\n");
    off_t frame_baru;
    frame_baru = mpg123_timeframe(handle, 20);
    mpg123_seek_frame(handle, frame_baru, SEEK_SET);


//decode dan play mp3
    printf("Memutar MP3...\n");
    size_t ukuran_samples;
    while(mpg123_read(handle, buffer, ukuran_buffer, &ukuran_samples) == 0)
    {
        ao_play(device, buffer, ukuran_samples);
    }
       
//akhir
    printf("Selesai.\n");
    free(buffer);
    mpg123_close(handle);
    mpg123_delete(handle);
    mpg123_exit();
    ao_close(device);
    ao_shutdown();
   
}



Mengetahui Durasi MP3
Meskipun fungsi API yang disediakan libmpg123 sudah lengkap, rupanya tidak semua hal bisa dilakukan secara instan. Durasi MP3 tidak dapat diproses secara langsung hanya dengan memanfaatkan sebuah API. Walaupun begitu, libmpg123 mempunyai beberapa fungsi dapat dipadukan untuk memperoleh durasi MP3 yang tepat.

Ada sedikit tambahan info yang belum sempat saya bahas sebelumnya. Awalnya saya mengira proses dekoding MP3 dilakukan secara satu kali saja. Satu file langsung didekode seluruhya dan diletakkan di memori, baru setelah itu diputar dengan libao. Ternyata saya salah. Nyatanya dekoding dilakukan secara looping menghasilkan samples.

Potongan samples yang belum didekode disebut frame. Proses dekode yang kita lihat kemarin sebenarnya adalah proses ekstraksi data dari frame. Setelah saya teliti lagi, satu kali pemanggilan fungsi mpg123_read() akan mendekode data hingga sebanyak 16 frame. Pada dasarnya banyaknya frame yang didekode dalam sekali pemanggilan tergantung ukuran buffer yang disediakan. Seperti yang kita ketahui, ukuran buffer ditentukan dengan fungsi mpg123_outblock(), jadi kesimpulannya, mpg123_outblock() berfungsi memberi rekomendasi ukuran buffer optimum.

Kita sudah melihat pada tutorial sebelumnya, bahwa rate diperoleh dengan fungsi mpg123_getformat(). Pada tutorial ini, rate perlu saya jelaskan lebih detil. Karena berhubungan erat dengan proses pencarian durasi total MP3. Rate umumnya lebih sering disebut sebagai sample rate. Samples rate secara detail merupakan jumlah data samples yang dikirim ke audio device setiap satu detik. Jadi jika kita ingin mengetahui durasi total MP3, caranya adalah dengan mencari jumlah total samples pada mp3, lalu membaginya dengan sample rate, dan dari situ kita mendapatkan durasi total MP3 dalam detik.

Durasi = Total samples / Sample rate

Sample rate sudah kita peroleh dengan mpg123_getformat(). Sekarang bagaimana kita mencari jumlah samples total? Fungsi berikut ini adalah jawabannya...

off_t mpg123_length(mpg123_handle *mh);

Fungsi ini akan mencari dan mengembalikan nilai berupa jumlah sample total dari file MP3. Parameter yang diperlukan adalah sebuah handle dari file yang telah dibuka.

//dapatkan durasi total mp3
    printf("Mencari durasi total...\n");
    off_t    sample_total;
    double    durasi_total;
    sample_total = mpg123_length(handle);
    durasi_total = (double)sample_total / (double)rate;

    printf("- Total sample: %d\n", (int)sample_total);
    printf("- Sample rate: %d\n", (int)rate);
    printf("- Durasi total: %f detik\n", durasi_total);


NB: durasi sebaiknya disimpan dalam variabel pecahan tipe double. Durasi selalu dalam ukuran detik dan berupa bilangan pecahan. Misalnya diketahui durasi 258,12345; maka dapat diartikan 258 detik lebih 12345 milidetik.


Melakukan seeking
Salah satu hal dasar yang perlu diketahui program MP3, adalah mengetahui bagaimana melakukan navigasi ke durasi tertentu. Biasanya sebuah MP3 player dilengkapi dengan tombol navigasi berbentuk seperti progress bar. Kira-kira seperti itulah gambaran umum tentang seeking.

Libmpg123 mendukung beberapa metode seeking. Pertama, mpg123_seek(), untuk melakukan navigasi sample. Kedua, mpg123_feedseek(), khusus untuk navigasi frame pada input streaming dari internet. Kedua fungsi ini akan  kita bahas pada kesempatan lain. Sementara itu, tutorial ini hanya membahas fungsi seeking dengan mpg123_seek_frame(). Fungsi ini digunakan untuk melakukan navigasi ke frame lain. Setelah melakukan navigasi frame, maka ketika mpg123_read() dipanggil, posisi frame baru inilah yang akan didekode selanjutnya. Fungsi API untuk navigasi berdasarkan waktu/durasi tidak disediakan secara langsung, sebagai gantinya libmpg123 menyediakan fungsi mpg123_timeframe(). Fungsi ini berguna untuk mengetahui posisi frame yang berisi samples pada durasi yang diminta.

off_t mpg123_seek_frame(mpg123_handle *mh, off_t frameoff, int whence);

Fungsi diatas bekerja berdasarkan parameter ketiga (parameter whence). Parameter ini dapat diisi SEEK_SET, SEEK_CUR atau SEEK_END. Cara kerjanya sama seperti pengoperasian file pada pemrograman C. Jika whence=SEEK_SET, maka posisi frame baru diambil langsung dari parameter kedua(frameoff). Jika whence=SEEK_CUR, posisi frame baru adalah posisi frame sekarang ditambah parameter frameoff. Jika whence=SEEK_END, posisi frame merupakan frame-terakhir dikurangi parameter frameoff.

off_t mpg123_timeframe(mpg123_handle *mh, double sec);

Seperti biasanya, parameter pertama adalah handle. Sedangkan parameter kedua berupa waktu yang kita inginkan. Hasil fungsi ini meupakan index offset yang kita minta. Untuk menuju ke frame ini, kita menggunakan fungsi mpg123_seek_frame().

//coba seeking ke 0:20
    printf("Meloncat ke 0:20\n");

    off_t frame_baru;
    frame_baru = mpg123_timeframe(handle, 20);
    mpg123_seek_frame(handle, frame_baru, SEEK_SET);


Sekian tutorial kedua tentang MP3 player di Linux. Untuk tutorial selanjutnya kita akan mencoba menghitung time elapsed(durasi terlewat) dan time remaining(durasi tersisa) pada MP3 yang sedang diputar.

Jangan kawatir, setelah Anda tahu tutorial berikutnya, anda sudah bisa mengimplementasikan MP3 player dengan kemampuan Play, Pause dilengkapi kemampuan seeking(navigasi).

Rencananya saya juga menulis tutorial mencari ID3 Tag untuk mengetahui properti MP3, sampai perancangan MP3 player dengan GTK. So, jangan sampai ketinggalan update blog ini ;)

Like Fans Page-nya...
Read more

Jumat, 09 Januari 2015

Membuat MP3 Player untuk Linux dengan C


Setelah cukup lama vakum dari dunia pemrograman, tiba-tiba saja timbul dari dalam diri saya untuk koding. Memang benar pemrograman itu walaupun sulit tetap mengasyikkan. Jadi, masalah mood dan semangat koding itu sama sekali tiak dipengaruhi oleh masalah kesulitan dalam koding, tapi, karena faktor X. Sampai sekarang saya belum tahu persis apa itu faktor X :3 #GJ #curhat

Anda pasti tanya, kenapa harus pakai C, buka C++, pascal, atau bahasa tingkat tinggi lainnya?

Saya tidak bisa menjawabnya. Entah kenapa saya suka yang susah-susah. Ya, walaupun sebagian besar yang saya buat susah itu selalu gagal. Tapi ini semua saya lakukan karena prinsip dan juga mencontoh oleh sosok-sosok yang saya idolakan. Tidak ada yang memuaskan jika kita meraih sesuatu itu dengan kemudahan. Itulah yang saya percaya. Lagipula saya belum pernah menemukan multimedia player populer yang ditulis dengan bahasa tingkat tinggi. Hehehe

Oke, langsung aja dah
Seperti judulnya, postingan ini akan menunjukkan cara membuat MP3 player sederhana dengan C.

libmpg123
Ini adalah library untuk membantu program melakukan decode file MP3. Sebelum program ini, XMMS juga sudah lebih dulu memakai library ini gan.
Alasan memilih libmpg123 adalah karena library ini multiplatform, jadi mudah jika agan berencana membuat port untuk Windows.
Untuk tutorial kali ini, saya pakai ubuntu, jadi jika anda tidak pakai Ubuntu, mohon sesuaikan sendiri ya. Coba mengunduh atau menelusurinya di google.
Tapi, jika ternyata ada yang suka postingan ini, nanti saya coba sertakan file-file yang dibutuhkan dalam postingan ini sekalian.
Untuk mendownload librarynya gunakan perintah :

sudo apt-get install libmpg123-0

Selain butuh librarynya, program ini juga perlu headernya agar bisa menggunakan fungsi yang disediakan library.

sudo apt-get install libmpg123-dev


Pengguna non-ubuntu bisa mengunjungi www.mpg123.de untuk informasi lebih lanjut.

libao
Sama halnya dengan libmpg123, libao ini juga saya pilih karena multiplatform. Fungsi libao beda ya. Libao adalah library yang menyediakan fungsi abstraksi untuk mengeluarkan suara pada berbagai macam audio device. Audio device yang didukung libao sangat lengkap, tapi, tentu saja itu tidak akan saya bahas disini.

Berikutnya, kita juga harus menginstall library dan headernya ya...

sudo apt-get install libao4
sudo apt-get install libao-dev



Proses Memutar MP3

Di atas tadi sudah saya kenalkan 2 library yang diperlukan untuk membuat pemutar MP3. Apa sih, kaitannya program yang kita buat dengan kedua library tadi?

Biarkan saya coba jelaskan. Singkatnya, libmpg123 bertugas membuka MP3 dan mendekodenya menjadi data yang dikenal audio device. Hasil dekode tadi tidak bisa langsung dikirim ke device oleh libmpg123, karena tugasnya memang hanya unduk mendekode. Mengirim kode ke device adalah tugas libao.

Berikut ini adalah mekanisme lengkapnya:

1. Inisialisasi libmpg123
1.1. Memanggil mpg123_init();
1.2. Memperoleh handle baru dengan mpg123_new()

2. Inisialisasi libao
2.1. Panggil ao_initialize()
2.2. Mengetahui ID audio driver default yang terhubung dengan ao_default_driver_id(). Driver ID diperlukan libao untuk menentukan audio device mana yang menjadi output saat memanggil fungsi ao_open_live().

3. Membuka dan menyiapkan MP3
3.1. Buka MP3 dengan mpg123_open()
3.2. Siapkan buffer dengan fungsi malloc. Buffer ini digunakan untuk menyimpan samples. Samples nilai dalam bentuk kode yang dipahami audio device untuk menghasilkan suara. Samples ini didapatkan dari proses decode MP3. Perlu diketahui musik yang terdengar di speaker merupakan deretan samples yang diterjemahkan oleh audio device. Ukuran satu buah samples ditentukan dengan mpg123_outblock();
3.3. Hubungkan libao dengan audio device dengan ao_open_live().

4. Dekode dan mainkan MP3
4.1. Gunakan mpg123_read() untuk mendekode MP3, sehingga dihasilkan samples demi samples.
4.2. Untuk setiap samples yang terbaca, kirimkan ke audio device untuk menghasilkan suara. Dalam hal ini libao menyediakan ao_play() untuk melakukannya.

5. Selesai
5.1. Panggil free() untuk membebaskan buffer
5.2. Tutup dan hapus handle dengan memanggil mpg123_close() disusul dengan memanggil mpg123_delete(). Proses ini pada dasarnya adalah menghapus memori yang dipakai untuk menyimpan informasi berkaitan dengan MP3 yang kita buka.
5.3. Hentikan library mpg123 dengan mpg123_exit(). Fungsi ini pada dasarnya adalah kebalikan dari mpg123_init()
5.4. Sama halnya dengan libmpg123, handle yang dibuat oleh libao juga harus ditutup. Lakukan dengan ao_close().
5.5. Panggil ao_shutdown() untuk menghentikan library.


Kode Pemutar MP3
#include <mpg123.h>
#include <ao/ao.h>

#define MP3_FILE "/home/irvan/test.mp3"
int main()
{
    mpg123_handle    *handle;
    ao_sample_format  sample_format;
    ao_device        *device;
    int               driver_id;
    char             *buffer;
    unsigned int      ukuran_buffer;
    long              rate;
    int               channels;
    int               encoding;
   
      
      
//inisialisasi MPG123
    printf("Komputoo MP3 player\nMembuka %s...\n", MP3_FILE);
    printf("Menyiapkan libmpg123\n");
    mpg123_init();
    handle = mpg123_new(0, 0);
    if(handle == 0)
    {
        printf("Gagal membuat handle baru!\n");
        exit(EXIT_FAILURE);
    }
   
//inisialisasi AO
    printf("Menyiapkan libao\n");
    ao_initialize();
    driver_id = ao_default_driver_id();
    if(driver_id == -1)
    {
        printf("Driver ID tidak diketahui\n");
        exit(EXIT_FAILURE);
    }

//buka mp3
    if(mpg123_open(handle,MP3_FILE) != 0)
    {
        printf("Gagal membuka file!\n");
        exit(EXIT_FAILURE);
    }

//inisialisasi memori buffer untuk menyimpan hasil decode mp3
    ukuran_buffer = mpg123_outblock(handle);
    buffer = (char*) malloc(ukuran_buffer);

//inisialisasi struktur sample_format dari MP3 yang terbuka
    printf("Mengolah informasi MP3...\n");
    mpg123_getformat(handle, &rate, &channels, &encoding);
    sample_format.bits = mpg123_encsize(encoding) * 8;
    sample_format.rate = rate;
    sample_format.channels = channels;
    sample_format.byte_format = AO_FMT_NATIVE;
    sample_format.matrix = 0;
   
//inisialisasi ao sebelum playing
    device = ao_open_live(driver_id, &sample_format, 0);
    if(device == 0)
    {
        printf("ao_open_live error!\n");
        exit(EXIT_FAILURE);
    }

//decode dan play mp3
    printf("Memutar MP3...\n");
    size_t ukuran_samples;
    while(mpg123_read(handle, buffer, ukuran_buffer, &ukuran_samples) == 0)
    {
        ao_play(device, buffer, ukuran_samples);
    }
   
//akhir
    printf("Selesai.\n");
    free(buffer);
    mpg123_close(handle);
    mpg123_delete(handle);
    mpg123_exit();
    ao_close(device);
    ao_shutdown();
   
}



Pembahasan

1. Inisialisasi libmpg123
Inisialisasi dilakukan dengan fungsi mpg123_init(). Pada tahap ini libmpg123 mengalokasikan resource serta mengolah konfigurasi untuk melakukan kerjanya secara garis besar.

int mpg123_init (void);

Fungsi mpg123_init() mengembalikan nilai 0 (MPG123_OK) jika tidak ditemukan kesalahan.

Sebelum membuka MP3, kita harus me-request sebuah handle kepada libmpg123. Handle berupa pointer ke structure

mpg123_handle* mpg123_new (const char * decoder, int *error)

Parameter decoder merupakan pointer ke string yang menunjukkan nama audio decoder yang dipilih. Parameter ini dapat diberi argumen 0, agar libmpg123 menentukan secara otomatis. Namun jika anda mau, anda bisa menggunakan fungsi mpg123_supported_decoders (). Fungsi ini mengembalikan pointer array string yang berisi deretan decoder yang didukung komputer anda.

const char** mpg123_supported_decoders (void)

Parameter kedua dapat dikosongi. Jika mau, anda bisa menyertakan sebuah variabel untuk menampung kode status error apabila terjadi kegagalan saat membuat handle. Kode tersebut dapat dengan mudah dideskripsikan dimana letak spesifik kesalahannya dengan fungsi mpg123_plain_strerror (). Fungsi ini mengembalikan string yang berisi keterangan error secara detail.

const char* mpg123_plain_strerror (int errcode);



2. Inisialisasi libao

Libao yang digunakan harus berjalan berdasarkan konfigurasi default. Proses pembacaan konfigurasi ini dilakukan saat sinyal inisialisasi dipanggil dengan API ao_initialize(). Konfigurasi untuk sekala umum ini disimpan pada “etc/libao.conf” dan ada pula konfigurasi lain untuk pengaturan spesifik setiap user yang disimpan pada folder “~/libao”.

void ao_initialize(void);

Setelah menginisialisasi libao, kita tidak dapat langsung terhubung dengan audio device. Karena beberapa komputer mungkin memiliki lebih dari satu audio device, libao mengharuskan kita untuk menentukannya terlebih dulu. Untuk melakukannya kita bisa menggunakan salah satu dari dua metode, yaitu dengan ao_default_driver_id() atau dengan ao_driver_id(). Fungsi ao_default_driver_id() memilihkan device secara otomatis, biasanya adalah device yang terpilih sebagai default device oleh sistem. Sementara itu fungsi ao_driver_id() mengizinkan kita untuk memilih driver secara spesifik berdasarkan nama uniknya. Info lebih lanjut: https://xiph.org/ao/doc/drivers.html

int ao_default_driver_id(char *short_name);

int ao_driver_id(char *short_name);


Pemanggilan fungsi diatas mengembalingan sebuah ID untuk driver yang terpilih. Nomor unik Driver ID adalah nilai yang diperlukan saat mengakses device, sehingga sebaiknya kita menyimpannya dalam sebuah variabel. Untuk memeriksa kesalahan, pastikan Driver ID yang dikembalikan dari pemanggilan fungsi bukan bernilai -1. Karena nilai ini merupakan indikasi terjadinya error.


3. Membuka dan menyiapkan MP3

Setelah libmpg123 siap dan sebuah handle sudah kita buat, maka libmpg123 sudah siap mendekode file MP3 yang kita inginkan.
Pertama-tama, pilih file MP3 dengan mpg123_open(). Parameter pertama merupakan handle yang telah dibuat dengan mpg123_new() dan parameter keduanya adalah nama file. Jika fungsi error, maka nilai yang dikembalikan merupakan bilangan acak selain 0.

int mpg123_open (mpg123_handle *mh, const char *path)

Ada satu beberapa hal lagi yang harus dipersiapkan sebelum mendekode MP3. Salah satunya yang paling penting adalah menyediakan memori(buffer) untuk menyimpan hasil dekode dalam bentuk samples. Ukuran relatif setiap 1 samples ini ditentukan dengan fungsi mpg123_outblock(). Alokasi memori dapat dilakukan dengan berbagai metode secara bebas, misalnya dengan fungsi malloc() yang disediakan C.

size_t mpg123_outblock(mpg123_handle *mh);

Tahap terakhir sebelum mendekode adalah menghubungkan libao dengan audio device serta mengatur output suara yang dihasilkan berdasakan pengaturan yang disimpan dalam MP3.

Pengaturan output tersebut diambil dari file dengan fungsi berikut:

int mpg123_getformat(mpg123_handle *mh, long *rate, int *channels, int *encoding);

Parameter pertama adalah handle. Parameter selanjutnya secara berurutan adalah pointer ke variabel-variabel untuk menyimpan rate, channel dan encoding. Rate merupakan kualitas suara yang dihasilkan dalam skala bps, artinya setiap detik ukuran data samples hasil dekode yang masuk ke audio device sebesar nilai rate. Channel adalah cara pengeluaran suara pada speaker, dapat berupa mono atau stereo. Terakhir adalah encoding. Encoding merupakan teknik pengorganisasian bilangan biner yang diterapkan pada MP3. Karena yang ini tiak terlalu ada kaitannya dengan tema kita hari ini, maka tidak perlu kita bahas.

Informasi yang telah kita ambil dari file MP3 selanjutnya kita kirimkan pengaturanya pada libao. Fungsi yang digunakan adalah ao_open_live().

ao_device* ao_open_live(int driver_id, ao_sample_format *format, ao_option *option);

Parameter pertama merupakan driver id yang telah kita peroleh pada tahap jauh sebelumnya. Parameter kedua adalah pointer ke structure ao_sample format. Struktur data inilah yang menyimpan informasi output. Fungsi ini mengembalikan handle yang diperlukan dalam pemutaran samples, jadi harus disimpan sampai pemutaran selesai.

typedef struct {
  int  bits; /* bits per sample */
  int  rate; /* samples per second (in a single channel) */
  int  channels; /* number of audio channels */
  int  byte_format; /* Byte ordering in sample */
  char *matrix; /* channel input matrix */
} ao_sample_format;


  • bits diperoleh dengan memanggil fungsi mpg123_encsize() dengan parameter jenis encoding yang telah diperoleh sebelumnya. Lalu kalikan hasilnya dengan 8. Angka 8 itu sendiri adalah jumlah bit dalam 1 byte.
  • rate dan channel ditentukan dari mpg123_getformat yang juga telah dilakukan sebelumnya.
  • byte_format tidak jauh berbeda dengan encoding. Byte_format menentukan teknik pengorganisasian angka digital yang digunakan oleh komputer kita(bukan oleh MP3).
  • matrix lebih baik dikosongi. Kunjungi link ini untuk info lebih lanjut https://xiph.org/ao/doc/ao_sample_format.html.

Dan, parameter terakhir dapat kita isikan secara opsional untuk memberikan instruksi spesial pada audio driver. Nilai ini sebaiknya dikosongi saja. Jika ingin mempelajari lebih jauh, kunjungi link berikut, https://xiph.org/ao/doc/drivers.html.


4. Dekode dan Memutar MP3
Proses dekode secara sederhana dapat dilakukan dengan fungsi mpg123_read(). Hasil pembacaan berupa sebuah samples yang siap dibaca audio device. Setiap pembacan satu samples, kita mengirimkan kode samples tersebut dengan fungsi ao_play(). Berikut ini adalah keterkaitan antara kedua fungsi.

    size_t ukuran_samples;
    while(mpg123_read(handle, buffer, ukuran_buffer, &ukuran_samples) == 0)
    {
        ao_play(device, buffer, ukuran_samples);
    }

Fungsi mpg123_read() menggunakan parameter pertama sebagai handle mp3 yang telah dibuka. Parameter kedua dan ketiga adalah pointer ke buffer serta ukuran buffer. Parameter terakhir merupakan pointer ke sebuah variabel yang menyimpan ukuran samples hasil pembacaan. Fungsi ini akan mengembalikan nilai 0 saat sukses, jika nilai yang dikembalikan adalah MPG123_DONE (-12), artinya samples yang dibaca itu adalah samples terakhir (akhir dari MP3).

Fungsi ao_play() mengirimkan samples ke audio device. Device yang dituju ditentukan oleh parameter pertama. Nilainya sudah kita peroleh dari pemanggilan ao_open_live(), parameter kedua adalah buffer yang menyimpan sebuah samples yang baru saja didekode dan diikuti parameter terakhir yang berupa ukuran samples.




 

5. Selesai
Setelah selesai, program sebaiknya menutup dan menghapus semua handle yang telah dibuat. Karena library sudah tak diperlukan lagi, maka library juga perlu di-uninisialisasi. Berikut adalah fungsi-fungsi yang dimaksud. Untuk penjelasannya baca ulang bagian paling atas.

int mpg123_close(mpg123_handle *mh);

void mpg123_delete(mpg123_handle *mh);

void mpg123_exit(void);

int ao_close(ao_device *device);

void ao_shutdown(void);




Kompilasi
gcc mp3play.c -o mp3play -l ao -l mpg123


Yep, demikian. Saya cape ngetik :p
Untuk tutorial selanjutnya, tentang seeking, pengaturan volume, dan lainnya akan dibahas pada tutorial selanjutnya.
Read more