1 1. PENDAHULUAN Spektogram suara (sonogram) adalah representasi visual dari sinyal akustik. Umumnya disajikan dalam ruang dua dimensi, dengan bagian horizontal mewakili time (waktu) dan bagian vertikal mewakili frekuensi. Intensitas amplitudo pada suatu frekuensi dan pada suatu waktu {time,frekuensi} di dalam spektogram dinyatakan dengan nilai warna tertentu (grayscale atau rgb). Untuk mendapatkan spektogram terlebih dahulu dilakukan proses terhadap sinyal suara dengan terlebih dahulu menentukan ukuran frame data sinyal dalam satu satuan waktu dan menggunakan transformasi Fourier (FFT). Perubahan yang dilakukan terhadap ukuran window FFT akan mempengaruhi ukuran band spektogram. 2. DATA MASUKAN Data masukan adalah berupa pengucapan huruf vokal (a i u e o) sebanyak satu kali pengucapan untuk setiap huruf dan pengisi suara dilakukan oleh penulis sendiri. Untuk perekaman suara digunakan software Audacity dan disimpan dalam format wav dengan frekuensi cuplik diatur sebesar 44100 Hz. 3. ALGORITMA PROGRAM Algoritma program yang dibangun dijabarkan sebagai berikut : 1) Baca dan ekstraksi informasi beserta data dari file wav. 2) Tentukan ukuran frame dalam satuan millisecond (dengan jangkauan yang umum digunakan antara 30ms sd. 100ms) dan lakukan proses framing. 3) Lakukan proses Hamming Window. 4) Lakukan proses FFT. 5) Hitung frekuensi bin. 6) Hitung amplitudo untuk masing-masing frekuensi bin. Untuk setiap nilai power yang didapatkan akan merepresentasikan nilai pada spektogram. 4. EKTRAKSI FILE WAV Informasi dari sebuah file wav didapatkan dengan mengakses 44 byte pertama
2 dari file tersebut. Perhatikan Gambar 1 berikut,
Gambar 1. Informasi sebuah file wav Dari Gambar 1 tersebut didapatkan informasi berupa : sampling rate (SampleRate), number of channels (NumChannels), average byte per second (ByteRate), block align (BlockAlign), bits per sample (BitsPerSample) dan data sizes (Subchunk2Size). Dengan adanya informasi tersebut ekstraksi terhadap data bisa dilakukan dengan sempurna. Berikut adalah penggalan program untuk melakukan aksi tersebut. import struct import os import sys import math import random from Numeric import * fileName = 'aiueo.wav' f = open(fileName,'rb')
3 fileSize = os.path.getsize(fileName) print 'File: %s (%s bytes)' % (fileName, fileSize) # First, find out if we have a valid WAV file. if fileSize<12: print 'Not a WAV file' f.close() exit() # 4s : 4 x char = 4 x 1 byte = 4 byte headerTuple = struct.unpack('4s', f.read(4)) id = headerTuple[0] if (id != 'RIFF'): print 'Not a WAV file' f.close() exit() length, rifftype = struct.unpack('i 4s', f.read(8)) if rifftype!='WAVE': print 'Not a WAV file' f.close() exit() if length!=fileSize-8: print 'Warning: incorrect RIFF header length (%s)' % length header = f.read(32) headerTuple = struct.unpack('4s i h h i i h h 4s i', header) chunk20Length = headerTuple[1] numChannel = headerTuple[3] sampleRate = headerTuple[4] avgBytePerSec = headerTuple[5] blockAlign = headerTuple[6] bitPerSample = headerTuple[7] sampleDataSizes = headerTuple[9] bytesPerSample = float(numChannel)*(math.ceil(bitPerSample/8.0)) numSamples = int(sampleDataSizes/bytesPerSample) loopRange = numSamples numSmplPer_ms = float(sampleRate/bytesPerSample/1000) ms = 0.001 timePerSample = ms/numSmplPer_ms time = (timePerSample*numSamples)/2 wavData = f.read(sampleDataSizes) f.close() if bitPerSample==8: charStruct = 'B' limit = 1 if bitPerSample==16: charStruct = 'h' limit = 2
4 j = 0 toDataFile = [] dataTmp = [] for i in range(loopRange): tmp = [] data = struct.unpack(charStruct, wavData[j:j+limit]) dataTmp.append(data[0]) tmp.append(i+1) tmp.append((i+1)*timePerSample) tmp.append(data[0]) toDataFile.append(tuple(tmp)) j += limit # simpan informasi file wav ke dalam file f = open('1info_' + fileName + '_.txt','w') f.write('File : %s\n' %fileName) f.write('File Size : %s bytes\n' %fileSize) f.write('Number of channel : %s\n' %numChannel) f.write('Block align : %s\n' %blockAlign) f.write('Sampling rate : %s Hz\n' %sampleRate) f.write('Avg bytes per sec : %s byte\n' %avgBytePerSec) f.write('Bit per sample : %s bit\n' %bitPerSample) f.write('Bytes per sample : %s byte\n' %bytesPerSample) f.write('Size of data : %s byte\n' %sampleDataSizes) f.write('Number of sample : %s\n' %numSamples) f.write('Number of sample\n') f.write(' per milli sec : %d\n' %numSmplPer_ms) f.write('Time : %.6f second\n' %time) f.close() # simpan data file wav ke dalam file f = open('2data_' + fileName + '_.txt','w') f.writelines( list( "%d %.6f %s\n" % (number, index, item) for (number, index, item) in toDataFile ) ) f.close()
5. PROSES FRAMING DATA Framing data merupakan proses yang digunakan untuk membagi data menjadi beberapa bagian, untuk mempercepat proses komputasi. Sedangkan hasil dari framing merupakan sinyal terpotong yang discontinue. Di sini data dibagi dalam 45ms tiap frame. Masing-masing sinyal hasil dari framing adalah sinyal terpotong yang discontinue. Sinyal discontinue ini akan dilanjutkan dalam proses Windowing. Sinyal terpotong tersebut akan dilanjutkan dalam proses Windowing. Berikut adalah penggalan program dari fungsi frame. def framing(dataIn, frameSize, fftSize): dataOut = dataIn[0:frameSize]; zeroSize = fftSize - frameSize zeroList = [0]*zeroSize
5 dataOut.extend(zeroList) return dataOut
Berikut adalah penggalan program untuk melakukan aksi tersebut. # 1. framing frameLength = (sampleRate/1000) * 45 # (44100Hz/1000)*45ms = 1980 fftLength = 2048 loopLength = numSamples/frameLength moduloSize = numSamples%frameLength if moduloSize!=0: setZeroSize = frameLength - moduloSize zeroList = [0]*setZeroSize dataTmp.extend(zeroList) loopLength += 1 dataTmp2 = [] for i in range(loopLength): dataTmp2.append( framing( dataTmp[i*frameLength: (i+1)*frameLength ], frameLength, fftLength) )
6. PROSES HAMMING WINDOW Sinyal terpotong discontinue hasil framing kemudian dikalikan dengan fungsi window agar menjadi sinyal yang continue. Fungsi windowing yang digunakan di sini adalah Hamming karena fungsi Hamming dapat membuat data pada awal frame dan akhir frame mendekati nilai 0 dengan baik. Dengan demikian sinyal menjadi continue. Berikut adalah penggalan program dari fungsi window Hamming. def hammingWindow(dataIn, N): dataOut = [] for i in range(N): multiplier=0.5- 0.46*math.cos((2 * pi * dataIn[i])/(N-1)) dataOut.append(multiplier); return dataOut
Berikut adalah penggalan program untuk melakukan aksi tersebut. # 2. windowing dataWindow = [] for i in range(loopLength): dataWindow.append( hammingWindow(dataFrame[i], fftLength) )
6 7. PROSES FFT Proses Fast Fourier Transform (FFT) ini dilakukan setelah didapat sinyal continue. Ukuran FFT yang digunakan adalah 2048. Sedangkan tiap frame didapati data sebanyak : (44100Hz/1000) * 45 = 1984 data (dilakukan pembulatan ke bawah), sehingga data tersebut dilakukan penyamaan data tiap frame dengan menambahkan nilai nol pada akhir nilai (zero padding) untuk setiap frame. Berikut adalah penggalan program dari fungsi FFT. pi=math.pi e=math.e j=complex(0,1) # 0 real, 1 imajiner def fft(f,inv): # f in list n=len(f) # n is length of f if n==1: return f for p in 2,3,5: if n%p==0: break else: raise Exception('%s not factorable ' % n) m = n/p Fout=[] for q in range(p): # 0,1 fp = f[q::p] # every p'th time sample Fp = fft( fp ,inv) Fout.extend( Fp ) for u in range(m): scratch = Fout[u::m] # u to end in strides of m for q1 in range(p): # indices to Fout above that became scratch k = q1*m + u Fout[ k ] = scratch[0] # cuz e**0==1 in loop below for q in range(1,p): if inv: t = e ** ( j*2*pi*k*q/n ) else: t = e ** ( -j*2*pi*k*q/n ) Fout[ k ] += scratch[q] * t return Fout # ================================================================= def real_fft( f ): #just half of N N = len(f) / 2 res = f[::2]
7 ims = f[1::2] fp = [ complex(r,i) for r,i in zip(res,ims) ] #print 'fft input : ', fp Fp = fft( fp ,0 ) # panggil fungsi fft di atas #print 'fft output : ', Fp F = [ complex(0,0) ] * ( N+1 ) F[0] = complex( Fp[0].real + Fp[0].imag , 0 ) for k in range(1,N/2+1): tw = e ** ( -j*pi*(.5+float(k)/N ) ) F1k = Fp[k] + Fp[N-k].conjugate() F2k = Fp[k] - Fp[N-k].conjugate() F2k *= tw F[k] = ( F1k + F2k ) * .5 F[N-k] = ( F1k - F2k ).conjugate() * .5 F[N] = complex( Fp[0].real - Fp[0].imag , 0 ) return F
Berikut adalah penggalan program untuk melakukan aksi tersebut. # 3. use FFT to convert time domain into fequency domain f = open('3FFT_' + fileName + '_.txt','w') fftOut = [] for i in range(loopLength): # data hasil sebanyak (fftLength/2)+1 fftOut.append(real_fft( dataWindow[i] )) f.writelines( "%s\n" % item for item in fftOut[i]) f.close()
8. FREKUENSI BIN Berikut adalah penggalan program untuk melakukan aksi tersebut. # 4. Frekuensi bin (lihat dari keterangan ada di bawah) nyquistFreq = float(sampleRate/2) freqResolution = float(nyquistFreq/fftLength)*2 loopBin = int(nyquistFreq/freqResolution) freqBins = [] for i in range(loopBin): freqBins.append(i*freqResolution)
9. AMPLITUDO Berikut adalah penggalan program untuk melakukan aksi tersebut. # 5. amplitude amplitude = []
8 f = open('4AmplitudeFreq_' + fileName + '_.txt','w') for i in range(loopLength): toDataFile = [] for j in range((fftLength/2)+1): toDataFile.append(math.sqrt(pow(fftOut[i][j].real,2) pow(fftOut[i][j].imag,2))) f.writelines( "%.2f\n" % toDataFile[j]) #f.writelines( "\n" ) #print toDataFile[1] #print toDataFile[fftLength/2] #amplitude.extend(toDataFile[0:(fftLength/2)+1]) amplitude.extend(toDataFile[1:fftLength/2]) f.close()
+
10. SPEKTOGRAM Berikut adalah penggalan program untuk melakukan aksi tersebut. # 6. tabel spectogram maxFFTvalue = max(amplitude) minFFTvalue = min(amplitude) #maxFFTvalue = 10*math.log10(max(amplitude)) #minFFTvalue = 10*math.log10(min(amplitude)) #perGrayColor = int(256/maxFFTvalue) #print maxFFTvalue #print minFFTvalue ampLength = len(amplitude[0]) time = 0 f = open('5Spectogram_' + fileName + '_.txt','w') for i in range(loopLength): time = (i*frameTime)+frameTime if (time/1000==0): sign = "ms" else: time /= 1000.0 sign = "s" for j in range(ampLength): #print freqBins[j], amplitude[i][j] f.writelines( "%s%s %d %d\n" % floor(freqBins[j]), floor(amplitude[i][j]))) f.close()
(time,
sign,
11. HASIL Berikut adalah hasil akhir dari program, dengan memindakan data dari file text ke dalam bentuk tabel. Data yang ditampilkan sebanyak 34 data, dengan masingmasing kolom merepresentasikan hasil pemotongan per ukuran FFT. Setiap kolom memiliki subkolom, yaitu : time (t), frekuensi (f) dan nilai amplitudo (A).
t 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms 45ms
f
A 0 21 43 64 86 107 129 150 172 193 215 236 258 279 301 322 344 366 387 409 430 452 473 495 516 538 559 581 602 624 645 667 689 710
t
45 90ms 4 90ms 48 90ms 29 90ms 30 90ms 56 90ms 25 90ms 28 90ms 29 90ms 13 90ms 26 90ms 35 90ms 30 90ms 34 90ms 30 90ms 10 90ms 14 90ms 3 90ms 10 90ms 27 90ms 21 90ms 23 90ms 23 90ms 15 90ms 13 90ms 7 90ms 10 90ms 17 90ms 11 90ms 14 90ms 25 90ms 4 90ms 25 90ms 26 90ms
f
A 0 21 43 64 86 107 129 150 172 193 215 236 258 279 301 322 344 366 387 409 430 452 473 495 516 538 559 581 602 624 645 667 689 710
t
39 135ms 35 135ms 20 135ms 36 135ms 17 135ms 40 135ms 30 135ms 37 135ms 25 135ms 39 135ms 17 135ms 55 135ms 44 135ms 15 135ms 15 135ms 24 135ms 27 135ms 22 135ms 28 135ms 19 135ms 10 135ms 6 135ms 13 135ms 23 135ms 19 135ms 33 135ms 11 135ms 12 135ms 7 135ms 15 135ms 21 135ms 12 135ms 6 135ms 6 135ms
f
A 0 21 43 64 86 107 129 150 172 193 215 236 258 279 301 322 344 366 387 409 430 452 473 495 516 538 559 581 602 624 645 667 689 710
t
41 180ms 24 180ms 19 180ms 20 180ms 39 180ms 22 180ms 23 180ms 31 180ms 34 180ms 33 180ms 44 180ms 27 180ms 6 180ms 23 180ms 18 180ms 48 180ms 20 180ms 5 180ms 15 180ms 35 180ms 16 180ms 20 180ms 21 180ms 6 180ms 15 180ms 14 180ms 12 180ms 17 180ms 29 180ms 18 180ms 19 180ms 11 180ms 13 180ms 7 180ms
f
A 0 21 43 64 86 107 129 150 172 193 215 236 258 279 301 322 344 366 387 409 430 452 473 495 516 538 559 581 602 624 645 667 689 710
t
25 225ms 22 225ms 37 225ms 43 225ms 27 225ms 22 225ms 39 225ms 70 225ms 23 225ms 4 225ms 25 225ms 16 225ms 24 225ms 35 225ms 26 225ms 13 225ms 21 225ms 23 225ms 48 225ms 19 225ms 8 225ms 14 225ms 26 225ms 8 225ms 37 225ms 34 225ms 6 225ms 21 225ms 5 225ms 16 225ms 24 225ms 11 225ms 18 225ms 20 225ms
f
A 0 21 43 64 86 107 129 150 172 193 215 236 258 279 301 322 344 366 387 409 430 452 473 495 516 538 559 581 602 624 645 667 689 710
t
172 270ms 31 270ms 50 270ms 42 270ms 97 270ms 77 270ms 13 270ms 7 270ms 26 270ms 28 270ms 13 270ms 56 270ms 27 270ms 26 270ms 10 270ms 26 270ms 13 270ms 14 270ms 9 270ms 34 270ms 8 270ms 12 270ms 5 270ms 5 270ms 14 270ms 17 270ms 11 270ms 0 270ms 3 270ms 8 270ms 17 270ms 11 270ms 16 270ms 9 270ms
f
A 0 21 43 64 86 107 129 150 172 193 215 236 258 279 301 322 344 366 387 409 430 452 473 495 516 538 559 581 602 624 645 667 689 710
t
20 315ms 10 315ms 21 315ms 8 315ms 73 315ms 41 315ms 11 315ms 20 315ms 12 315ms 26 315ms 13 315ms 12 315ms 6 315ms 6 315ms 15 315ms 11 315ms 9 315ms 25 315ms 11 315ms 19 315ms 15 315ms 32 315ms 15 315ms 2 315ms 18 315ms 5 315ms 41 315ms 16 315ms 11 315ms 0 315ms 10 315ms 29 315ms 38 315ms 14 315ms
f
A 0 21 43 64 86 107 129 150 172 193 215 236 258 279 301 322 344 366 387 409 430 452 473 495 516 538 559 581 602 624 645 667 689 710
16 12 7 10 56 72 22 17 13 23 61 17 31 13 16 44 8 16 2 10 18 21 6 11 11 19 43 24 1 12 15 22 67 13